diff --git a/.gitignore b/.gitignore index fbd138e9..d6efb9cf 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ .project .settings .metadata + +/build* diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ff66a6d..e78e8929 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,9 @@ cmake_minimum_required(VERSION 3.13) +set(FSFW_VERSION 2) +set(FSFW_SUBVERSION 0) +set(FSFW_REVISION 0) + option(FSFW_GENERATE_SECTIONS "Generate function and data sections. Required to remove unused code" ON ) @@ -7,6 +11,11 @@ if(FSFW_GENERATE_SECTIONS) option(FSFW_REMOVE_UNUSED_CODE "Remove unused code" ON) endif() +option(FSFW_BUILD_UNITTESTS "Build unittest binary in addition to static library" OFF) +if(FSFW_BUILD_UNITTESTS) + option(FSFW_TESTS_GEN_COV "Generate coverage data for unittests" ON) +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_INTERNAL_TESTS "Add internal unit tests" ON) @@ -26,11 +35,64 @@ option(FSFW_ADD_TMSTORAGE "Compile with tm storage components" OFF) option(FSFW_ADD_SGP4_PROPAGATOR "Add SGP4 propagator code" OFF) set(LIB_FSFW_NAME fsfw) +set(FSFW_TEST_TGT fsfw-tests) + add_library(${LIB_FSFW_NAME}) + +if(FSFW_BUILD_UNITTESTS) + message(STATUS "Building the FSFW unittests in addition to the static library") + # Check whether the user has already installed Catch2 first + find_package(Catch2 3) + # Not installed, so use FetchContent to download and provide Catch2 + if(NOT Catch2_FOUND) + include(FetchContent) + + FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v3.0.0-preview3 + ) + + FetchContent_MakeAvailable(Catch2) + endif() + + set(FSFW_CONFIG_PATH tests/src/fsfw_tests/unit/testcfg) + configure_file(tests/src/fsfw_tests/unit/testcfg/FSFWConfig.h.in FSFWConfig.h) + configure_file(tests/src/fsfw_tests/unit/testcfg/TestsConfig.h.in tests/TestsConfig.h) + configure_file(tests/src/fsfw_tests/unit/testcfg/OBSWConfig.h.in OBSWConfig.h) + + project(${FSFW_TEST_TGT} CXX C) + add_executable(${FSFW_TEST_TGT}) + + if(FSFW_TESTS_GEN_COV) + message(STATUS "Generating coverage data for the library") + message(STATUS "Targets linking against ${LIB_FSFW_NAME} " + "will be compiled with coverage data as well" + ) + include(FetchContent) + FetchContent_Declare( + cmake-modules + GIT_REPOSITORY https://github.com/bilke/cmake-modules.git + ) + FetchContent_MakeAvailable(cmake-modules) + set(CMAKE_BUILD_TYPE "Debug") + list(APPEND CMAKE_MODULE_PATH ${cmake-modules_SOURCE_DIR}) + include(CodeCoverage) + endif() +endif() + set(FSFW_CORE_INC_PATH "inc") set_property(CACHE FSFW_OSAL PROPERTY STRINGS host linux rtems freertos) +# Configure Files +target_include_directories(${LIB_FSFW_NAME} PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} +) +target_include_directories(${LIB_FSFW_NAME} INTERFACE + ${CMAKE_CURRENT_BINARY_DIR} +) + if(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True) @@ -63,29 +125,37 @@ endif() set(FSFW_OSAL_DEFINITION FSFW_OSAL_HOST) if(FSFW_OSAL MATCHES host) - set(OS_FSFW_NAME "Host") + set(FSFW_OS_NAME "Host") set(FSFW_OSAL_HOST ON) elseif(FSFW_OSAL MATCHES linux) - set(OS_FSFW_NAME "Linux") + set(FSFW_OS_NAME "Linux") set(FSFW_OSAL_LINUX ON) elseif(FSFW_OSAL MATCHES freertos) - set(OS_FSFW_NAME "FreeRTOS") + set(FSFW_OS_NAME "FreeRTOS") set(FSFW_OSAL_FREERTOS ON) target_link_libraries(${LIB_FSFW_NAME} PRIVATE ${LIB_OS_NAME} ) elseif(FSFW_OSAL STREQUAL rtems) - set(OS_FSFW_NAME "RTEMS") + set(FSFW_OS_NAME "RTEMS") set(FSFW_OSAL_RTEMS ON) else() message(WARNING "Invalid operating system for FSFW specified! Setting to host.." ) - set(OS_FSFW_NAME "Host") + set(FSFW_OS_NAME "Host") set(OS_FSFW "host") endif() -message(STATUS "Compiling FSFW for the ${OS_FSFW_NAME} operating system.") +if(FSFW_BUILD_UNITTESTS) + configure_file(src/fsfw/FSFW.h.in fsfw/FSFW.h) + configure_file(src/fsfw/FSFWVersion.h.in fsfw/FSFWVersion.h) +else() + configure_file(src/fsfw/FSFW.h.in FSFW.h) + configure_file(src/fsfw/FSFWVersion.h.in FSFWVersion.h) +endif() + +message(STATUS "Compiling FSFW for the ${FSFW_OS_NAME} operating system.") add_subdirectory(src) add_subdirectory(tests) @@ -94,12 +164,81 @@ if(FSFW_ADD_HAL) endif() add_subdirectory(contrib) +if(FSFW_BUILD_UNITTESTS) + if(FSFW_TESTS_GEN_COV) + if(CMAKE_COMPILER_IS_GNUCXX) + include(CodeCoverage) + + # Remove quotes. + separate_arguments(COVERAGE_COMPILER_FLAGS + NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}" + ) + + # Add compile options manually, we don't want coverage for Catch2 + target_compile_options(${FSFW_TEST_TGT} PRIVATE + "${COVERAGE_COMPILER_FLAGS}" + ) + target_compile_options(${LIB_FSFW_NAME} PRIVATE + "${COVERAGE_COMPILER_FLAGS}" + ) + + # Exclude directories here + if(WIN32) + set(GCOVR_ADDITIONAL_ARGS + "--exclude-throw-branches" + "--exclude-unreachable-branches" + ) + set(COVERAGE_EXCLUDES + "/c/msys64/mingw64/*" + ) + elseif(UNIX) + set(COVERAGE_EXCLUDES + "/usr/include/*" "/usr/bin/*" "Catch2/*" + "/usr/local/include/*" "*/fsfw_tests/*" + "*/catch2-src/*" + ) + endif() + + target_link_options(${FSFW_TEST_TGT} PRIVATE + -fprofile-arcs + -ftest-coverage + ) + target_link_options(${LIB_FSFW_NAME} PRIVATE + -fprofile-arcs + -ftest-coverage + ) + # Need to specify this as an interface, otherwise there will the compile issues + target_link_options(${LIB_FSFW_NAME} INTERFACE + -fprofile-arcs + -ftest-coverage + ) + + if(WIN32) + setup_target_for_coverage_gcovr_html( + NAME ${FSFW_TEST_TGT}_coverage + EXECUTABLE ${FSFW_TEST_TGT} + DEPENDENCIES ${FSFW_TEST_TGT} + ) + else() + setup_target_for_coverage_lcov( + NAME ${FSFW_TEST_TGT}_coverage + EXECUTABLE ${FSFW_TEST_TGT} + DEPENDENCIES ${FSFW_TEST_TGT} + ) + endif() + endif() + endif() + target_link_libraries(${FSFW_TEST_TGT} PRIVATE Catch2::Catch2 ${LIB_FSFW_NAME}) +endif() + # The project CMakeLists file has to set the FSFW_CONFIG_PATH and add it. # If this is not given, we include the default configuration and emit a warning. if(NOT FSFW_CONFIG_PATH) - message(WARNING "Flight Software Framework configuration path not set!") - message(WARNING "Setting default configuration!") - add_subdirectory(defaultcfg/fsfwconfig) + message(WARNING "Flight Software Framework configuration path not set!") + set(DEF_CONF_PATH misc/defaultcfg/fsfwconfig) + message(WARNING "Setting default configuration from ${DEF_CONF_PATH} ..") + add_subdirectory(${DEF_CONF_PATH}) + set(FSFW_CONFIG_PATH ${DEF_CONF_PATH}) endif() # FSFW might be part of a possibly complicated folder structure, so we @@ -186,4 +325,17 @@ target_compile_options(${LIB_FSFW_NAME} PRIVATE target_link_libraries(${LIB_FSFW_NAME} PRIVATE ${FSFW_ADDITIONAL_LINK_LIBS} -) \ No newline at end of file +) + +string(CONCAT POST_BUILD_COMMENT + "######################################################################\n" + "Built FSFW v${FSFW_VERSION}.${FSFW_SUBVERSION}.${FSFW_REVISION}, " + "Target OSAL: ${FSFW_OS_NAME}\n" + "######################################################################\n" +) + +add_custom_command( + TARGET ${LIB_FSFW_NAME} + POST_BUILD + COMMENT ${POST_BUILD_COMMENT} +) diff --git a/README.md b/README.md index 48d2b8e7..312bc077 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,90 @@ Currently, the FSFW provides the following OSALs: - FreeRTOS - RTEMS -The recommended hardware is a microprocessor with more than 1 MB of RAM and 1 MB of non-volatile Memory. For reference, current applications use a Cobham Gaisler UT699 (LEON3FT), a ISISPACE IOBC or a Zynq-7020 SoC. The `fsfw` was also successfully run on the STM32H743ZI-Nucleo board and on a Raspberry Pi and is currently running on the active satellite mission Flying Laptop. +The recommended hardware is a microprocessor with more than 1 MB of RAM and 1 MB of non-volatile +memory. For reference, current applications use a Cobham Gaisler UT699 (LEON3FT), a +ISISPACE IOBC or a Zynq-7020 SoC. The `fsfw` was also successfully run on the +STM32H743ZI-Nucleo board and on a Raspberry Pi and is currently running on the active +satellite mission Flying Laptop. ## Getting started -The [FSFW example](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_example) provides a good starting point and a demo to see the FSFW capabilities and build it with the Make or the CMake build system. It is recommended to evaluate the FSFW by building and playing around with the demo application. +The [Hosted FSFW example](https://egit.irs.uni-stuttgart.de/fsfw/fsfw-example-hosted) provides a +good starting point and a demo to see the FSFW capabilities. +It is recommended to get started by building and playing around with the demo application. +There are also other examples provided for all OSALs using the popular embedded platforms +Raspberry Pi, Beagle Bone Black and STM32H7. -Generally, the FSFW is included in a project by compiling the FSFW sources and providing -a configuration folder and adding it to the include path. There are some functions like `printChar` which are different depending on the target architecture and need to be implemented by the mission developer. +Generally, the FSFW is included in a project by providing +a configuration folder, building the static library and linking against it. +There are some functions like `printChar` which are different depending on the target architecture +and need to be implemented by the mission developer. A template configuration folder was provided and can be copied into the project root to have -a starting point. The [configuration section](doc/README-config.md#top) provides more specific information about the possible options. +a starting point. The [configuration section](doc/README-config.md#top) provides more specific +information about the possible options. + +## Adding the library + +The following steps show how to add and use FSFW components. It is still recommended to +try out the example mentioned above to get started, but the following steps show how to +add and link against the FSFW library in general. + +1. Add this repository as a submodule + + ```sh + git submodule add https://egit.irs.uni-stuttgart.de/fsfw/fsfw.git fsfw + ``` + +2. Add the following directive inside the uppermost `CMakeLists.txt` file of your project + + ```cmake + add_subdirectory(fsfw) + ``` + +3. Make sure to provide a configuration folder and supply the path to that folder with + the `FSFW_CONFIG_PATH` CMake variable from the uppermost `CMakeLists.txt` file. + It is also necessary to provide the `printChar` function. You can find an example + implementation for a hosted build + [here](https://egit.irs.uni-stuttgart.de/fsfw/fsfw-example-hosted/src/branch/master/bsp_hosted/utility/printChar.c). + +4. Link against the FSFW library + + ```cmake + target_link_libraries( PRIVATE fsfw) + ``` + +5. It should now be possible use the FSFW as a static library from the user code. + +## Building the unittests + +The FSFW also has unittests which use the [Catch2 library](https://github.com/catchorg/Catch2). +These are built by setting the CMake option `FSFW_BUILD_UNITTESTS` to `ON` or `TRUE` +from your project `CMakeLists.txt` file or from the command line. + +The fsfw-tests binary will be built as part of the static library and dropped alongside it. +If the unittests are built, the library and the tests will be built with coverage information by +default. This can be disabled by setting the `FSFW_TESTS_COV_GEN` option to `OFF` or `FALSE`. + +You can use the following commands inside the `fsfw` folder to set up the build system + +```sh +mkdir build-Unittest && cd build-Unittest +cmake -DFSFW_BUILD_UNITTESTS=ON -DFSFW_OSAL=host .. +``` + +You can also use `-DFSFW_OSAL=linux` on Linux systems. + +Coverage data in HTML format can be generated using the `CodeCoverage` +[CMake module](https://github.com/bilke/cmake-modules/tree/master). +To build the unittests, run them and then generare the coverage data in this format, +the following command can be used inside the build directory after the build system was set up + +```sh +cmake --build . -- fsfw-tests_coverage -j +``` + +The `coverage.py` script located in the `script` folder can also be used to do this conveniently. ## Index diff --git a/automation/Dockerfile b/automation/Dockerfile new file mode 100644 index 00000000..93a4fe7d --- /dev/null +++ b/automation/Dockerfile @@ -0,0 +1,8 @@ +FROM ubuntu:focal + +RUN apt-get update +RUN apt-get --yes upgrade + +#tzdata is a dependency, won't install otherwise +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get --yes install gcc g++ cmake make lcov git valgrind nano diff --git a/automation/Jenkinsfile b/automation/Jenkinsfile new file mode 100644 index 00000000..d4a8e2ab --- /dev/null +++ b/automation/Jenkinsfile @@ -0,0 +1,72 @@ +pipeline { + agent any + environment { + BUILDDIR = 'build-unittests' + } + stages { + stage('Create Docker') { + agent { + dockerfile { + dir 'automation' + additionalBuildArgs '--no-cache' + reuseNode true + } + } + steps { + sh 'rm -rf $BUILDDIR' + } + } + stage('Configure') { + agent { + dockerfile { + dir 'automation' + reuseNode true + } + } + steps { + dir(BUILDDIR) { + sh 'cmake -DFSFW_OSAL=host -DFSFW_BUILD_UNITTESTS=ON ..' + } + } + } + stage('Build') { + agent { + dockerfile { + dir 'automation' + reuseNode true + } + } + steps { + dir(BUILDDIR) { + sh 'cmake --build . -j' + } + } + } + stage('Unittests') { + agent { + dockerfile { + dir 'automation' + reuseNode true + } + } + steps { + dir(BUILDDIR) { + sh 'cmake --build . -- fsfw-tests_coverage -j' + } + } + } + stage('Valgrind') { + agent { + dockerfile { + dir 'automation' + reuseNode true + } + } + steps { + dir(BUILDDIR) { + sh 'valgrind --leak-check=full --error-exitcode=1 ./fsfw-tests' + } + } + } + } +} diff --git a/hal/src/fsfw_hal/devicehandlers/MgmRM3100Handler.cpp b/hal/src/fsfw_hal/devicehandlers/MgmRM3100Handler.cpp index 124eebbc..db4ea607 100644 --- a/hal/src/fsfw_hal/devicehandlers/MgmRM3100Handler.cpp +++ b/hal/src/fsfw_hal/devicehandlers/MgmRM3100Handler.cpp @@ -186,7 +186,7 @@ ReturnValue_t MgmRM3100Handler::interpretDeviceReply(DeviceCommandId_t id, const uint8_t cmmValue = packet[1]; // We clear the seventh bit in any case // because this one is zero sometimes for some reason - bitutil::bitClear(&cmmValue, 6); + bitutil::clear(&cmmValue, 6); if(cmmValue == cmmRegValue and internalState == InternalState::READ_CMM) { commandExecuted = true; } diff --git a/hal/src/fsfw_hal/stm32h7/definitions.h b/hal/src/fsfw_hal/stm32h7/definitions.h new file mode 100644 index 00000000..af63a541 --- /dev/null +++ b/hal/src/fsfw_hal/stm32h7/definitions.h @@ -0,0 +1,25 @@ +#ifndef FSFW_HAL_STM32H7_DEFINITIONS_H_ +#define FSFW_HAL_STM32H7_DEFINITIONS_H_ + +#include +#include "stm32h7xx.h" + +namespace stm32h7 { + +/** + * Typedef for STM32 GPIO pair where the first entry is the port used (e.g. GPIOA) + * and the second entry is the pin number + */ +struct GpioCfg { + GpioCfg(): port(nullptr), pin(0), altFnc(0) {}; + + GpioCfg(GPIO_TypeDef* port, uint16_t pin, uint8_t altFnc = 0): + port(port), pin(pin), altFnc(altFnc) {}; + GPIO_TypeDef* port; + uint16_t pin; + uint8_t altFnc; +}; + +} + +#endif /* #ifndef FSFW_HAL_STM32H7_DEFINITIONS_H_ */ diff --git a/hal/src/fsfw_hal/stm32h7/devicetest/GyroL3GD20H.cpp b/hal/src/fsfw_hal/stm32h7/devicetest/GyroL3GD20H.cpp index 051be344..d1fdd1e5 100644 --- a/hal/src/fsfw_hal/stm32h7/devicetest/GyroL3GD20H.cpp +++ b/hal/src/fsfw_hal/stm32h7/devicetest/GyroL3GD20H.cpp @@ -4,7 +4,7 @@ #include "fsfw_hal/stm32h7/spi/spiDefinitions.h" #include "fsfw_hal/stm32h7/spi/spiCore.h" #include "fsfw_hal/stm32h7/spi/spiInterrupts.h" -#include "fsfw_hal/stm32h7/spi/stm32h743ziSpi.h" +#include "fsfw_hal/stm32h7/spi/stm32h743zi.h" #include "fsfw/tasks/TaskFactory.h" #include "fsfw/serviceinterface/ServiceInterface.h" @@ -33,20 +33,20 @@ GyroL3GD20H::GyroL3GD20H(SPI_HandleTypeDef *spiHandle, spi::TransferModes transf mspCfg = new spi::MspDmaConfigStruct(); auto typedCfg = dynamic_cast(mspCfg); spi::setDmaHandles(txDmaHandle, rxDmaHandle); - spi::h743zi::standardDmaCfg(*typedCfg, IrqPriorities::HIGHEST_FREERTOS, + stm32h7::h743zi::standardDmaCfg(*typedCfg, IrqPriorities::HIGHEST_FREERTOS, IrqPriorities::HIGHEST_FREERTOS, IrqPriorities::HIGHEST_FREERTOS); spi::setSpiDmaMspFunctions(typedCfg); } else if(transferMode == spi::TransferModes::INTERRUPT) { mspCfg = new spi::MspIrqConfigStruct(); auto typedCfg = dynamic_cast(mspCfg); - spi::h743zi::standardInterruptCfg(*typedCfg, IrqPriorities::HIGHEST_FREERTOS); + stm32h7::h743zi::standardInterruptCfg(*typedCfg, IrqPriorities::HIGHEST_FREERTOS); spi::setSpiIrqMspFunctions(typedCfg); } else if(transferMode == spi::TransferModes::POLLING) { mspCfg = new spi::MspPollingConfigStruct(); auto typedCfg = dynamic_cast(mspCfg); - spi::h743zi::standardPollingCfg(*typedCfg); + stm32h7::h743zi::standardPollingCfg(*typedCfg); spi::setSpiPollingMspFunctions(typedCfg); } diff --git a/hal/src/fsfw_hal/stm32h7/spi/CMakeLists.txt b/hal/src/fsfw_hal/stm32h7/spi/CMakeLists.txt index e28c35aa..aa5541bc 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/CMakeLists.txt +++ b/hal/src/fsfw_hal/stm32h7/spi/CMakeLists.txt @@ -5,5 +5,5 @@ target_sources(${LIB_FSFW_NAME} PRIVATE mspInit.cpp SpiCookie.cpp SpiComIF.cpp - stm32h743ziSpi.cpp + stm32h743zi.cpp ) diff --git a/hal/src/fsfw_hal/stm32h7/spi/SpiComIF.cpp b/hal/src/fsfw_hal/stm32h7/spi/SpiComIF.cpp index 1813aac0..4c4f7744 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/SpiComIF.cpp +++ b/hal/src/fsfw_hal/stm32h7/spi/SpiComIF.cpp @@ -138,12 +138,14 @@ ReturnValue_t SpiComIF::initializeInterface(CookieIF *cookie) { spi::setSpiDmaMspFunctions(typedCfg); } - gpio::initializeGpioClock(gpioPort); - GPIO_InitTypeDef chipSelect = {}; - chipSelect.Pin = gpioPin; - chipSelect.Mode = GPIO_MODE_OUTPUT_PP; - HAL_GPIO_Init(gpioPort, &chipSelect); - HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_SET); + if(gpioPort != nullptr) { + gpio::initializeGpioClock(gpioPort); + GPIO_InitTypeDef chipSelect = {}; + chipSelect.Pin = gpioPin; + chipSelect.Mode = GPIO_MODE_OUTPUT_PP; + HAL_GPIO_Init(gpioPort, &chipSelect); + HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_SET); + } if(HAL_SPI_Init(&spiHandle) != HAL_OK) { sif::printWarning("SpiComIF::initialize: Error initializing SPI\n"); @@ -259,10 +261,15 @@ ReturnValue_t SpiComIF::handlePollingSendOperation(uint8_t* recvPtr, SPI_HandleT return returnval; } spiCookie.setTransferState(spi::TransferStates::WAIT); - HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_RESET); + if(gpioPort != nullptr) { + HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_RESET); + } + auto result = HAL_SPI_TransmitReceive(&spiHandle, const_cast(sendData), recvPtr, sendLen, defaultPollingTimeout); - HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_SET); + if(gpioPort != nullptr) { + HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_SET); + } spiSemaphore->release(); switch(result) { case(HAL_OK): { @@ -392,8 +399,10 @@ ReturnValue_t SpiComIF::genericIrqSendSetup(uint8_t *recvPtr, SPI_HandleTypeDef& // The SPI handle is passed to the default SPI callback as a void argument. This callback // is different from the user callbacks specified above! spi::assignSpiUserArgs(spiCookie.getSpiIdx(), reinterpret_cast(&spiHandle)); - HAL_GPIO_WritePin(spiCookie.getChipSelectGpioPort(), spiCookie.getChipSelectGpioPin(), - GPIO_PIN_RESET); + if(spiCookie.getChipSelectGpioPort() != nullptr) { + HAL_GPIO_WritePin(spiCookie.getChipSelectGpioPort(), spiCookie.getChipSelectGpioPin(), + GPIO_PIN_RESET); + } return HasReturnvaluesIF::RETURN_OK; } @@ -426,9 +435,12 @@ void SpiComIF::genericIrqHandler(void *irqArgsVoid, spi::TransferStates targetSt spiCookie->setTransferState(targetState); - // Pull CS pin high again - HAL_GPIO_WritePin(spiCookie->getChipSelectGpioPort(), spiCookie->getChipSelectGpioPin(), - GPIO_PIN_SET); + if(spiCookie->getChipSelectGpioPort() != nullptr) { + // Pull CS pin high again + HAL_GPIO_WritePin(spiCookie->getChipSelectGpioPort(), spiCookie->getChipSelectGpioPin(), + GPIO_PIN_SET); + } + #if defined FSFW_OSAL_FREERTOS // Release the task semaphore diff --git a/hal/src/fsfw_hal/stm32h7/spi/SpiComIF.h b/hal/src/fsfw_hal/stm32h7/spi/SpiComIF.h index 9548e102..cb6c4cf8 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/SpiComIF.h +++ b/hal/src/fsfw_hal/stm32h7/spi/SpiComIF.h @@ -60,7 +60,6 @@ public: void addDmaHandles(DMA_HandleTypeDef* txHandle, DMA_HandleTypeDef* rxHandle); ReturnValue_t initialize() override; -protected: // DeviceCommunicationIF overrides virtual ReturnValue_t initializeInterface(CookieIF * cookie) override; @@ -72,7 +71,7 @@ protected: virtual ReturnValue_t readReceivedMessage(CookieIF *cookie, uint8_t **buffer, size_t *size) override; -private: +protected: struct SpiInstance { SpiInstance(size_t maxRecvSize): replyBuffer(std::vector(maxRecvSize)) {} diff --git a/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.cpp b/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.cpp index 88f1e1f1..200d4651 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.cpp +++ b/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.cpp @@ -3,10 +3,10 @@ SpiCookie::SpiCookie(address_t deviceAddress, spi::SpiBus spiIdx, spi::TransferModes transferMode, spi::MspCfgBase* mspCfg, uint32_t spiSpeed, spi::SpiModes spiMode, - uint16_t chipSelectGpioPin, GPIO_TypeDef* chipSelectGpioPort, size_t maxRecvSize): + size_t maxRecvSize, stm32h7::GpioCfg csGpio): deviceAddress(deviceAddress), spiIdx(spiIdx), spiSpeed(spiSpeed), spiMode(spiMode), - transferMode(transferMode), chipSelectGpioPin(chipSelectGpioPin), - chipSelectGpioPort(chipSelectGpioPort), mspCfg(mspCfg), maxRecvSize(maxRecvSize) { + transferMode(transferMode), csGpio(csGpio), + mspCfg(mspCfg), maxRecvSize(maxRecvSize) { spiHandle.Init.DataSize = SPI_DATASIZE_8BIT; spiHandle.Init.FirstBit = SPI_FIRSTBIT_MSB; spiHandle.Init.TIMode = SPI_TIMODE_DISABLE; @@ -24,11 +24,11 @@ SpiCookie::SpiCookie(address_t deviceAddress, spi::SpiBus spiIdx, spi::TransferM } uint16_t SpiCookie::getChipSelectGpioPin() const { - return chipSelectGpioPin; + return csGpio.pin; } GPIO_TypeDef* SpiCookie::getChipSelectGpioPort() { - return chipSelectGpioPort; + return csGpio.port; } address_t SpiCookie::getDeviceAddress() const { diff --git a/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.h b/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.h index 45226b4a..56c6e800 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.h +++ b/hal/src/fsfw_hal/stm32h7/spi/SpiCookie.h @@ -3,11 +3,14 @@ #include "spiDefinitions.h" #include "mspInit.h" +#include "../definitions.h" #include "fsfw/devicehandlers/CookieIF.h" #include "stm32h743xx.h" +#include + /** * @brief SPI cookie implementation for the STM32H7 device family * @details @@ -18,6 +21,7 @@ class SpiCookie: public CookieIF { friend class SpiComIF; public: + /** * Allows construction of a SPI cookie for a connected SPI device * @param deviceAddress @@ -32,10 +36,11 @@ public: * definitions supplied in the MCU header file! (e.g. GPIO_PIN_X) * @param chipSelectGpioPort GPIO port (e.g. GPIOA) * @param maxRecvSize Maximum expected receive size. Chose as small as possible. + * @param csGpio Optional CS GPIO definition. */ SpiCookie(address_t deviceAddress, spi::SpiBus spiIdx, spi::TransferModes transferMode, spi::MspCfgBase* mspCfg, uint32_t spiSpeed, spi::SpiModes spiMode, - uint16_t chipSelectGpioPin, GPIO_TypeDef* chipSelectGpioPort, size_t maxRecvSize); + size_t maxRecvSize, stm32h7::GpioCfg csGpio = stm32h7::GpioCfg(nullptr, 0, 0)); uint16_t getChipSelectGpioPin() const; GPIO_TypeDef* getChipSelectGpioPort(); @@ -55,8 +60,8 @@ private: spi::SpiModes spiMode; spi::TransferModes transferMode; volatile spi::TransferStates transferState = spi::TransferStates::IDLE; - uint16_t chipSelectGpioPin; - GPIO_TypeDef* chipSelectGpioPort; + stm32h7::GpioCfg csGpio; + // The MSP configuration is cached here. Be careful when using this, it is automatically // deleted by the SPI communication interface if it is not required anymore! spi::MspCfgBase* mspCfg = nullptr; diff --git a/hal/src/fsfw_hal/stm32h7/spi/mspInit.cpp b/hal/src/fsfw_hal/stm32h7/spi/mspInit.cpp index 4df61f9b..b7ff2f70 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/mspInit.cpp +++ b/hal/src/fsfw_hal/stm32h7/spi/mspInit.cpp @@ -118,40 +118,40 @@ void spi::halMspInitPolling(SPI_HandleTypeDef* hspi, MspCfgBase* cfgBase) { GPIO_InitTypeDef GPIO_InitStruct = {}; /*##-1- Enable peripherals and GPIO Clocks #################################*/ /* Enable GPIO TX/RX clock */ - cfg->setupMacroWrapper(); + cfg->setupCb(); /*##-2- Configure peripheral GPIO ##########################################*/ /* SPI SCK GPIO pin configuration */ - GPIO_InitStruct.Pin = cfg->sckPin; + GPIO_InitStruct.Pin = cfg->sck.pin; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLDOWN; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - GPIO_InitStruct.Alternate = cfg->sckAlternateFunction; - HAL_GPIO_Init(cfg->sckPort, &GPIO_InitStruct); + GPIO_InitStruct.Alternate = cfg->sck.altFnc; + HAL_GPIO_Init(cfg->sck.port, &GPIO_InitStruct); /* SPI MISO GPIO pin configuration */ - GPIO_InitStruct.Pin = cfg->misoPin; - GPIO_InitStruct.Alternate = cfg->misoAlternateFunction; - HAL_GPIO_Init(cfg->misoPort, &GPIO_InitStruct); + GPIO_InitStruct.Pin = cfg->miso.pin; + GPIO_InitStruct.Alternate = cfg->miso.altFnc; + HAL_GPIO_Init(cfg->miso.port, &GPIO_InitStruct); /* SPI MOSI GPIO pin configuration */ - GPIO_InitStruct.Pin = cfg->mosiPin; - GPIO_InitStruct.Alternate = cfg->mosiAlternateFunction; - HAL_GPIO_Init(cfg->mosiPort, &GPIO_InitStruct); + GPIO_InitStruct.Pin = cfg->mosi.pin; + GPIO_InitStruct.Alternate = cfg->mosi.altFnc; + HAL_GPIO_Init(cfg->mosi.port, &GPIO_InitStruct); } void spi::halMspDeinitPolling(SPI_HandleTypeDef* hspi, MspCfgBase* cfgBase) { auto cfg = reinterpret_cast(cfgBase); // Reset peripherals - cfg->cleanUpMacroWrapper(); + cfg->cleanupCb(); // Disable peripherals and GPIO Clocks /* Configure SPI SCK as alternate function */ - HAL_GPIO_DeInit(cfg->sckPort, cfg->sckPin); + HAL_GPIO_DeInit(cfg->sck.port, cfg->sck.pin); /* Configure SPI MISO as alternate function */ - HAL_GPIO_DeInit(cfg->misoPort, cfg->misoPin); + HAL_GPIO_DeInit(cfg->miso.port, cfg->miso.pin); /* Configure SPI MOSI as alternate function */ - HAL_GPIO_DeInit(cfg->mosiPort, cfg->mosiPin); + HAL_GPIO_DeInit(cfg->mosi.port, cfg->mosi.pin); } void spi::halMspInitInterrupt(SPI_HandleTypeDef* hspi, MspCfgBase* cfgBase) { diff --git a/hal/src/fsfw_hal/stm32h7/spi/mspInit.h b/hal/src/fsfw_hal/stm32h7/spi/mspInit.h index e6de2f8e..0fb553f7 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/mspInit.h +++ b/hal/src/fsfw_hal/stm32h7/spi/mspInit.h @@ -2,6 +2,7 @@ #define FSFW_HAL_STM32H7_SPI_MSPINIT_H_ #include "spiDefinitions.h" +#include "../definitions.h" #include "../dma.h" #include "stm32h7xx_hal_spi.h" @@ -12,6 +13,8 @@ extern "C" { #endif +using mspCb = void (*) (void); + /** * @brief This file provides MSP implementation for DMA, IRQ and Polling mode for the * SPI peripheral. This configuration is required for the SPI communication to work. @@ -19,27 +22,37 @@ extern "C" { namespace spi { struct MspCfgBase { + MspCfgBase(); + MspCfgBase(stm32h7::GpioCfg sck, stm32h7::GpioCfg mosi, stm32h7::GpioCfg miso, + mspCb cleanupCb = nullptr, mspCb setupCb = nullptr): + sck(sck), mosi(mosi), miso(miso), cleanupCb(cleanupCb), + setupCb(setupCb) {} + virtual ~MspCfgBase() = default; - void (* cleanUpMacroWrapper) (void) = nullptr; - void (* setupMacroWrapper) (void) = nullptr; + stm32h7::GpioCfg sck; + stm32h7::GpioCfg mosi; + stm32h7::GpioCfg miso; - GPIO_TypeDef* sckPort = nullptr; - uint32_t sckPin = 0; - uint8_t sckAlternateFunction = 0; - GPIO_TypeDef* mosiPort = nullptr; - uint32_t mosiPin = 0; - uint8_t mosiAlternateFunction = 0; - GPIO_TypeDef* misoPort = nullptr; - uint32_t misoPin = 0; - uint8_t misoAlternateFunction = 0; + mspCb cleanupCb = nullptr; + mspCb setupCb = nullptr; }; -struct MspPollingConfigStruct: public MspCfgBase {}; +struct MspPollingConfigStruct: public MspCfgBase { + MspPollingConfigStruct(): MspCfgBase() {}; + MspPollingConfigStruct(stm32h7::GpioCfg sck, stm32h7::GpioCfg mosi, stm32h7::GpioCfg miso, + mspCb cleanupCb = nullptr, mspCb setupCb = nullptr): + MspCfgBase(sck, mosi, miso, cleanupCb, setupCb) {} +}; /* A valid instance of this struct must be passed to the MSP initialization function as a void* argument */ struct MspIrqConfigStruct: public MspPollingConfigStruct { + MspIrqConfigStruct(): MspPollingConfigStruct() {}; + MspIrqConfigStruct(stm32h7::GpioCfg sck, stm32h7::GpioCfg mosi, stm32h7::GpioCfg miso, + mspCb cleanupCb = nullptr, mspCb setupCb = nullptr): + MspPollingConfigStruct(sck, mosi, miso, cleanupCb, setupCb) {} + SpiBus spiBus = SpiBus::SPI_1; user_handler_t spiIrqHandler = nullptr; user_args_t spiUserArgs = nullptr; @@ -53,11 +66,16 @@ struct MspIrqConfigStruct: public MspPollingConfigStruct { /* A valid instance of this struct must be passed to the MSP initialization function as a void* argument */ struct MspDmaConfigStruct: public MspIrqConfigStruct { + MspDmaConfigStruct(): MspIrqConfigStruct() {}; + MspDmaConfigStruct(stm32h7::GpioCfg sck, stm32h7::GpioCfg mosi, stm32h7::GpioCfg miso, + mspCb cleanupCb = nullptr, mspCb setupCb = nullptr): + MspIrqConfigStruct(sck, mosi, miso, cleanupCb, setupCb) {} void (* dmaClkEnableWrapper) (void) = nullptr; - dma::DMAIndexes txDmaIndex; - dma::DMAIndexes rxDmaIndex; - dma::DMAStreams txDmaStream; - dma::DMAStreams rxDmaStream; + + dma::DMAIndexes txDmaIndex = dma::DMAIndexes::DMA_1; + dma::DMAIndexes rxDmaIndex = dma::DMAIndexes::DMA_1; + dma::DMAStreams txDmaStream = dma::DMAStreams::STREAM_0; + dma::DMAStreams rxDmaStream = dma::DMAStreams::STREAM_0; IRQn_Type txDmaIrqNumber = DMA1_Stream0_IRQn; IRQn_Type rxDmaIrqNumber = DMA1_Stream1_IRQn; // Priorities for NVIC diff --git a/hal/src/fsfw_hal/stm32h7/spi/stm32h743ziSpi.cpp b/hal/src/fsfw_hal/stm32h7/spi/stm32h743zi.cpp similarity index 75% rename from hal/src/fsfw_hal/stm32h7/spi/stm32h743ziSpi.cpp rename to hal/src/fsfw_hal/stm32h7/spi/stm32h743zi.cpp index 43194704..1bafccd5 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/stm32h743ziSpi.cpp +++ b/hal/src/fsfw_hal/stm32h7/spi/stm32h743zi.cpp @@ -1,4 +1,4 @@ -#include "fsfw_hal/stm32h7/spi/stm32h743ziSpi.h" +#include "fsfw_hal/stm32h7/spi/stm32h743zi.h" #include "fsfw_hal/stm32h7/spi/spiCore.h" #include "fsfw_hal/stm32h7/spi/spiInterrupts.h" @@ -22,27 +22,27 @@ void spiDmaClockEnableWrapper() { __HAL_RCC_DMA2_CLK_ENABLE(); } -void spi::h743zi::standardPollingCfg(MspPollingConfigStruct& cfg) { - cfg.setupMacroWrapper = &spiSetupWrapper; - cfg.cleanUpMacroWrapper = &spiCleanUpWrapper; - cfg.sckPort = GPIOA; - cfg.sckPin = GPIO_PIN_5; - cfg.misoPort = GPIOA; - cfg.misoPin = GPIO_PIN_6; - cfg.mosiPort = GPIOA; - cfg.mosiPin = GPIO_PIN_7; - cfg.sckAlternateFunction = GPIO_AF5_SPI1; - cfg.mosiAlternateFunction = GPIO_AF5_SPI1; - cfg.misoAlternateFunction = GPIO_AF5_SPI1; +void stm32h7::h743zi::standardPollingCfg(spi::MspPollingConfigStruct& cfg) { + cfg.setupCb = &spiSetupWrapper; + cfg.cleanupCb = &spiCleanUpWrapper; + cfg.sck.port = GPIOA; + cfg.sck.pin = GPIO_PIN_5; + cfg.miso.port = GPIOA; + cfg.miso.pin = GPIO_PIN_6; + cfg.mosi.port = GPIOA; + cfg.mosi.pin = GPIO_PIN_7; + cfg.sck.altFnc = GPIO_AF5_SPI1; + cfg.mosi.altFnc = GPIO_AF5_SPI1; + cfg.miso.altFnc = GPIO_AF5_SPI1; } -void spi::h743zi::standardInterruptCfg(MspIrqConfigStruct& cfg, IrqPriorities spiIrqPrio, +void stm32h7::h743zi::standardInterruptCfg(spi::MspIrqConfigStruct& cfg, IrqPriorities spiIrqPrio, IrqPriorities spiSubprio) { // High, but works on FreeRTOS as well (priorities range from 0 to 15) cfg.preEmptPriority = spiIrqPrio; cfg.subpriority = spiSubprio; cfg.spiIrqNumber = SPI1_IRQn; - cfg.spiBus = SpiBus::SPI_1; + cfg.spiBus = spi::SpiBus::SPI_1; user_handler_t spiUserHandler = nullptr; user_args_t spiUserArgs = nullptr; getSpiUserHandler(spi::SpiBus::SPI_1, &spiUserHandler, &spiUserArgs); @@ -55,7 +55,7 @@ void spi::h743zi::standardInterruptCfg(MspIrqConfigStruct& cfg, IrqPriorities sp standardPollingCfg(cfg); } -void spi::h743zi::standardDmaCfg(MspDmaConfigStruct& cfg, IrqPriorities spiIrqPrio, +void stm32h7::h743zi::standardDmaCfg(spi::MspDmaConfigStruct& cfg, IrqPriorities spiIrqPrio, IrqPriorities txIrqPrio, IrqPriorities rxIrqPrio, IrqPriorities spiSubprio, IrqPriorities txSubprio, IrqPriorities rxSubprio) { cfg.dmaClkEnableWrapper = &spiDmaClockEnableWrapper; diff --git a/hal/src/fsfw_hal/stm32h7/spi/stm32h743ziSpi.h b/hal/src/fsfw_hal/stm32h7/spi/stm32h743zi.h similarity index 64% rename from hal/src/fsfw_hal/stm32h7/spi/stm32h743ziSpi.h rename to hal/src/fsfw_hal/stm32h7/spi/stm32h743zi.h index 87689add..daa95554 100644 --- a/hal/src/fsfw_hal/stm32h7/spi/stm32h743ziSpi.h +++ b/hal/src/fsfw_hal/stm32h7/spi/stm32h743zi.h @@ -3,21 +3,20 @@ #include "mspInit.h" -namespace spi { +namespace stm32h7 { namespace h743zi { -void standardPollingCfg(MspPollingConfigStruct& cfg); -void standardInterruptCfg(MspIrqConfigStruct& cfg, IrqPriorities spiIrqPrio, +void standardPollingCfg(spi::MspPollingConfigStruct& cfg); +void standardInterruptCfg(spi::MspIrqConfigStruct& cfg, IrqPriorities spiIrqPrio, IrqPriorities spiSubprio = HIGHEST); -void standardDmaCfg(MspDmaConfigStruct& cfg, IrqPriorities spiIrqPrio, +void standardDmaCfg(spi::MspDmaConfigStruct& cfg, IrqPriorities spiIrqPrio, IrqPriorities txIrqPrio, IrqPriorities rxIrqPrio, IrqPriorities spiSubprio = HIGHEST, IrqPriorities txSubPrio = HIGHEST, IrqPriorities rxSubprio = HIGHEST); + } } - - #endif /* FSFW_HAL_STM32H7_SPI_STM32H743ZISPI_H_ */ diff --git a/scripts/coverage.py b/scripts/coverage.py new file mode 100755 index 00000000..71b6fb03 --- /dev/null +++ b/scripts/coverage.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -* +"""Small portable helper script to generate LCOV HTML coverage data""" +import os +import platform +import sys +import time +import argparse +from typing import List + + +"""Copy this helper script into your project folder. It will try to determine a CMake build folder +and then attempt to build your project with coverage information. + +See Unittest documentation at https://egit.irs.uni-stuttgart.de/fsfw/fsfw for more +information how to set up the build folder. +""" +def main(): + + parser = argparse.ArgumentParser(description="Processing arguments for LCOV helper script.") + + build_dir_list = [] + if not os.path.isfile('README.md'): + os.chdir('..') + for directory in os.listdir("."): + if os.path.isdir(directory): + os.chdir(directory) + check_for_cmake_build_dir(build_dir_list) + os.chdir("..") + + if len(build_dir_list) == 0: + print("No valid CMake build directory found. Trying to set up hosted build") + build_directory = 'build-Debug-Host' + os.mkdir(build_directory) + os.chdir(build_directory) + os.system('cmake -DFSFW_OSAL=host -DFSFW_BUILD_UNITTESTS=ON ..') + os.chdir('..') + elif len(build_dir_list) == 1: + build_directory = build_dir_list[0] + else: + print("Multiple build directories found!") + build_directory = determine_build_dir(build_dir_list) + perform_lcov_operation(build_directory) + + +def check_for_cmake_build_dir(build_dir_dict: list): + if os.path.isfile("CMakeCache.txt"): + build_dir_dict.append(os.getcwd()) + + +def perform_lcov_operation(directory): + os.chdir(directory) + os.system("cmake --build . -- fsfw-tests_coverage -j") + + +def determine_build_dir(build_dir_list: List[str]): + build_directory = "" + for idx, directory in enumerate(build_dir_list): + print(f"{idx + 1}: {directory}") + while True: + idx = input("Pick the directory to perform LCOV HTML generation by index: ") + if not idx.isdigit(): + print("Invalid input!") + continue + + idx = int(idx) + if idx > len(build_dir_list) or idx < 1: + print("Invalid input!") + continue + build_directory = build_dir_list[idx - 1] + break + return build_directory + + +if __name__ == "__main__": + main() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5a8f139b..e4670807 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,5 +16,3 @@ target_include_directories(${LIB_FSFW_NAME} PRIVATE target_include_directories(${LIB_FSFW_NAME} INTERFACE ${CMAKE_CURRENT_BINARY_DIR} ) - -configure_file(fsfw/FSFW.h.in fsfw/FSFW.h) diff --git a/src/fsfw/FSFWVersion.h b/src/fsfw/FSFWVersion.h deleted file mode 100644 index c581a85c..00000000 --- a/src/fsfw/FSFWVersion.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef FSFW_VERSION_H_ -#define FSFW_VERSION_H_ - -const char* const FSFW_VERSION_NAME = "ASTP"; - -#define FSFW_VERSION 2 -#define FSFW_SUBVERSION 0 -#define FSFW_REVISION 0 - -#endif /* FSFW_VERSION_H_ */ diff --git a/src/fsfw/FSFWVersion.h.in b/src/fsfw/FSFWVersion.h.in new file mode 100644 index 00000000..7935b2f6 --- /dev/null +++ b/src/fsfw/FSFWVersion.h.in @@ -0,0 +1,9 @@ +#ifndef FSFW_VERSION_H_ +#define FSFW_VERSION_H_ + +// Versioning is kept in project CMakeLists.txt file +#define FSFW_VERSION @FSFW_VERSION@ +#define FSFW_SUBVERSION @FSFW_SUBVERSION@ +#define FSFW_REVISION @FSFW_REVISION@ + +#endif /* FSFW_VERSION_H_ */ diff --git a/src/fsfw/datapoollocal/LocalPoolDataSetBase.cpp b/src/fsfw/datapoollocal/LocalPoolDataSetBase.cpp index 5422c68a..d1ac0c7f 100644 --- a/src/fsfw/datapoollocal/LocalPoolDataSetBase.cpp +++ b/src/fsfw/datapoollocal/LocalPoolDataSetBase.cpp @@ -110,7 +110,7 @@ ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(uint8_t **buffer for (uint16_t count = 0; count < fillCount; count++) { if(registeredVariables[count]->isValid()) { /* Set bit at correct position */ - bitutil::bitSet(validityPtr + validBufferIndex, validBufferIndexBit); + bitutil::set(validityPtr + validBufferIndex, validBufferIndexBit); } if(validBufferIndexBit == 7) { validBufferIndex ++; @@ -156,8 +156,8 @@ ReturnValue_t LocalPoolDataSetBase::deSerializeWithValidityBuffer( uint8_t validBufferIndexBit = 0; for (uint16_t count = 0; count < fillCount; count++) { // set validity buffer here. - bool nextVarValid = bitutil::bitGet(*buffer + - validBufferIndex, validBufferIndexBit); + bool nextVarValid = false; + bitutil::get(*buffer + validBufferIndex, validBufferIndexBit, nextVarValid); registeredVariables[count]->setValid(nextVarValid); if(validBufferIndexBit == 7) { diff --git a/src/fsfw/globalfunctions/PeriodicOperationDivider.cpp b/src/fsfw/globalfunctions/PeriodicOperationDivider.cpp index 98f83d04..ac6a78e4 100644 --- a/src/fsfw/globalfunctions/PeriodicOperationDivider.cpp +++ b/src/fsfw/globalfunctions/PeriodicOperationDivider.cpp @@ -3,16 +3,16 @@ PeriodicOperationDivider::PeriodicOperationDivider(uint32_t divider, bool resetAutomatically): resetAutomatically(resetAutomatically), - counter(divider), divider(divider) { + divider(divider) { } bool PeriodicOperationDivider::checkAndIncrement() { - counter++; bool opNecessary = check(); - if(opNecessary) { - if(resetAutomatically) { - resetCounter(); - } + if(opNecessary and resetAutomatically) { + resetCounter(); + } + else { + counter++; } return opNecessary; } @@ -24,10 +24,8 @@ bool PeriodicOperationDivider::check() { return false; } - - void PeriodicOperationDivider::resetCounter() { - counter = 0; + counter = 1; } void PeriodicOperationDivider::setDivider(uint32_t newDivider) { diff --git a/src/fsfw/globalfunctions/PeriodicOperationDivider.h b/src/fsfw/globalfunctions/PeriodicOperationDivider.h index bfc81eea..636849c0 100644 --- a/src/fsfw/globalfunctions/PeriodicOperationDivider.h +++ b/src/fsfw/globalfunctions/PeriodicOperationDivider.h @@ -16,16 +16,15 @@ public: /** * Initialize with the desired divider and specify whether the internal * counter will be reset automatically. - * @param divider + * @param divider Value of 0 or 1 will cause #check and #checkAndIncrement to always return + * true * @param resetAutomatically */ PeriodicOperationDivider(uint32_t divider, bool resetAutomatically = true); - /** - * Check whether operation is necessary. - * If an operation is necessary and the class has been - * configured to be reset automatically, the counter will be reset. + * Check whether operation is necessary. If an operation is necessary and the class has been + * configured to be reset automatically, the counter will be reset to 1 automatically * * @return * -@c true if the counter is larger or equal to the divider @@ -34,8 +33,7 @@ public: bool checkAndIncrement(); /** - * Checks whether an operation is necessary. - * This function will not increment the counter! + * Checks whether an operation is necessary. This function will not increment the counter. * @return * -@c true if the counter is larger or equal to the divider * -@c false otherwise @@ -43,7 +41,7 @@ public: bool check(); /** - * Can be used to reset the counter to 0 manually. + * Can be used to reset the counter to 1 manually */ void resetCounter(); uint32_t getCounter() const; @@ -54,9 +52,10 @@ public: */ void setDivider(uint32_t newDivider); uint32_t getDivider() const; + private: bool resetAutomatically = true; - uint32_t counter = 0; + uint32_t counter = 1; uint32_t divider = 0; }; diff --git a/src/fsfw/globalfunctions/arrayprinter.cpp b/src/fsfw/globalfunctions/arrayprinter.cpp index 45a1cb38..964b9d04 100644 --- a/src/fsfw/globalfunctions/arrayprinter.cpp +++ b/src/fsfw/globalfunctions/arrayprinter.cpp @@ -45,18 +45,18 @@ void arrayprinter::printHex(const uint8_t *data, size_t size, std::cout << "\r" << std::endl; } - std::cout << "[" << std::hex; + std::cout << "hex [" << std::setfill('0') << std::hex; for(size_t i = 0; i < size; i++) { - std::cout << "0x" << static_cast(data[i]); + std::cout << std::setw(2) << static_cast(data[i]); if(i < size - 1) { - std::cout << " , "; + std::cout << ","; if(i > 0 and (i + 1) % maxCharPerLine == 0) { std::cout << std::endl; } } } - std::cout << std::dec; + std::cout << std::dec << std::setfill(' '); std::cout << "]" << std::endl; #else // General format: 0x01, 0x02, 0x03 so it is number of chars times 6 @@ -69,16 +69,16 @@ void arrayprinter::printHex(const uint8_t *data, size_t size, break; } - currentPos += snprintf(printBuffer + currentPos, 6, "0x%02x", data[i]); + currentPos += snprintf(printBuffer + currentPos, 6, "%02x", data[i]); if(i < size - 1) { - currentPos += sprintf(printBuffer + currentPos, ", "); + currentPos += sprintf(printBuffer + currentPos, ","); if(i > 0 and (i + 1) % maxCharPerLine == 0) { currentPos += sprintf(printBuffer + currentPos, "\n"); } } } #if FSFW_DISABLE_PRINTOUT == 0 - printf("[%s]\n", printBuffer); + printf("hex [%s]\n", printBuffer); #endif /* FSFW_DISABLE_PRINTOUT == 0 */ #endif } @@ -90,11 +90,11 @@ void arrayprinter::printDec(const uint8_t *data, size_t size, std::cout << "\r" << std::endl; } - std::cout << "[" << std::dec; + std::cout << "dec [" << std::dec; for(size_t i = 0; i < size; i++) { std::cout << static_cast(data[i]); if(i < size - 1){ - std::cout << " , "; + std::cout << ","; if(i > 0 and (i + 1) % maxCharPerLine == 0) { std::cout << std::endl; } @@ -114,14 +114,14 @@ void arrayprinter::printDec(const uint8_t *data, size_t size, currentPos += snprintf(printBuffer + currentPos, 3, "%d", data[i]); if(i < size - 1) { - currentPos += sprintf(printBuffer + currentPos, ", "); + currentPos += sprintf(printBuffer + currentPos, ","); if(i > 0 and (i + 1) % maxCharPerLine == 0) { currentPos += sprintf(printBuffer + currentPos, "\n"); } } } #if FSFW_DISABLE_PRINTOUT == 0 - printf("[%s]\n", printBuffer); + printf("dec [%s]\n", printBuffer); #endif /* FSFW_DISABLE_PRINTOUT == 0 */ #endif } diff --git a/src/fsfw/globalfunctions/bitutility.cpp b/src/fsfw/globalfunctions/bitutility.cpp index 628e30c2..54e94a95 100644 --- a/src/fsfw/globalfunctions/bitutility.cpp +++ b/src/fsfw/globalfunctions/bitutility.cpp @@ -1,6 +1,6 @@ #include "fsfw/globalfunctions/bitutility.h" -void bitutil::bitSet(uint8_t *byte, uint8_t position) { +void bitutil::set(uint8_t *byte, uint8_t position) { if(position > 7) { return; } @@ -8,7 +8,7 @@ void bitutil::bitSet(uint8_t *byte, uint8_t position) { *byte |= 1 << shiftNumber; } -void bitutil::bitToggle(uint8_t *byte, uint8_t position) { +void bitutil::toggle(uint8_t *byte, uint8_t position) { if(position > 7) { return; } @@ -16,7 +16,7 @@ void bitutil::bitToggle(uint8_t *byte, uint8_t position) { *byte ^= 1 << shiftNumber; } -void bitutil::bitClear(uint8_t *byte, uint8_t position) { +void bitutil::clear(uint8_t *byte, uint8_t position) { if(position > 7) { return; } @@ -24,10 +24,11 @@ void bitutil::bitClear(uint8_t *byte, uint8_t position) { *byte &= ~(1 << shiftNumber); } -bool bitutil::bitGet(const uint8_t *byte, uint8_t position) { +bool bitutil::get(const uint8_t *byte, uint8_t position, bool& bit) { if(position > 7) { return false; } uint8_t shiftNumber = position + (7 - 2 * position); - return *byte & (1 << shiftNumber); + bit = *byte & (1 << shiftNumber); + return true; } diff --git a/src/fsfw/globalfunctions/bitutility.h b/src/fsfw/globalfunctions/bitutility.h index 1fc1290d..00f19310 100644 --- a/src/fsfw/globalfunctions/bitutility.h +++ b/src/fsfw/globalfunctions/bitutility.h @@ -5,13 +5,36 @@ namespace bitutil { -/* Helper functions for manipulating the individual bits of a byte. -Position refers to n-th bit of a byte, going from 0 (most significant bit) to -7 (least significant bit) */ -void bitSet(uint8_t* byte, uint8_t position); -void bitToggle(uint8_t* byte, uint8_t position); -void bitClear(uint8_t* byte, uint8_t position); -bool bitGet(const uint8_t* byte, uint8_t position); +// Helper functions for manipulating the individual bits of a byte. +// Position refers to n-th bit of a byte, going from 0 (most significant bit) to +// 7 (least significant bit) + +/** + * @brief Set the bit in a given byte + * @param byte + * @param position + */ +void set(uint8_t* byte, uint8_t position); +/** + * @brief Toggle the bit in a given byte + * @param byte + * @param position + */ +void toggle(uint8_t* byte, uint8_t position); +/** + * @brief Clear the bit in a given byte + * @param byte + * @param position + */ +void clear(uint8_t* byte, uint8_t position); +/** + * @brief Get the bit in a given byte + * @param byte + * @param position + * @param If the input is valid, this will be set to true if the bit is set and false otherwise. + * @return False if position is invalid, True otherwise + */ +bool get(const uint8_t* byte, uint8_t position, bool& bit); } diff --git a/src/fsfw/memory/FileSystemArgsIF.h b/src/fsfw/memory/FileSystemArgsIF.h new file mode 100644 index 00000000..67d423ff --- /dev/null +++ b/src/fsfw/memory/FileSystemArgsIF.h @@ -0,0 +1,13 @@ +#ifndef FSFW_SRC_FSFW_MEMORY_FILESYSTEMARGS_H_ +#define FSFW_SRC_FSFW_MEMORY_FILESYSTEMARGS_H_ + +/** + * Empty base interface which can be implemented by to pass arguments via the HasFileSystemIF. + * Users can then dynamic_cast the base pointer to the require child pointer. + */ +class FileSystemArgsIF { +public: + virtual~ FileSystemArgsIF() {}; +}; + +#endif /* FSFW_SRC_FSFW_MEMORY_FILESYSTEMARGS_H_ */ diff --git a/src/fsfw/memory/HasFileSystemIF.h b/src/fsfw/memory/HasFileSystemIF.h index c897f0db..be2f8923 100644 --- a/src/fsfw/memory/HasFileSystemIF.h +++ b/src/fsfw/memory/HasFileSystemIF.h @@ -1,9 +1,10 @@ #ifndef FSFW_MEMORY_HASFILESYSTEMIF_H_ #define FSFW_MEMORY_HASFILESYSTEMIF_H_ -#include "../returnvalues/HasReturnvaluesIF.h" -#include "../returnvalues/FwClassIds.h" -#include "../ipc/messageQueueDefinitions.h" +#include "FileSystemArgsIF.h" +#include "fsfw/returnvalues/HasReturnvaluesIF.h" +#include "fsfw/returnvalues/FwClassIds.h" +#include "fsfw/ipc/messageQueueDefinitions.h" #include @@ -59,7 +60,7 @@ public: */ virtual ReturnValue_t appendToFile(const char* repositoryPath, const char* filename, const uint8_t* data, size_t size, - uint16_t packetNumber, void* args = nullptr) = 0; + uint16_t packetNumber, FileSystemArgsIF* args = nullptr) = 0; /** * @brief Generic function to create a new file. @@ -72,7 +73,7 @@ public: */ virtual ReturnValue_t createFile(const char* repositoryPath, const char* filename, const uint8_t* data = nullptr, - size_t size = 0, void* args = nullptr) = 0; + size_t size = 0, FileSystemArgsIF* args = nullptr) = 0; /** * @brief Generic function to delete a file. @@ -82,23 +83,29 @@ public: * @return */ virtual ReturnValue_t removeFile(const char* repositoryPath, - const char* filename, void* args = nullptr) = 0; + const char* filename, FileSystemArgsIF* args = nullptr) = 0; /** * @brief Generic function to create a directory * @param repositoryPath + * @param Equivalent to the -p flag in Unix systems. If some required parent directories + * do not exist, create them as well * @param args Any other arguments which an implementation might require * @return */ - virtual ReturnValue_t createDirectory(const char* repositoryPath, void* args = nullptr) = 0; + virtual ReturnValue_t createDirectory(const char* repositoryPath, const char* dirname, + bool createParentDirs, FileSystemArgsIF* args = nullptr) = 0; /** * @brief Generic function to remove a directory * @param repositoryPath * @param args Any other arguments which an implementation might require */ - virtual ReturnValue_t removeDirectory(const char* repositoryPath, - bool deleteRecurively = false, void* args = nullptr) = 0; + virtual ReturnValue_t removeDirectory(const char* repositoryPath, const char* dirname, + bool deleteRecurively = false, FileSystemArgsIF* args = nullptr) = 0; + + virtual ReturnValue_t renameFile(const char* repositoryPath, const char* oldFilename, + const char* newFilename, FileSystemArgsIF* args = nullptr) = 0; }; diff --git a/src/fsfw/tcdistribution/PUSDistributor.cpp b/src/fsfw/tcdistribution/PUSDistributor.cpp index 1a5f713d..adab58c8 100644 --- a/src/fsfw/tcdistribution/PUSDistributor.cpp +++ b/src/fsfw/tcdistribution/PUSDistributor.cpp @@ -29,7 +29,7 @@ PUSDistributor::TcMqMapIter PUSDistributor::selectDestination() { tcStatus = checker.checkPacket(currentPacket); if(tcStatus != HasReturnvaluesIF::RETURN_OK) { #if FSFW_VERBOSE_LEVEL >= 1 - std::string keyword; + const char* keyword = "unnamed error"; if(tcStatus == TcPacketCheck::INCORRECT_CHECKSUM) { keyword = "checksum"; } @@ -45,9 +45,6 @@ PUSDistributor::TcMqMapIter PUSDistributor::selectDestination() { else if(tcStatus == TcPacketCheck::INCOMPLETE_PACKET) { keyword = "incomplete packet"; } - else { - keyword = "unnamed error"; - } #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::warning << "PUSDistributor::handlePacket: Packet format invalid, " << keyword << " error" << std::endl; diff --git a/src/fsfw/tmtcpacket/SpacePacket.h b/src/fsfw/tmtcpacket/SpacePacket.h index dad8b95e..16673319 100644 --- a/src/fsfw/tmtcpacket/SpacePacket.h +++ b/src/fsfw/tmtcpacket/SpacePacket.h @@ -71,12 +71,20 @@ protected: namespace spacepacket { -constexpr uint16_t getTcSpacePacketIdFromApid(uint16_t apid) { - return (0x18 << 8) | (((apid >> 8) & 0x07) << 8) | (apid & 0x00ff); +constexpr uint16_t getSpacePacketIdFromApid(bool isTc, uint16_t apid, + bool secondaryHeaderFlag = true) { + return (((isTc << 5) & 0x10) | ((secondaryHeaderFlag << 4) & 0x08) | + ((apid >> 8) & 0x07)) << 8 | (apid & 0x00ff); } -constexpr uint16_t getTmSpacePacketIdFromApid(uint16_t apid) { - return (0x08 << 8) | (((apid >> 8) & 0x07) << 8) | (apid & 0x00ff); +constexpr uint16_t getTcSpacePacketIdFromApid(uint16_t apid, + bool secondaryHeaderFlag = true) { + return getSpacePacketIdFromApid(true, apid, secondaryHeaderFlag); +} + +constexpr uint16_t getTmSpacePacketIdFromApid(uint16_t apid, + bool secondaryHeaderFlag = true) { + return getSpacePacketIdFromApid(false, apid, secondaryHeaderFlag); } } diff --git a/src/fsfw/tmtcpacket/pus/tm/TmPacketStoredBase.cpp b/src/fsfw/tmtcpacket/pus/tm/TmPacketStoredBase.cpp index 20a8ed89..523fb619 100644 --- a/src/fsfw/tmtcpacket/pus/tm/TmPacketStoredBase.cpp +++ b/src/fsfw/tmtcpacket/pus/tm/TmPacketStoredBase.cpp @@ -118,7 +118,7 @@ void TmPacketStoredBase::handleStoreFailure(const char *const packetType, Return "%d too large\n", packetType, sizeToReserve); break; } -#endif #endif } +#endif } diff --git a/tests/src/fsfw_tests/CMakeLists.txt b/tests/src/fsfw_tests/CMakeLists.txt index e4a6be80..f6e1b8ab 100644 --- a/tests/src/fsfw_tests/CMakeLists.txt +++ b/tests/src/fsfw_tests/CMakeLists.txt @@ -1,8 +1,9 @@ - if(FSFW_ADD_INTERNAL_TESTS) add_subdirectory(internal) endif() -if(FSFW_ADD_UNITTESTS) +if(FSFW_BUILD_UNITTESTS) add_subdirectory(unit) +else() + add_subdirectory(integration) endif() diff --git a/tests/src/fsfw_tests/integration/CMakeLists.txt b/tests/src/fsfw_tests/integration/CMakeLists.txt new file mode 100644 index 00000000..e44fbee7 --- /dev/null +++ b/tests/src/fsfw_tests/integration/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(assemblies) +add_subdirectory(controller) +add_subdirectory(devices) +add_subdirectory(task) diff --git a/tests/src/fsfw_tests/integration/assemblies/CMakeLists.txt b/tests/src/fsfw_tests/integration/assemblies/CMakeLists.txt new file mode 100644 index 00000000..22c06600 --- /dev/null +++ b/tests/src/fsfw_tests/integration/assemblies/CMakeLists.txt @@ -0,0 +1,3 @@ +target_sources(${LIB_FSFW_NAME} PRIVATE + TestAssembly.cpp +) \ No newline at end of file diff --git a/tests/src/fsfw_tests/integration/assemblies/TestAssembly.cpp b/tests/src/fsfw_tests/integration/assemblies/TestAssembly.cpp new file mode 100644 index 00000000..0ead4bfd --- /dev/null +++ b/tests/src/fsfw_tests/integration/assemblies/TestAssembly.cpp @@ -0,0 +1,201 @@ +#include "TestAssembly.h" + +#include + + +TestAssembly::TestAssembly(object_id_t objectId, object_id_t parentId, object_id_t testDevice0, + object_id_t testDevice1): + AssemblyBase(objectId, parentId), deviceHandler0Id(testDevice0), + deviceHandler1Id(testDevice1) { + ModeListEntry newModeListEntry; + newModeListEntry.setObject(testDevice0); + newModeListEntry.setMode(MODE_OFF); + newModeListEntry.setSubmode(SUBMODE_NONE); + + commandTable.insert(newModeListEntry); + + newModeListEntry.setObject(testDevice1); + newModeListEntry.setMode(MODE_OFF); + newModeListEntry.setSubmode(SUBMODE_NONE); + + commandTable.insert(newModeListEntry); + +} + +TestAssembly::~TestAssembly() { +} + +ReturnValue_t TestAssembly::commandChildren(Mode_t mode, + Submode_t submode) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestAssembly: Received command to go to mode " << mode << + " submode " << (int) submode << std::endl; +#else + sif::printInfo("TestAssembly: Received command to go to mode %d submode %d\n", mode, submode); +#endif + ReturnValue_t result = RETURN_OK; + if(mode == MODE_OFF){ + commandTable[0].setMode(MODE_OFF); + commandTable[0].setSubmode(SUBMODE_NONE); + commandTable[1].setMode(MODE_OFF); + commandTable[1].setSubmode(SUBMODE_NONE); + } + else if(mode == DeviceHandlerIF::MODE_NORMAL) { + if(submode == submodes::SINGLE){ + commandTable[0].setMode(MODE_OFF); + commandTable[0].setSubmode(SUBMODE_NONE); + commandTable[1].setMode(MODE_OFF); + commandTable[1].setSubmode(SUBMODE_NONE); + // We try to prefer 0 here but we try to switch to 1 even if it might fail + if(isDeviceAvailable(deviceHandler0Id)) { + if (childrenMap[deviceHandler0Id].mode == MODE_ON) { + commandTable[0].setMode(mode); + commandTable[0].setSubmode(SUBMODE_NONE); + } + else { + commandTable[0].setMode(MODE_ON); + commandTable[0].setSubmode(SUBMODE_NONE); + result = NEED_SECOND_STEP; + } + } + else { + if (childrenMap[deviceHandler1Id].mode == MODE_ON) { + commandTable[1].setMode(mode); + commandTable[1].setSubmode(SUBMODE_NONE); + } + else{ + commandTable[1].setMode(MODE_ON); + commandTable[1].setSubmode(SUBMODE_NONE); + result = NEED_SECOND_STEP; + } + } + } + else{ + // Dual Mode Normal + if (childrenMap[deviceHandler0Id].mode == MODE_ON) { + commandTable[0].setMode(mode); + commandTable[0].setSubmode(SUBMODE_NONE); + } + else{ + commandTable[0].setMode(MODE_ON); + commandTable[0].setSubmode(SUBMODE_NONE); + result = NEED_SECOND_STEP; + } + if (childrenMap[deviceHandler1Id].mode == MODE_ON) { + commandTable[1].setMode(mode); + commandTable[1].setSubmode(SUBMODE_NONE); + } + else{ + commandTable[1].setMode(MODE_ON); + commandTable[1].setSubmode(SUBMODE_NONE); + result = NEED_SECOND_STEP; + } + } + } + else{ + //Mode ON + if(submode == submodes::SINGLE){ + commandTable[0].setMode(MODE_OFF); + commandTable[0].setSubmode(SUBMODE_NONE); + commandTable[1].setMode(MODE_OFF); + commandTable[1].setSubmode(SUBMODE_NONE); + // We try to prefer 0 here but we try to switch to 1 even if it might fail + if(isDeviceAvailable(deviceHandler0Id)){ + commandTable[0].setMode(MODE_ON); + commandTable[0].setSubmode(SUBMODE_NONE); + } + else{ + commandTable[1].setMode(MODE_ON); + commandTable[1].setSubmode(SUBMODE_NONE); + } + } + else{ + commandTable[0].setMode(MODE_ON); + commandTable[0].setSubmode(SUBMODE_NONE); + commandTable[1].setMode(MODE_ON); + commandTable[1].setSubmode(SUBMODE_NONE); + } + } + + + + HybridIterator iter(commandTable.begin(), + commandTable.end()); + executeTable(iter); + return result; +} + +ReturnValue_t TestAssembly::isModeCombinationValid(Mode_t mode, + Submode_t submode) { + switch (mode) { + case MODE_OFF: + if (submode == SUBMODE_NONE) { + return RETURN_OK; + } else { + return INVALID_SUBMODE; + } + case DeviceHandlerIF::MODE_NORMAL: + case MODE_ON: + if (submode < 3) { + return RETURN_OK; + } else { + return INVALID_SUBMODE; + } + } + return INVALID_MODE; +} + +ReturnValue_t TestAssembly::initialize() { + ReturnValue_t result = AssemblyBase::initialize(); + if(result != RETURN_OK){ + return result; + } + handler0 = ObjectManager::instance()->get(deviceHandler0Id); + handler1 = ObjectManager::instance()->get(deviceHandler1Id); + if((handler0 == nullptr) or (handler1 == nullptr)){ + return HasReturnvaluesIF::RETURN_FAILED; + } + + handler0->setParentQueue(this->getCommandQueue()); + handler1->setParentQueue(this->getCommandQueue()); + + + result = registerChild(deviceHandler0Id); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + result = registerChild(deviceHandler1Id); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + return result; +} + +ReturnValue_t TestAssembly::checkChildrenStateOn( + Mode_t wantedMode, Submode_t wantedSubmode) { + if(submode == submodes::DUAL){ + for(const auto& info:childrenMap) { + if(info.second.mode != wantedMode or info.second.mode != wantedSubmode){ + return NOT_ENOUGH_CHILDREN_IN_CORRECT_STATE; + } + } + return RETURN_OK; + } + else if(submode == submodes::SINGLE) { + for(const auto& info:childrenMap) { + if(info.second.mode == wantedMode and info.second.mode != wantedSubmode){ + return RETURN_OK; + } + } + } + return INVALID_SUBMODE; +} + +bool TestAssembly::isDeviceAvailable(object_id_t object) { + if(healthHelper.healthTable->getHealth(object) == HasHealthIF::HEALTHY){ + return true; + } + else{ + return false; + } +} diff --git a/tests/src/fsfw_tests/integration/assemblies/TestAssembly.h b/tests/src/fsfw_tests/integration/assemblies/TestAssembly.h new file mode 100644 index 00000000..3cc6f450 --- /dev/null +++ b/tests/src/fsfw_tests/integration/assemblies/TestAssembly.h @@ -0,0 +1,56 @@ +#ifndef MISSION_ASSEMBLIES_TESTASSEMBLY_H_ +#define MISSION_ASSEMBLIES_TESTASSEMBLY_H_ + +#include +#include "../devices/TestDeviceHandler.h" + +class TestAssembly: public AssemblyBase { +public: + TestAssembly(object_id_t objectId, object_id_t parentId, object_id_t testDevice0, + object_id_t testDevice1); + virtual ~TestAssembly(); + ReturnValue_t initialize() override; + + enum submodes: Submode_t{ + SINGLE = 0, + DUAL = 1 + }; + +protected: + /** + * Command children to reach [mode,submode] combination + * Can be done by setting #commandsOutstanding correctly, + * or using executeTable() + * @param mode + * @param submode + * @return + * - @c RETURN_OK if ok + * - @c NEED_SECOND_STEP if children need to be commanded again + */ + ReturnValue_t commandChildren(Mode_t mode, Submode_t submode) override; + /** + * Check whether desired assembly mode was achieved by checking the modes + * or/and health states of child device handlers. + * The assembly template class will also call this function if a health + * or mode change of a child device handler was detected. + * @param wantedMode + * @param wantedSubmode + * @return + */ + ReturnValue_t isModeCombinationValid(Mode_t mode, Submode_t submode) + override; + + ReturnValue_t checkChildrenStateOn(Mode_t wantedMode, + Submode_t wantedSubmode) override; +private: + FixedArrayList commandTable; + object_id_t deviceHandler0Id = 0; + object_id_t deviceHandler1Id = 0; + TestDevice* handler0 = nullptr; + TestDevice* handler1 = nullptr; + + + bool isDeviceAvailable(object_id_t object); +}; + +#endif /* MISSION_ASSEMBLIES_TESTASSEMBLY_H_ */ diff --git a/tests/src/fsfw_tests/integration/controller/CMakeLists.txt b/tests/src/fsfw_tests/integration/controller/CMakeLists.txt new file mode 100644 index 00000000..f5655b71 --- /dev/null +++ b/tests/src/fsfw_tests/integration/controller/CMakeLists.txt @@ -0,0 +1,3 @@ +target_sources(${LIB_FSFW_NAME} PRIVATE + TestController.cpp +) \ No newline at end of file diff --git a/tests/src/fsfw_tests/integration/controller/TestController.cpp b/tests/src/fsfw_tests/integration/controller/TestController.cpp new file mode 100644 index 00000000..96da5fe3 --- /dev/null +++ b/tests/src/fsfw_tests/integration/controller/TestController.cpp @@ -0,0 +1,215 @@ +#include "TestController.h" +#include "OBSWConfig.h" + +#include +#include +#include + +TestController::TestController(object_id_t objectId, object_id_t device0, object_id_t device1, + size_t commandQueueDepth): + ExtendedControllerBase(objectId, objects::NO_OBJECT, commandQueueDepth), + deviceDataset0(device0), + deviceDataset1(device1) { +} + +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( + deviceDataset0.getCreatorObjectId()); + 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( + deviceDataset0.getCreatorObjectId()); + 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..475d8703 --- /dev/null +++ b/tests/src/fsfw_tests/integration/controller/TestController.h @@ -0,0 +1,51 @@ +#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, object_id_t device0, object_id_t device1, + size_t commandQueueDepth = 10); + virtual~ TestController(); +protected: + testdevice::TestDataSet deviceDataset0; + testdevice::TestDataSet deviceDataset1; + + /* Extended Controller Base overrides */ + ReturnValue_t handleCommandMessage(CommandMessage *message) override; + void performControlOperation() override; + + /* HasLocalDatapoolIF callbacks */ + void handleChangedDataset(sid_t sid, store_address_t storeId, bool* clearMessage) override; + void handleChangedPoolVariable(gp_id_t globPoolId, store_address_t storeId, + bool* clearMessage) override; + + LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override; + ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap, + LocalDataPoolManager& poolManager) override; + + ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, + uint32_t *msToReachTheMode) override; + + ReturnValue_t initializeAfterTaskCreation() override; + +private: + + bool traceVariable = false; + uint8_t traceCycles = 5; + uint8_t traceCounter = traceCycles; + + enum TraceTypes { + NONE, + TRACE_DEV_0_UINT8, + TRACE_DEV_0_VECTOR + }; + TraceTypes currentTraceType = TraceTypes::NONE; +}; + + +#endif /* MISSION_CONTROLLER_TESTCONTROLLER_H_ */ diff --git a/tests/src/fsfw_tests/integration/controller/ctrldefinitions/testCtrlDefinitions.h b/tests/src/fsfw_tests/integration/controller/ctrldefinitions/testCtrlDefinitions.h new file mode 100644 index 00000000..7bf045df --- /dev/null +++ b/tests/src/fsfw_tests/integration/controller/ctrldefinitions/testCtrlDefinitions.h @@ -0,0 +1,18 @@ +#ifndef MISSION_CONTROLLER_CTRLDEFINITIONS_TESTCTRLDEFINITIONS_H_ +#define MISSION_CONTROLLER_CTRLDEFINITIONS_TESTCTRLDEFINITIONS_H_ + +#include +#include + +namespace testcontroller { + +enum sourceObjectIds: object_id_t { + DEVICE_0_ID = objects::TEST_DEVICE_HANDLER_0, + DEVICE_1_ID = objects::TEST_DEVICE_HANDLER_1, +}; + +} + + + +#endif /* MISSION_CONTROLLER_CTRLDEFINITIONS_TESTCTRLDEFINITIONS_H_ */ diff --git a/tests/src/fsfw_tests/integration/devices/CMakeLists.txt b/tests/src/fsfw_tests/integration/devices/CMakeLists.txt new file mode 100644 index 00000000..cfd238d2 --- /dev/null +++ b/tests/src/fsfw_tests/integration/devices/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(${LIB_FSFW_NAME} PRIVATE + TestCookie.cpp + TestDeviceHandler.cpp + TestEchoComIF.cpp +) diff --git a/tests/src/fsfw_tests/integration/devices/TestCookie.cpp b/tests/src/fsfw_tests/integration/devices/TestCookie.cpp new file mode 100644 index 00000000..91098f80 --- /dev/null +++ b/tests/src/fsfw_tests/integration/devices/TestCookie.cpp @@ -0,0 +1,14 @@ +#include "TestCookie.h" + +TestCookie::TestCookie(address_t address, size_t replyMaxLen): + address(address), replyMaxLen(replyMaxLen) {} + +TestCookie::~TestCookie() {} + +address_t TestCookie::getAddress() const { + return address; +} + +size_t TestCookie::getReplyMaxLen() const { + return replyMaxLen; +} diff --git a/tests/src/fsfw_tests/integration/devices/TestCookie.h b/tests/src/fsfw_tests/integration/devices/TestCookie.h new file mode 100644 index 00000000..5dac3f25 --- /dev/null +++ b/tests/src/fsfw_tests/integration/devices/TestCookie.h @@ -0,0 +1,22 @@ +#ifndef MISSION_DEVICES_TESTCOOKIE_H_ +#define MISSION_DEVICES_TESTCOOKIE_H_ + +#include +#include + +/** + * @brief Really simple cookie which does not do a lot. + */ +class TestCookie: public CookieIF { +public: + TestCookie(address_t address, size_t maxReplyLen); + virtual ~TestCookie(); + + address_t getAddress() const; + size_t getReplyMaxLen() const; +private: + address_t address = 0; + size_t replyMaxLen = 0; +}; + +#endif /* MISSION_DEVICES_TESTCOOKIE_H_ */ diff --git a/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.cpp b/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.cpp new file mode 100644 index 00000000..46138c56 --- /dev/null +++ b/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.cpp @@ -0,0 +1,798 @@ +#include "TestDeviceHandler.h" +#include "FSFWConfig.h" + +#include "fsfw/datapool/PoolReadGuard.h" + +#include + +TestDevice::TestDevice(object_id_t objectId, object_id_t comIF, + CookieIF * cookie, testdevice::DeviceIndex deviceIdx, bool fullInfoPrintout, + bool changingDataset): + DeviceHandlerBase(objectId, comIF, cookie), deviceIdx(deviceIdx), + dataset(this), fullInfoPrintout(fullInfoPrintout) { +} + +TestDevice::~TestDevice() {} + +void TestDevice::performOperationHook() { + if(periodicPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::performOperationHook: Alive!" << std::endl; +#else + sif::printInfo("TestDevice%d::performOperationHook: Alive!", deviceIdx); +#endif + } + + if(oneShot) { + oneShot = false; + } +} + + +void TestDevice::doStartUp() { + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::doStartUp: Switching On" << std::endl; +#else + sif::printInfo("TestDevice%d::doStartUp: Switching On\n", static_cast(deviceIdx)); +#endif + } + + setMode(_MODE_TO_ON); + return; +} + + +void TestDevice::doShutDown() { + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::doShutDown: Switching Off" << std::endl; +#else + sif::printInfo("TestDevice%d::doShutDown: Switching Off\n", static_cast(deviceIdx)); +#endif + } + + setMode(_MODE_SHUT_DOWN); + return; +} + + +ReturnValue_t TestDevice::buildNormalDeviceCommand(DeviceCommandId_t* id) { + using namespace testdevice; + *id = TEST_NORMAL_MODE_CMD; + if(DeviceHandlerBase::isAwaitingReply()) { + return NOTHING_TO_SEND; + } + return buildCommandFromCommand(*id, nullptr, 0); +} + +ReturnValue_t TestDevice::buildTransitionDeviceCommand(DeviceCommandId_t* id) { + if(mode == _MODE_TO_ON) { + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::buildTransitionDeviceCommand: Was called" + " from _MODE_TO_ON mode" << std::endl; +#else + sif::printInfo("TestDevice%d::buildTransitionDeviceCommand: " + "Was called from _MODE_TO_ON mode\n", deviceIdx); +#endif + } + + } + if(mode == _MODE_TO_NORMAL) { + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::buildTransitionDeviceCommand: Was called " + "from _MODE_TO_NORMAL mode" << std::endl; +#else + sif::printInfo("TestDevice%d::buildTransitionDeviceCommand: Was called from " + " _MODE_TO_NORMAL mode\n", deviceIdx); +#endif + } + + setMode(MODE_NORMAL); + } + if(mode == _MODE_SHUT_DOWN) { + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::buildTransitionDeviceCommand: Was called " + "from _MODE_SHUT_DOWN mode" << std::endl; +#else + sif::printInfo("TestDevice%d::buildTransitionDeviceCommand: Was called from " + "_MODE_SHUT_DOWN mode\n", deviceIdx); +#endif + } + + setMode(MODE_OFF); + } + return NOTHING_TO_SEND; +} + +void TestDevice::doTransition(Mode_t modeFrom, Submode_t submodeFrom) { + if(mode == _MODE_TO_NORMAL) { + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::doTransition: Custom transition to " + "normal mode" << std::endl; +#else + sif::printInfo("TestDevice%d::doTransition: Custom transition to normal mode\n", + deviceIdx); +#endif + } + + } + else { + DeviceHandlerBase::doTransition(modeFrom, submodeFrom); + } +} + +ReturnValue_t TestDevice::buildCommandFromCommand( + DeviceCommandId_t deviceCommand, const uint8_t* commandData, + size_t commandDataLen) { + using namespace testdevice; + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + switch(deviceCommand) { + case(TEST_NORMAL_MODE_CMD): { + commandSent = true; + result = buildNormalModeCommand(deviceCommand, commandData, commandDataLen); + break; + } + + case(TEST_COMMAND_0): { + commandSent = true; + result = buildTestCommand0(deviceCommand, commandData, commandDataLen); + break; + } + + case(TEST_COMMAND_1): { + commandSent = true; + result = buildTestCommand1(deviceCommand, commandData, commandDataLen); + break; + } + case(TEST_NOTIF_SNAPSHOT_VAR): { + if(changingDatasets) { + changingDatasets = false; + } + + PoolReadGuard readHelper(&dataset.testUint8Var); + if(deviceIdx == testdevice::DeviceIndex::DEVICE_0) { + /* This will trigger a variable notification to the demo controller */ + dataset.testUint8Var = 220; + dataset.testUint8Var.setValid(true); + } + else if(deviceIdx == testdevice::DeviceIndex::DEVICE_1) { + /* This will trigger a variable snapshot to the demo controller */ + dataset.testUint8Var = 30; + dataset.testUint8Var.setValid(true); + } + + break; + } + case(TEST_NOTIF_SNAPSHOT_SET): { + if(changingDatasets) { + changingDatasets = false; + } + + PoolReadGuard readHelper(&dataset.testFloat3Vec); + + if(deviceIdx == testdevice::DeviceIndex::DEVICE_0) { + /* This will trigger a variable notification to the demo controller */ + dataset.testFloat3Vec.value[0] = 60; + dataset.testFloat3Vec.value[1] = 70; + dataset.testFloat3Vec.value[2] = 55; + dataset.testFloat3Vec.setValid(true); + } + else if(deviceIdx == testdevice::DeviceIndex::DEVICE_1) { + /* This will trigger a variable notification to the demo controller */ + dataset.testFloat3Vec.value[0] = -60; + dataset.testFloat3Vec.value[1] = -70; + dataset.testFloat3Vec.value[2] = -55; + dataset.testFloat3Vec.setValid(true); + } + break; + } + default: + result = DeviceHandlerIF::COMMAND_NOT_SUPPORTED; + } + return result; +} + + +ReturnValue_t TestDevice::buildNormalModeCommand(DeviceCommandId_t deviceCommand, + const uint8_t* commandData, size_t commandDataLen) { + if(fullInfoPrintout) { +#if OBSW_VERBOSE_LEVEL >= 3 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice::buildTestCommand1: Building normal command" << std::endl; +#else + sif::printInfo("TestDevice::buildTestCommand1: Building command from TEST_COMMAND_1\n"); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#endif /* OBSW_VERBOSE_LEVEL >= 3 */ + } + + if(commandDataLen > MAX_BUFFER_SIZE - sizeof(DeviceCommandId_t)) { + return DeviceHandlerIF::INVALID_NUMBER_OR_LENGTH_OF_PARAMETERS; + } + /* The command is passed on in the command buffer as it is */ + passOnCommand(deviceCommand, commandData, commandDataLen); + return RETURN_OK; +} + +ReturnValue_t TestDevice::buildTestCommand0(DeviceCommandId_t deviceCommand, + const uint8_t* commandData, size_t commandDataLen) { + using namespace testdevice; + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::buildTestCommand0: Executing simple command " + " with completion reply" << std::endl; +#else + sif::printInfo("TestDevice%d::buildTestCommand0: Executing simple command with " + "completion reply\n", deviceIdx); +#endif + } + + if(commandDataLen > MAX_BUFFER_SIZE - sizeof(DeviceCommandId_t)) { + return DeviceHandlerIF::INVALID_NUMBER_OR_LENGTH_OF_PARAMETERS; + } + + /* The command is passed on in the command buffer as it is */ + passOnCommand(deviceCommand, commandData, commandDataLen); + return RETURN_OK; +} + +ReturnValue_t TestDevice::buildTestCommand1(DeviceCommandId_t deviceCommand, + const uint8_t* commandData, + size_t commandDataLen) { + using namespace testdevice; + if(commandDataLen < 7) { + return DeviceHandlerIF::INVALID_NUMBER_OR_LENGTH_OF_PARAMETERS; + } + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::buildTestCommand1: Executing command with " + "data reply" << std::endl; +#else + sif::printInfo("TestDevice%d:buildTestCommand1: Executing command with data reply\n", + deviceIdx); +#endif + } + + deviceCommand = EndianConverter::convertBigEndian(deviceCommand); + memcpy(commandBuffer, &deviceCommand, sizeof(deviceCommand)); + + /* Assign and check parameters */ + uint16_t parameter1 = 0; + size_t size = commandDataLen; + ReturnValue_t result = SerializeAdapter::deSerialize(¶meter1, + &commandData, &size, SerializeIF::Endianness::BIG); + if(result == HasReturnvaluesIF::RETURN_FAILED) { + return result; + } + + /* Parameter 1 needs to be correct */ + if(parameter1 != testdevice::COMMAND_1_PARAM1) { + return DeviceHandlerIF::INVALID_COMMAND_PARAMETER; + } + uint64_t parameter2 = 0; + result = SerializeAdapter::deSerialize(¶meter2, + &commandData, &size, SerializeIF::Endianness::BIG); + if(parameter2!= testdevice::COMMAND_1_PARAM2){ + return DeviceHandlerIF::INVALID_COMMAND_PARAMETER; + } + + /* Pass on the parameters to the Echo IF */ + commandBuffer[4] = (parameter1 & 0xFF00) >> 8; + commandBuffer[5] = (parameter1 & 0xFF); + parameter2 = EndianConverter::convertBigEndian(parameter2); + memcpy(commandBuffer + 6, ¶meter2, sizeof(parameter2)); + rawPacket = commandBuffer; + rawPacketLen = sizeof(deviceCommand) + sizeof(parameter1) + + sizeof(parameter2); + return RETURN_OK; +} + +void TestDevice::passOnCommand(DeviceCommandId_t command, const uint8_t *commandData, + size_t commandDataLen) { + DeviceCommandId_t deviceCommandBe = EndianConverter::convertBigEndian(command); + memcpy(commandBuffer, &deviceCommandBe, sizeof(deviceCommandBe)); + memcpy(commandBuffer + 4, commandData, commandDataLen); + rawPacket = commandBuffer; + rawPacketLen = sizeof(deviceCommandBe) + commandDataLen; +} + +void TestDevice::fillCommandAndReplyMap() { + namespace td = testdevice; + insertInCommandAndReplyMap(testdevice::TEST_NORMAL_MODE_CMD, 5, &dataset); + insertInCommandAndReplyMap(testdevice::TEST_COMMAND_0, 5); + insertInCommandAndReplyMap(testdevice::TEST_COMMAND_1, 5); + + /* No reply expected for these commands */ + insertInCommandMap(td::TEST_NOTIF_SNAPSHOT_SET); + insertInCommandMap(td::TEST_NOTIF_SNAPSHOT_VAR); +} + + +ReturnValue_t TestDevice::scanForReply(const uint8_t *start, size_t len, + DeviceCommandId_t *foundId, size_t *foundLen) { + using namespace testdevice; + + /* Unless a command was sent explicitely, we don't expect any replies and ignore this + the packet. On a real device, there might be replies which are sent without a previous + command. */ + if(not commandSent) { + return DeviceHandlerBase::IGNORE_FULL_PACKET; + } + else { + commandSent = false; + } + + if(len < sizeof(object_id_t)) { + return DeviceHandlerIF::LENGTH_MISSMATCH; + } + + size_t size = len; + ReturnValue_t result = SerializeAdapter::deSerialize(foundId, &start, &size, + SerializeIF::Endianness::BIG); + if (result != RETURN_OK) { + return result; + } + + DeviceCommandId_t pendingCmd = this->getPendingCommand(); + + switch(pendingCmd) { + + case(TEST_NORMAL_MODE_CMD): { + if(fullInfoPrintout) { +#if OBSW_VERBOSE_LEVEL >= 3 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice::scanForReply: Reply for normal commnand (ID " << + TEST_NORMAL_MODE_CMD << ") received!" << std::endl; +#else + sif::printInfo("TestDevice%d::scanForReply: Reply for normal command (ID %d) " + "received!\n", deviceIdx, TEST_NORMAL_MODE_CMD); +#endif +#endif + } + + *foundLen = len; + *foundId = pendingCmd; + return RETURN_OK; + } + + case(TEST_COMMAND_0): { + if(len < TEST_COMMAND_0_SIZE) { + return DeviceHandlerIF::LENGTH_MISSMATCH; + } + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::scanForReply: Reply for simple command " + "(ID " << TEST_COMMAND_0 << ") received!" << std::endl; +#else + sif::printInfo("TestDevice%d::scanForReply: Reply for simple command (ID %d) " + "received!\n", deviceIdx, TEST_COMMAND_0); +#endif + } + + *foundLen = TEST_COMMAND_0_SIZE; + *foundId = pendingCmd; + return RETURN_OK; + } + + case(TEST_COMMAND_1): { + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::scanForReply: Reply for data command " + "(ID " << TEST_COMMAND_1 << ") received!" << std::endl; +#else + sif::printInfo("TestDevice%d::scanForReply: Reply for data command (ID %d) " + "received\n", deviceIdx, TEST_COMMAND_1); +#endif + } + + *foundLen = len; + *foundId = pendingCmd; + return RETURN_OK; + } + + default: + return DeviceHandlerIF::DEVICE_REPLY_INVALID; + } +} + + +ReturnValue_t TestDevice::interpretDeviceReply(DeviceCommandId_t id, + const uint8_t* packet) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + switch(id) { + /* Periodic replies */ + case testdevice::TEST_NORMAL_MODE_CMD: { + result = interpretingNormalModeReply(); + break; + } + /* Simple reply */ + case testdevice::TEST_COMMAND_0: { + result = interpretingTestReply0(id, packet); + break; + } + /* Data reply */ + case testdevice::TEST_COMMAND_1: { + result = interpretingTestReply1(id, packet); + break; + } + default: + return DeviceHandlerIF::DEVICE_REPLY_INVALID; + } + return result; +} + +ReturnValue_t TestDevice::interpretingNormalModeReply() { + CommandMessage directReplyMessage; + if(changingDatasets) { + PoolReadGuard readHelper(&dataset); + if(dataset.testUint8Var.value == 0) { + dataset.testUint8Var.value = 10; + dataset.testUint32Var.value = 777; + dataset.testFloat3Vec.value[0] = 2.5; + dataset.testFloat3Vec.value[1] = -2.5; + dataset.testFloat3Vec.value[2] = 2.5; + dataset.setValidity(true, true); + } + else { + dataset.testUint8Var.value = 0; + dataset.testUint32Var.value = 0; + dataset.testFloat3Vec.value[0] = 0.0; + dataset.testFloat3Vec.value[1] = 0.0; + dataset.testFloat3Vec.value[2] = 0.0; + dataset.setValidity(false, true); + } + return RETURN_OK; + } + + PoolReadGuard readHelper(&dataset); + if(dataset.testUint8Var.value == 0) { + /* Reset state */ + dataset.testUint8Var.value = 128; + } + else if(dataset.testUint8Var.value > 200) { + if(not resetAfterChange) { + /* This will trigger an update notification to the controller */ + dataset.testUint8Var.setChanged(true); + resetAfterChange = true; + /* Decrement by 30 automatically. This will prevent any additional notifications. */ + dataset.testUint8Var.value -= 30; + } + } + /* If the value is greater than 0, it will be decremented in a linear way */ + else if(dataset.testUint8Var.value > 128) { + size_t sizeToDecrement = 0; + if(dataset.testUint8Var.value > 128 + 30) { + sizeToDecrement = 30; + } + else { + sizeToDecrement = dataset.testUint8Var.value - 128; + resetAfterChange = false; + } + dataset.testUint8Var.value -= sizeToDecrement; + } + else if(dataset.testUint8Var.value < 50) { + if(not resetAfterChange) { + /* This will trigger an update snapshot to the controller */ + dataset.testUint8Var.setChanged(true); + resetAfterChange = true; + } + else { + /* Increment by 30 automatically. */ + dataset.testUint8Var.value += 30; + } + } + /* Increment in linear way */ + else if(dataset.testUint8Var.value < 128) { + size_t sizeToIncrement = 0; + if(dataset.testUint8Var.value < 128 - 20) { + sizeToIncrement = 20; + } + else { + sizeToIncrement = 128 - dataset.testUint8Var.value; + resetAfterChange = false; + } + dataset.testUint8Var.value += sizeToIncrement; + } + + /* TODO: Same for vector */ + float vectorMean = (dataset.testFloat3Vec.value[0] + dataset.testFloat3Vec.value[1] + + dataset.testFloat3Vec.value[2]) / 3.0; + + /* Lambda (private local function) */ + auto sizeToAdd = [](bool tooHigh, float currentVal) { + if(tooHigh) { + if(currentVal - 20.0 > 10.0) { + return -10.0; + } + else { + return 20.0 - currentVal; + } + } + else { + if(std::abs(currentVal + 20.0) > 10.0) { + return 10.0; + } + else { + return -20.0 - currentVal; + } + } + }; + + if(vectorMean > 20.0 and std::abs(vectorMean - 20.0) > 1.0) { + if(not resetAfterChange) { + dataset.testFloat3Vec.setChanged(true); + resetAfterChange = true; + } + else { + float sizeToDecrementVal0 = 0; + float sizeToDecrementVal1 = 0; + float sizeToDecrementVal2 = 0; + + sizeToDecrementVal0 = sizeToAdd(true, dataset.testFloat3Vec.value[0]); + sizeToDecrementVal1 = sizeToAdd(true, dataset.testFloat3Vec.value[1]); + sizeToDecrementVal2 = sizeToAdd(true, dataset.testFloat3Vec.value[2]); + + dataset.testFloat3Vec.value[0] += sizeToDecrementVal0; + dataset.testFloat3Vec.value[1] += sizeToDecrementVal1; + dataset.testFloat3Vec.value[2] += sizeToDecrementVal2; + } + } + else if (vectorMean < -20.0 and std::abs(vectorMean + 20.0) < 1.0) { + if(not resetAfterChange) { + dataset.testFloat3Vec.setChanged(true); + resetAfterChange = true; + } + else { + float sizeToDecrementVal0 = 0; + float sizeToDecrementVal1 = 0; + float sizeToDecrementVal2 = 0; + + sizeToDecrementVal0 = sizeToAdd(false, dataset.testFloat3Vec.value[0]); + sizeToDecrementVal1 = sizeToAdd(false, dataset.testFloat3Vec.value[1]); + sizeToDecrementVal2 = sizeToAdd(false, dataset.testFloat3Vec.value[2]); + + dataset.testFloat3Vec.value[0] += sizeToDecrementVal0; + dataset.testFloat3Vec.value[1] += sizeToDecrementVal1; + dataset.testFloat3Vec.value[2] += sizeToDecrementVal2; + } + } + else { + if(resetAfterChange) { + resetAfterChange = false; + } + } + + return RETURN_OK; +} + +ReturnValue_t TestDevice::interpretingTestReply0(DeviceCommandId_t id, const uint8_t* packet) { + CommandMessage commandMessage; + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice::interpretingTestReply0: Generating step and finish reply" << + std::endl; +#else + sif::printInfo("TestDevice::interpretingTestReply0: Generating step and finish reply\n"); +#endif + } + + MessageQueueId_t commander = getCommanderQueueId(id); + /* Generate one step reply and the finish reply */ + actionHelper.step(1, commander, id); + actionHelper.finish(true, commander, id); + + return RETURN_OK; +} + +ReturnValue_t TestDevice::interpretingTestReply1(DeviceCommandId_t id, + const uint8_t* packet) { + CommandMessage directReplyMessage; + if(fullInfoPrintout) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::interpretingReply1: Setting data reply" << + std::endl; +#else + sif::printInfo("TestDevice%d::interpretingReply1: Setting data reply\n", deviceIdx); +#endif + } + + MessageQueueId_t commander = getCommanderQueueId(id); + /* Send reply with data */ + ReturnValue_t result = actionHelper.reportData(commander, id, packet, + testdevice::TEST_COMMAND_1_SIZE, false); + + if (result != RETURN_OK) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "TestDevice" << deviceIdx << "::interpretingReply1: Sending data " + "reply failed!" << std::endl; +#else + sif::printError("TestDevice%d::interpretingReply1: Sending data reply failed!\n", + deviceIdx); +#endif + return result; + } + + if(result == HasReturnvaluesIF::RETURN_OK) { + /* Finish reply */ + actionHelper.finish(true, commander, id); + } + else { + /* Finish reply */ + actionHelper.finish(false, commander, id, result); + } + + return RETURN_OK; +} + +uint32_t TestDevice::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) { + return 5000; +} + +void TestDevice::enableFullDebugOutput(bool enable) { + this->fullInfoPrintout = enable; +} + +ReturnValue_t TestDevice::initializeLocalDataPool(localpool::DataPool &localDataPoolMap, + LocalDataPoolManager &poolManager) { + namespace td = testdevice; + localDataPoolMap.emplace(td::PoolIds::TEST_UINT8_ID, new PoolEntry({0})); + localDataPoolMap.emplace(td::PoolIds::TEST_UINT32_ID, new PoolEntry({0})); + localDataPoolMap.emplace(td::PoolIds::TEST_FLOAT_VEC_3_ID, + new PoolEntry({0.0, 0.0, 0.0})); + + sid_t sid(this->getObjectId(), td::TEST_SET_ID); + /* Subscribe for periodic HK packets but do not enable reporting for now. + Non-diangostic with a period of one second */ + poolManager.subscribeForPeriodicPacket(sid, false, 1.0, false); + return HasReturnvaluesIF::RETURN_OK; +} + + +ReturnValue_t TestDevice::getParameter(uint8_t domainId, uint8_t uniqueId, + ParameterWrapper* parameterWrapper, const ParameterWrapper* newValues, + uint16_t startAtIndex) { + using namespace testdevice; + switch (uniqueId) { + case ParameterUniqueIds::TEST_UINT32_0: { + if(fullInfoPrintout) { + uint32_t newValue = 0; + ReturnValue_t result = newValues->getElement(&newValue, 0, 0); + if(result == HasReturnvaluesIF::RETURN_OK) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::getParameter: Setting parameter 0 to " + "new value " << newValue << std::endl; +#else + sif::printInfo("TestDevice%d::getParameter: Setting parameter 0 to new value %lu\n", + deviceIdx, static_cast(newValue)); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ + } + } + parameterWrapper->set(testParameter0); + break; + } + case ParameterUniqueIds::TEST_INT32_1: { + if(fullInfoPrintout) { + int32_t newValue = 0; + ReturnValue_t result = newValues->getElement(&newValue, 0, 0); + if(result == HasReturnvaluesIF::RETURN_OK) { +#if OBSW_DEVICE_HANDLER_PRINTOUT == 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::getParameter: Setting parameter 1 to " + "new value " << newValue << std::endl; +#else + sif::printInfo("TestDevice%d::getParameter: Setting parameter 1 to new value %lu\n", + deviceIdx, static_cast(newValue)); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#endif /* OBSW_DEVICE_HANDLER_PRINTOUT == 1 */ + } + } + parameterWrapper->set(testParameter1); + break; + } + case ParameterUniqueIds::TEST_FLOAT_VEC3_2: { + if(fullInfoPrintout) { + float newVector[3]; + if(newValues->getElement(newVector, 0, 0) != RETURN_OK or + newValues->getElement(newVector + 1, 0, 1) != RETURN_OK or + newValues->getElement(newVector + 2, 0, 2) != RETURN_OK) { + return HasReturnvaluesIF::RETURN_FAILED; + } +#if OBSW_DEVICE_HANDLER_PRINTOUT == 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::getParameter: Setting parameter 3 to " + "(float vector with 3 entries) to new values [" << newVector[0] << ", " << + newVector[1] << ", " << newVector[2] << "]" << std::endl; +#else + sif::printInfo("TestDevice%d::getParameter: Setting parameter 3 to new values " + "[%f, %f, %f]\n", deviceIdx, newVector[0], newVector[1], newVector[2]); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#endif /* OBSW_DEVICE_HANDLER_PRINTOUT == 1 */ + } + parameterWrapper->setVector(vectorFloatParams2); + break; + } + case(ParameterUniqueIds::PERIODIC_PRINT_ENABLED): { + if(fullInfoPrintout) { + uint8_t enabled = 0; + ReturnValue_t result = newValues->getElement(&enabled, 0, 0); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + char const* printout = nullptr; + if (enabled) { + printout = "enabled"; + } + else { + printout = "disabled"; + } +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::getParameter: Periodic printout " << + printout << std::endl; +#else + sif::printInfo("TestDevice%d::getParameter: Periodic printout %s", printout); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ + } + + parameterWrapper->set(periodicPrintout); + break; + } + case(ParameterUniqueIds::CHANGING_DATASETS): { + + uint8_t enabled = 0; + ReturnValue_t result = newValues->getElement(&enabled, 0, 0); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + if(not enabled) { + PoolReadGuard readHelper(&dataset); + dataset.testUint8Var.value = 0; + dataset.testUint32Var.value = 0; + dataset.testFloat3Vec.value[0] = 0.0; + dataset.testFloat3Vec.value[0] = 0.0; + dataset.testFloat3Vec.value[1] = 0.0; + } + + if(fullInfoPrintout) { + char const* printout = nullptr; + if (enabled) { + printout = "enabled"; + } + else { + printout = "disabled"; + } +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "TestDevice" << deviceIdx << "::getParameter: Changing datasets " << + printout << std::endl; +#else + sif::printInfo("TestDevice%d::getParameter: Changing datasets %s", printout); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ + } + + parameterWrapper->set(changingDatasets); + break; + } + default: + return INVALID_IDENTIFIER_ID; + } + return HasReturnvaluesIF::RETURN_OK; +} + +LocalPoolObjectBase* TestDevice::getPoolObjectHandle(lp_id_t localPoolId) { + namespace td = testdevice; + if (localPoolId == td::PoolIds::TEST_UINT8_ID) { + return &dataset.testUint8Var; + } + else if (localPoolId == td::PoolIds::TEST_UINT32_ID) { + return &dataset.testUint32Var; + } + else if(localPoolId == td::PoolIds::TEST_FLOAT_VEC_3_ID) { + return &dataset.testFloat3Vec; + } + else { + return nullptr; + } +} diff --git a/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.h b/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.h new file mode 100644 index 00000000..0eb47731 --- /dev/null +++ b/tests/src/fsfw_tests/integration/devices/TestDeviceHandler.h @@ -0,0 +1,142 @@ +#ifndef TEST_TESTDEVICES_TESTDEVICEHANDLER_H_ +#define TEST_TESTDEVICES_TESTDEVICEHANDLER_H_ + +#include "devicedefinitions/testDeviceDefinitions.h" + +#include "fsfw/devicehandlers/DeviceHandlerBase.h" +#include "fsfw/globalfunctions/PeriodicOperationDivider.h" +#include "fsfw/timemanager/Countdown.h" + +/** + * @brief Basic dummy device handler to test device commanding without a physical device. + * @details + * This test device handler provided a basic demo for the device handler object. + * It can also be commanded with the following PUS services, using + * the specified object ID of the test device handler. + * + * 1. PUS Service 8 - Functional commanding + * 2. PUS Service 2 - Device access, raw commanding + * 3. PUS Service 20 - Parameter Management + * 4. PUS Service 3 - Housekeeping + + * @author R. Mueller + * @ingroup devices + */ +class TestDevice: public DeviceHandlerBase { +public: + /** + * Build the test device in the factory. + * @param objectId This ID will be assigned to the test device handler. + * @param comIF The ID of the Communication IF used by test device handler. + * @param cookie Cookie object used by the test device handler. This is + * also used and passed to the comIF object. + * @param onImmediately This will start a transition to MODE_ON immediately + * so the device handler jumps into #doStartUp. Should only be used + * in development to reduce need of commanding while debugging. + * @param changingDataset + * Will be used later to change the local datasets containeds in the device. + */ + TestDevice(object_id_t objectId, object_id_t comIF, CookieIF * cookie, + testdevice::DeviceIndex deviceIdx = testdevice::DeviceIndex::DEVICE_0, + bool fullInfoPrintout = false, bool changingDataset = true); + + /** + * This can be used to enable and disable a lot of demo print output. + * @param enable + */ + void enableFullDebugOutput(bool enable); + + virtual ~ TestDevice(); + + //! Size of internal buffer used for communication. + static constexpr uint8_t MAX_BUFFER_SIZE = 255; + + //! Unique index if the device handler is created multiple times. + testdevice::DeviceIndex deviceIdx = testdevice::DeviceIndex::DEVICE_0; + +protected: + testdevice::TestDataSet dataset; + //! This is used to reset the dataset after a commanded change has been made. + bool resetAfterChange = false; + bool commandSent = false; + + /** DeviceHandlerBase overrides (see DHB documentation) */ + + /** + * Hook into the DHB #performOperation call which is executed + * periodically. + */ + void performOperationHook() override; + + virtual void doStartUp() override; + virtual void doShutDown() override; + + virtual ReturnValue_t buildNormalDeviceCommand( + DeviceCommandId_t * id) override; + virtual ReturnValue_t buildTransitionDeviceCommand( + DeviceCommandId_t * id) override; + virtual ReturnValue_t buildCommandFromCommand(DeviceCommandId_t + deviceCommand, const uint8_t * commandData, + size_t commandDataLen) override; + + virtual void fillCommandAndReplyMap() override; + + virtual ReturnValue_t scanForReply(const uint8_t *start, size_t len, + DeviceCommandId_t *foundId, size_t *foundLen) override; + virtual ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, + const uint8_t *packet) override; + virtual uint32_t getTransitionDelayMs(Mode_t modeFrom, + Mode_t modeTo) override; + + virtual void doTransition(Mode_t modeFrom, Submode_t subModeFrom) override; + + virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap, + LocalDataPoolManager& poolManager) override; + virtual LocalPoolObjectBase* getPoolObjectHandle(lp_id_t localPoolId) override; + + /* HasParametersIF overrides */ + virtual ReturnValue_t getParameter(uint8_t domainId, uint8_t uniqueId, + ParameterWrapper *parameterWrapper, + const ParameterWrapper *newValues, uint16_t startAtIndex) override; + + uint8_t commandBuffer[MAX_BUFFER_SIZE]; + + bool fullInfoPrintout = false; + bool oneShot = true; + + /* Variables for parameter service */ + uint32_t testParameter0 = 0; + int32_t testParameter1 = -2; + float vectorFloatParams2[3] = {}; + + /* Change device handler functionality, changeable via parameter service */ + uint8_t periodicPrintout = false; + uint8_t changingDatasets = false; + + ReturnValue_t buildNormalModeCommand(DeviceCommandId_t deviceCommand, + const uint8_t* commandData, size_t commandDataLen); + ReturnValue_t buildTestCommand0(DeviceCommandId_t deviceCommand, const uint8_t* commandData, + size_t commandDataLen); + ReturnValue_t buildTestCommand1(DeviceCommandId_t deviceCommand, const uint8_t* commandData, + size_t commandDataLen); + void passOnCommand(DeviceCommandId_t command, const uint8_t* commandData, + size_t commandDataLen); + + ReturnValue_t interpretingNormalModeReply(); + ReturnValue_t interpretingTestReply0(DeviceCommandId_t id, + const uint8_t* packet); + ReturnValue_t interpretingTestReply1(DeviceCommandId_t id, + const uint8_t* packet); + ReturnValue_t interpretingTestReply2(DeviceCommandId_t id, const uint8_t* packet); + + /* Some timer utilities */ + uint8_t divider1 = 2; + PeriodicOperationDivider opDivider1 = PeriodicOperationDivider(divider1); + uint8_t divider2 = 10; + PeriodicOperationDivider opDivider2 = PeriodicOperationDivider(divider2); + static constexpr uint32_t initTimeout = 2000; + Countdown countdown1 = Countdown(initTimeout); +}; + + +#endif /* TEST_TESTDEVICES_TESTDEVICEHANDLER_H_ */ diff --git a/tests/src/fsfw_tests/integration/devices/TestEchoComIF.cpp b/tests/src/fsfw_tests/integration/devices/TestEchoComIF.cpp new file mode 100644 index 00000000..afb23a06 --- /dev/null +++ b/tests/src/fsfw_tests/integration/devices/TestEchoComIF.cpp @@ -0,0 +1,86 @@ +#include "TestEchoComIF.h" +#include "TestCookie.h" + +#include +#include +#include +#include + + +TestEchoComIF::TestEchoComIF(object_id_t objectId): + SystemObject(objectId) { +} + +TestEchoComIF::~TestEchoComIF() {} + +ReturnValue_t TestEchoComIF::initializeInterface(CookieIF * cookie) { + TestCookie* dummyCookie = dynamic_cast(cookie); + if(dummyCookie == nullptr) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "TestEchoComIF::initializeInterface: Invalid cookie!" << std::endl; +#else + sif::printWarning("TestEchoComIF::initializeInterface: Invalid cookie!\n"); +#endif + return NULLPOINTER; + } + + auto resultPair = replyMap.emplace( + dummyCookie->getAddress(), ReplyBuffer(dummyCookie->getReplyMaxLen())); + if(not resultPair.second) { + return HasReturnvaluesIF::RETURN_FAILED; + } + return RETURN_OK; +} + +ReturnValue_t TestEchoComIF::sendMessage(CookieIF *cookie, + const uint8_t * sendData, size_t sendLen) { + TestCookie* dummyCookie = dynamic_cast(cookie); + if(dummyCookie == nullptr) { + return NULLPOINTER; + } + + ReplyBuffer& replyBuffer = replyMap.find(dummyCookie->getAddress())->second; + if(sendLen > replyBuffer.capacity()) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "TestEchoComIF::sendMessage: Send length " << sendLen << " larger than " + "current reply buffer length!" << std::endl; +#else + sif::printWarning("TestEchoComIF::sendMessage: Send length %d larger than current " + "reply buffer length!\n", sendLen); +#endif + return HasReturnvaluesIF::RETURN_FAILED; + } + replyBuffer.resize(sendLen); + memcpy(replyBuffer.data(), sendData, sendLen); + return RETURN_OK; +} + +ReturnValue_t TestEchoComIF::getSendSuccess(CookieIF *cookie) { + return RETURN_OK; +} + +ReturnValue_t TestEchoComIF::requestReceiveMessage(CookieIF *cookie, + size_t requestLen) { + return RETURN_OK; +} + +ReturnValue_t TestEchoComIF::readReceivedMessage(CookieIF *cookie, + uint8_t **buffer, size_t *size) { + TestCookie* dummyCookie = dynamic_cast(cookie); + if(dummyCookie == nullptr) { + return NULLPOINTER; + } + + ReplyBuffer& replyBuffer = replyMap.find(dummyCookie->getAddress())->second; + *buffer = replyBuffer.data(); + *size = replyBuffer.size(); + + dummyReplyCounter ++; + if(dummyReplyCounter == 10) { + // add anything that needs to be read periodically by dummy handler + dummyReplyCounter = 0; + } + return RETURN_OK; + +} + diff --git a/tests/src/fsfw_tests/integration/devices/TestEchoComIF.h b/tests/src/fsfw_tests/integration/devices/TestEchoComIF.h new file mode 100644 index 00000000..38270cfe --- /dev/null +++ b/tests/src/fsfw_tests/integration/devices/TestEchoComIF.h @@ -0,0 +1,56 @@ +#ifndef TEST_TESTDEVICES_TESTECHOCOMIF_H_ +#define TEST_TESTDEVICES_TESTECHOCOMIF_H_ + +#include +#include +#include +#include + +#include + +/** + * @brief Used to simply returned sent data from device handler + * @details Assign this com IF in the factory when creating the device handler + * @ingroup test + */ +class TestEchoComIF: public DeviceCommunicationIF, public SystemObject { +public: + TestEchoComIF(object_id_t objectId); + virtual ~TestEchoComIF(); + + /** + * DeviceCommunicationIF overrides + * (see DeviceCommunicationIF documentation + */ + ReturnValue_t initializeInterface(CookieIF * cookie) override; + ReturnValue_t sendMessage(CookieIF *cookie, const uint8_t * sendData, + size_t sendLen) override; + ReturnValue_t getSendSuccess(CookieIF *cookie) override; + ReturnValue_t requestReceiveMessage(CookieIF *cookie, + size_t requestLen) override; + ReturnValue_t readReceivedMessage(CookieIF *cookie, uint8_t **buffer, + size_t *size) override; + +private: + + /** + * Send TM packet which contains received data as TM[17,130]. + * Wiretapping will do the same. + * @param data + * @param len + */ + void sendTmPacket(const uint8_t *data,uint32_t len); + + AcceptsTelemetryIF* funnel = nullptr; + MessageQueueIF* tmQueue = nullptr; + size_t replyMaxLen = 0; + + using ReplyBuffer = std::vector; + std::map replyMap; + + uint8_t dummyReplyCounter = 0; + + uint16_t packetSubCounter = 0; +}; + +#endif /* TEST_TESTDEVICES_TESTECHOCOMIF_H_ */ diff --git a/tests/src/fsfw_tests/integration/devices/devicedefinitions/testDeviceDefinitions.h b/tests/src/fsfw_tests/integration/devices/devicedefinitions/testDeviceDefinitions.h new file mode 100644 index 00000000..1c112e3f --- /dev/null +++ b/tests/src/fsfw_tests/integration/devices/devicedefinitions/testDeviceDefinitions.h @@ -0,0 +1,97 @@ +#ifndef MISSION_DEVICES_DEVICEDEFINITIONS_TESTDEVICEDEFINITIONS_H_ +#define MISSION_DEVICES_DEVICEDEFINITIONS_TESTDEVICEDEFINITIONS_H_ + +#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; + +class TestDataSet: public StaticLocalDataSet<3> { +public: + TestDataSet(HasLocalDataPoolIF* owner): StaticLocalDataSet(owner, TEST_SET_ID) {} + TestDataSet(object_id_t owner): StaticLocalDataSet(sid_t(owner, TEST_SET_ID)) {} + + lp_var_t testUint8Var = lp_var_t( + gp_id_t(this->getCreatorObjectId(), PoolIds::TEST_UINT8_ID), this); + lp_var_t testUint32Var = lp_var_t( + gp_id_t(this->getCreatorObjectId(), PoolIds::TEST_UINT32_ID), this); + lp_vec_t testFloat3Vec = lp_vec_t( + gp_id_t(this->getCreatorObjectId(), PoolIds::TEST_FLOAT_VEC_3_ID), this); +}; + +} + +#endif /* MISSION_DEVICES_DEVICEDEFINITIONS_TESTDEVICEDEFINITIONS_H_ */ diff --git a/tests/src/fsfw_tests/integration/task/CMakeLists.txt b/tests/src/fsfw_tests/integration/task/CMakeLists.txt new file mode 100644 index 00000000..0402d093 --- /dev/null +++ b/tests/src/fsfw_tests/integration/task/CMakeLists.txt @@ -0,0 +1,3 @@ +target_sources(${TARGET_NAME} PRIVATE + TestTask.cpp +) \ No newline at end of file diff --git a/tests/src/fsfw_tests/integration/task/TestTask.cpp b/tests/src/fsfw_tests/integration/task/TestTask.cpp new file mode 100644 index 00000000..b33bd51c --- /dev/null +++ b/tests/src/fsfw_tests/integration/task/TestTask.cpp @@ -0,0 +1,68 @@ +#include "TestTask.h" + +#include +#include + +bool TestTask::oneShotAction = true; +MutexIF* TestTask::testLock = nullptr; + +TestTask::TestTask(object_id_t objectId): + SystemObject(objectId), testMode(testModes::A) { + 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; + return result; +} + +ReturnValue_t TestTask::performActionA() { + /* This is performed each alternating task cycle */ + ReturnValue_t result = RETURN_OK; + return result; +} + +ReturnValue_t TestTask::performActionB() { + /* This is performed each alternating task cycle */ + ReturnValue_t result = RETURN_OK; + return result; +} + diff --git a/tests/src/fsfw_tests/integration/task/TestTask.h b/tests/src/fsfw_tests/integration/task/TestTask.h new file mode 100644 index 00000000..e56b5581 --- /dev/null +++ b/tests/src/fsfw_tests/integration/task/TestTask.h @@ -0,0 +1,43 @@ +#ifndef MISSION_DEMO_TESTTASK_H_ +#define MISSION_DEMO_TESTTASK_H_ + +#include +#include +#include + +/** + * @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); + virtual ~TestTask(); + virtual ReturnValue_t performOperation(uint8_t operationCode = 0) override; + +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 testFlag = false; + +private: + static bool oneShotAction; + static MutexIF* testLock; + StorageManagerIF* IPCStore; +}; + +#endif /* TESTTASK_H_ */ diff --git a/tests/src/fsfw_tests/internal/InternalUnitTester.cpp b/tests/src/fsfw_tests/internal/InternalUnitTester.cpp index 20998d64..3c8eec1e 100644 --- a/tests/src/fsfw_tests/internal/InternalUnitTester.cpp +++ b/tests/src/fsfw_tests/internal/InternalUnitTester.cpp @@ -1,9 +1,9 @@ #include "fsfw_tests/internal/InternalUnitTester.h" #include "fsfw_tests/internal/UnittDefinitions.h" -#include "fsfw_tests/internal/osal/IntTestMq.h" -#include "fsfw_tests/internal/osal/IntTestSemaphore.h" -#include "fsfw_tests/internal/osal/IntTestMutex.h" +#include "fsfw_tests/internal/osal/testMq.h" +#include "fsfw_tests/internal/osal/testSemaphore.h" +#include "fsfw_tests/internal/osal/testMutex.h" #include "fsfw_tests/internal/serialize/IntTestSerialization.h" #include "fsfw_tests/internal/globalfunctions/TestArrayPrinter.h" diff --git a/tests/src/fsfw_tests/internal/osal/CMakeLists.txt b/tests/src/fsfw_tests/internal/osal/CMakeLists.txt index 84316089..8d79d759 100644 --- a/tests/src/fsfw_tests/internal/osal/CMakeLists.txt +++ b/tests/src/fsfw_tests/internal/osal/CMakeLists.txt @@ -1,5 +1,5 @@ target_sources(${LIB_FSFW_NAME} PRIVATE - IntTestMq.cpp - IntTestMutex.cpp - IntTestSemaphore.cpp + testMq.cpp + testMutex.cpp + testSemaphore.cpp ) diff --git a/tests/src/fsfw_tests/internal/osal/IntTestMq.cpp b/tests/src/fsfw_tests/internal/osal/testMq.cpp similarity index 96% rename from tests/src/fsfw_tests/internal/osal/IntTestMq.cpp rename to tests/src/fsfw_tests/internal/osal/testMq.cpp index 6c31b354..8a252910 100644 --- a/tests/src/fsfw_tests/internal/osal/IntTestMq.cpp +++ b/tests/src/fsfw_tests/internal/osal/testMq.cpp @@ -1,4 +1,4 @@ -#include "fsfw_tests/internal/osal/IntTestMq.h" +#include "testMq.h" #include "fsfw_tests/internal/UnittDefinitions.h" #include diff --git a/tests/src/fsfw_tests/internal/osal/IntTestMq.h b/tests/src/fsfw_tests/internal/osal/testMq.h similarity index 100% rename from tests/src/fsfw_tests/internal/osal/IntTestMq.h rename to tests/src/fsfw_tests/internal/osal/testMq.h diff --git a/tests/src/fsfw_tests/internal/osal/IntTestMutex.cpp b/tests/src/fsfw_tests/internal/osal/testMutex.cpp similarity index 96% rename from tests/src/fsfw_tests/internal/osal/IntTestMutex.cpp rename to tests/src/fsfw_tests/internal/osal/testMutex.cpp index d9184cd8..9b50121a 100644 --- a/tests/src/fsfw_tests/internal/osal/IntTestMutex.cpp +++ b/tests/src/fsfw_tests/internal/osal/testMutex.cpp @@ -1,4 +1,4 @@ -#include "fsfw_tests/internal/osal/IntTestMutex.h" +#include "testMutex.h" #include "fsfw_tests/internal/UnittDefinitions.h" #include "fsfw/platform.h" diff --git a/tests/src/fsfw_tests/internal/osal/IntTestMutex.h b/tests/src/fsfw_tests/internal/osal/testMutex.h similarity index 100% rename from tests/src/fsfw_tests/internal/osal/IntTestMutex.h rename to tests/src/fsfw_tests/internal/osal/testMutex.h diff --git a/tests/src/fsfw_tests/internal/osal/IntTestSemaphore.cpp b/tests/src/fsfw_tests/internal/osal/testSemaphore.cpp similarity index 98% rename from tests/src/fsfw_tests/internal/osal/IntTestSemaphore.cpp rename to tests/src/fsfw_tests/internal/osal/testSemaphore.cpp index 4b28f961..458dcb04 100644 --- a/tests/src/fsfw_tests/internal/osal/IntTestSemaphore.cpp +++ b/tests/src/fsfw_tests/internal/osal/testSemaphore.cpp @@ -1,5 +1,5 @@ #include "fsfw/FSFW.h" -#include "fsfw_tests/internal/osal/IntTestSemaphore.h" +#include "testSemaphore.h" #include "fsfw_tests/internal/UnittDefinitions.h" #include "fsfw/tasks/SemaphoreFactory.h" diff --git a/tests/src/fsfw_tests/internal/osal/IntTestSemaphore.h b/tests/src/fsfw_tests/internal/osal/testSemaphore.h similarity index 100% rename from tests/src/fsfw_tests/internal/osal/IntTestSemaphore.h rename to tests/src/fsfw_tests/internal/osal/testSemaphore.h diff --git a/tests/src/fsfw_tests/unit/CMakeLists.txt b/tests/src/fsfw_tests/unit/CMakeLists.txt index f30e4b6b..164e3bde 100644 --- a/tests/src/fsfw_tests/unit/CMakeLists.txt +++ b/tests/src/fsfw_tests/unit/CMakeLists.txt @@ -1,16 +1,17 @@ -target_sources(${TARGET_NAME} PRIVATE +target_sources(${FSFW_TEST_TGT} PRIVATE CatchDefinitions.cpp CatchFactory.cpp printChar.cpp ) -if(FSFW_CUSTOM_UNITTEST_RUNNER) - target_sources(${TARGET_NAME} PRIVATE +# if(FSFW_CUSTOM_UNITTEST_RUNNER) + target_sources(${FSFW_TEST_TGT} PRIVATE CatchRunner.cpp CatchSetup.cpp ) -endif() +# endif() +add_subdirectory(testcfg) add_subdirectory(action) add_subdirectory(container) add_subdirectory(osal) diff --git a/tests/src/fsfw_tests/unit/CatchFactory.cpp b/tests/src/fsfw_tests/unit/CatchFactory.cpp index 010ab5dd..42cb927e 100644 --- a/tests/src/fsfw_tests/unit/CatchFactory.cpp +++ b/tests/src/fsfw_tests/unit/CatchFactory.cpp @@ -1,4 +1,5 @@ #include "CatchFactory.h" +#include "tests/TestsConfig.h" #include "datapoollocal/LocalPoolOwnerBase.h" #include "mocks/HkReceiverMock.h" diff --git a/tests/src/fsfw_tests/unit/CatchFactory.h b/tests/src/fsfw_tests/unit/CatchFactory.h index 38ec46bd..cc14e3d9 100644 --- a/tests/src/fsfw_tests/unit/CatchFactory.h +++ b/tests/src/fsfw_tests/unit/CatchFactory.h @@ -1,7 +1,7 @@ #ifndef FSFW_CATCHFACTORY_H_ #define FSFW_CATCHFACTORY_H_ -#include "TestsConfig.h" +#include "tests/TestsConfig.h" #include "fsfw/objectmanager/SystemObjectIF.h" #include "fsfw/objectmanager/ObjectManager.h" diff --git a/tests/src/fsfw_tests/unit/CatchRunner.cpp b/tests/src/fsfw_tests/unit/CatchRunner.cpp index c96db7f4..1ea3ab35 100644 --- a/tests/src/fsfw_tests/unit/CatchRunner.cpp +++ b/tests/src/fsfw_tests/unit/CatchRunner.cpp @@ -14,7 +14,7 @@ extern int customSetup(); -int fsfwtest::customMain(int argc, char* argv[]) { +int main(int argc, char* argv[]) { customSetup(); // Catch internal function call diff --git a/tests/src/fsfw_tests/unit/CatchRunner.h b/tests/src/fsfw_tests/unit/CatchRunner.h index 720625c6..06ff07b6 100644 --- a/tests/src/fsfw_tests/unit/CatchRunner.h +++ b/tests/src/fsfw_tests/unit/CatchRunner.h @@ -7,7 +7,7 @@ namespace fsfwtest { * Can be called by upper level main() if default Catch2 main is overriden * @return */ -int customMain(int argc, char* argv[]); +//int customMain(int argc, char* argv[]); } diff --git a/tests/src/fsfw_tests/unit/action/CMakeLists.txt b/tests/src/fsfw_tests/unit/action/CMakeLists.txt index 0339000f..659f251a 100644 --- a/tests/src/fsfw_tests/unit/action/CMakeLists.txt +++ b/tests/src/fsfw_tests/unit/action/CMakeLists.txt @@ -1,3 +1,3 @@ -target_sources(${TARGET_NAME} PRIVATE +target_sources(${FSFW_TEST_TGT} PRIVATE TestActionHelper.cpp ) diff --git a/tests/src/fsfw_tests/unit/container/CMakeLists.txt b/tests/src/fsfw_tests/unit/container/CMakeLists.txt index 966c5834..5dae974c 100644 --- a/tests/src/fsfw_tests/unit/container/CMakeLists.txt +++ b/tests/src/fsfw_tests/unit/container/CMakeLists.txt @@ -1,4 +1,4 @@ -target_sources(${TARGET_NAME} PRIVATE +target_sources(${FSFW_TEST_TGT} PRIVATE RingBufferTest.cpp TestArrayList.cpp TestDynamicFifo.cpp diff --git a/tests/src/fsfw_tests/unit/container/RingBufferTest.cpp b/tests/src/fsfw_tests/unit/container/RingBufferTest.cpp index 819401ab..0be8b2a7 100644 --- a/tests/src/fsfw_tests/unit/container/RingBufferTest.cpp +++ b/tests/src/fsfw_tests/unit/container/RingBufferTest.cpp @@ -78,7 +78,7 @@ TEST_CASE("Ring Buffer Test" , "[RingBufferTest]") { TEST_CASE("Ring Buffer Test2" , "[RingBufferTest2]") { uint8_t testData[13]= {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; uint8_t readBuffer[10] = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13}; - uint8_t* newBuffer = new uint8_t[10]; + uint8_t* newBuffer = new uint8_t[15]; SimpleRingBuffer ringBuffer(newBuffer, 10, true, 5); SECTION("Simple Test") { @@ -168,7 +168,7 @@ TEST_CASE("Ring Buffer Test2" , "[RingBufferTest2]") { TEST_CASE("Ring Buffer Test3" , "[RingBufferTest3]") { uint8_t testData[13]= {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; uint8_t readBuffer[10] = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13}; - uint8_t* newBuffer = new uint8_t[10]; + uint8_t* newBuffer = new uint8_t[25]; SimpleRingBuffer ringBuffer(newBuffer, 10, true, 15); SECTION("Simple Test") { diff --git a/tests/src/fsfw_tests/unit/datapoollocal/CMakeLists.txt b/tests/src/fsfw_tests/unit/datapoollocal/CMakeLists.txt index 1c98e7dc..bf465282 100644 --- a/tests/src/fsfw_tests/unit/datapoollocal/CMakeLists.txt +++ b/tests/src/fsfw_tests/unit/datapoollocal/CMakeLists.txt @@ -1,4 +1,4 @@ -target_sources(${TARGET_NAME} PRIVATE +target_sources(${FSFW_TEST_TGT} PRIVATE LocalPoolVariableTest.cpp LocalPoolVectorTest.cpp DataSetTest.cpp diff --git a/tests/src/fsfw_tests/unit/datapoollocal/DataSetTest.cpp b/tests/src/fsfw_tests/unit/datapoollocal/DataSetTest.cpp index 94b13f2f..e84f07b6 100644 --- a/tests/src/fsfw_tests/unit/datapoollocal/DataSetTest.cpp +++ b/tests/src/fsfw_tests/unit/datapoollocal/DataSetTest.cpp @@ -1,4 +1,5 @@ #include "LocalPoolOwnerBase.h" +#include "tests/TestsConfig.h" #include "fsfw_tests/unit/CatchDefinitions.h" #include @@ -170,14 +171,19 @@ TEST_CASE("DataSetTest" , "[DataSetTest]") { /* We can do it like this because the buffer only has one byte for less than 8 variables */ uint8_t* validityByte = buffer + sizeof(buffer) - 1; - CHECK(bitutil::bitGet(validityByte, 0) == true); - CHECK(bitutil::bitGet(validityByte, 1) == false); - CHECK(bitutil::bitGet(validityByte, 2) == true); + bool bitSet = false; + bitutil::get(validityByte, 0, bitSet); + + CHECK(bitSet == true); + bitutil::get(validityByte, 1, bitSet); + CHECK(bitSet == false); + bitutil::get(validityByte, 2, bitSet); + CHECK(bitSet == true); /* Now we manipulate the validity buffer for the deserialization */ - bitutil::bitClear(validityByte, 0); - bitutil::bitSet(validityByte, 1); - bitutil::bitClear(validityByte, 2); + bitutil::clear(validityByte, 0); + bitutil::set(validityByte, 1); + bitutil::clear(validityByte, 2); /* Zero out everything except validity buffer */ std::memset(buffer, 0, sizeof(buffer) - 1); sizeToDeserialize = maxSize; @@ -238,8 +244,11 @@ TEST_CASE("DataSetTest" , "[DataSetTest]") { std::memcpy(validityBuffer.data(), buffer + 9 + sizeof(uint16_t) * 3, 2); /* The first 9 variables should be valid */ CHECK(validityBuffer[0] == 0xff); - CHECK(bitutil::bitGet(validityBuffer.data() + 1, 0) == true); - CHECK(bitutil::bitGet(validityBuffer.data() + 1, 1) == false); + bool bitSet = false; + bitutil::get(validityBuffer.data() + 1, 0, bitSet); + CHECK(bitSet == true); + bitutil::get(validityBuffer.data() + 1, 1, bitSet); + CHECK(bitSet == false); /* Now we invert the validity */ validityBuffer[0] = 0; diff --git a/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolManagerTest.cpp b/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolManagerTest.cpp index 7b2f9412..b1160254 100644 --- a/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolManagerTest.cpp +++ b/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolManagerTest.cpp @@ -143,7 +143,7 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { CHECK(cdsShort.msDay_h == Catch::Approx(timeCdsNow.msDay_h).margin(1)); CHECK(cdsShort.msDay_hh == Catch::Approx(timeCdsNow.msDay_hh).margin(1)); CHECK(cdsShort.msDay_l == Catch::Approx(timeCdsNow.msDay_l).margin(1)); - CHECK(cdsShort.msDay_ll == Catch::Approx(timeCdsNow.msDay_ll).margin(1)); + CHECK(cdsShort.msDay_ll == Catch::Approx(timeCdsNow.msDay_ll).margin(5)); } SECTION("VariableSnapshotTest") { @@ -205,7 +205,7 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { CHECK(cdsShort.msDay_h == Catch::Approx(timeCdsNow.msDay_h).margin(1)); CHECK(cdsShort.msDay_hh == Catch::Approx(timeCdsNow.msDay_hh).margin(1)); CHECK(cdsShort.msDay_l == Catch::Approx(timeCdsNow.msDay_l).margin(1)); - CHECK(cdsShort.msDay_ll == Catch::Approx(timeCdsNow.msDay_ll).margin(1)); + CHECK(cdsShort.msDay_ll == Catch::Approx(timeCdsNow.msDay_ll).margin(5)); } SECTION("VariableNotificationTest") { diff --git a/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolOwnerBase.h b/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolOwnerBase.h index ea5bb7e0..1f532568 100644 --- a/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolOwnerBase.h +++ b/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolOwnerBase.h @@ -1,7 +1,7 @@ #ifndef FSFW_UNITTEST_TESTS_DATAPOOLLOCAL_LOCALPOOLOWNERBASE_H_ #define FSFW_UNITTEST_TESTS_DATAPOOLLOCAL_LOCALPOOLOWNERBASE_H_ -#include "objects/systemObjectList.h" +#include "tests/TestsConfig.h" #include "../mocks/MessageQueueMockBase.h" #include diff --git a/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolVariableTest.cpp b/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolVariableTest.cpp index 648a76e2..b029ec26 100644 --- a/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolVariableTest.cpp +++ b/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolVariableTest.cpp @@ -1,4 +1,5 @@ #include "LocalPoolOwnerBase.h" +#include "tests/TestsConfig.h" #include "fsfw_tests/unit/CatchDefinitions.h" #include diff --git a/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolVectorTest.cpp b/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolVectorTest.cpp index 3f846dec..5298e5b9 100644 --- a/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolVectorTest.cpp +++ b/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolVectorTest.cpp @@ -1,4 +1,5 @@ #include "LocalPoolOwnerBase.h" +#include "tests/TestsConfig.h" #include "fsfw_tests/unit/CatchDefinitions.h" #include diff --git a/tests/src/fsfw_tests/unit/globalfunctions/CMakeLists.txt b/tests/src/fsfw_tests/unit/globalfunctions/CMakeLists.txt index 617c7f5a..3b29f23f 100644 --- a/tests/src/fsfw_tests/unit/globalfunctions/CMakeLists.txt +++ b/tests/src/fsfw_tests/unit/globalfunctions/CMakeLists.txt @@ -1,3 +1,5 @@ -target_sources(${TARGET_NAME} PRIVATE +target_sources(${FSFW_TEST_TGT} PRIVATE testDleEncoder.cpp + testOpDivider.cpp + testBitutil.cpp ) diff --git a/tests/src/fsfw_tests/unit/globalfunctions/testBitutil.cpp b/tests/src/fsfw_tests/unit/globalfunctions/testBitutil.cpp new file mode 100644 index 00000000..2627adcf --- /dev/null +++ b/tests/src/fsfw_tests/unit/globalfunctions/testBitutil.cpp @@ -0,0 +1,64 @@ +#include "fsfw/globalfunctions/bitutility.h" +#include + +TEST_CASE("Bitutility" , "[Bitutility]") { + uint8_t dummyByte = 0; + bool bitSet = false; + for(uint8_t pos = 0; pos < 8; pos++) { + bitutil::set(&dummyByte, pos); + REQUIRE(dummyByte == (1 << (7 - pos))); + bitutil::get(&dummyByte, pos, bitSet); + REQUIRE(bitSet == 1); + dummyByte = 0; + } + + dummyByte = 0xff; + for(uint8_t pos = 0; pos < 8; pos++) { + bitutil::get(&dummyByte, pos, bitSet); + REQUIRE(bitSet == 1); + bitutil::clear(&dummyByte, pos); + bitutil::get(&dummyByte, pos, bitSet); + REQUIRE(bitSet == 0); + dummyByte = 0xff; + } + + dummyByte = 0xf0; + for(uint8_t pos = 0; pos < 8; pos++) { + if(pos < 4) { + bitutil::get(&dummyByte, pos, bitSet); + REQUIRE(bitSet == 1); + bitutil::toggle(&dummyByte, pos); + bitutil::get(&dummyByte, pos, bitSet); + REQUIRE(bitSet == 0); + } + else { + bitutil::get(&dummyByte, pos, bitSet); + REQUIRE(bitSet == false); + bitutil::toggle(&dummyByte, pos); + bitutil::get(&dummyByte, pos, bitSet); + REQUIRE(bitSet == true); + } + } + REQUIRE(dummyByte == 0x0f); + + dummyByte = 0; + bitutil::set(&dummyByte, 8); + REQUIRE(dummyByte == 0); + bitutil::set(&dummyByte, -1); + REQUIRE(dummyByte == 0); + dummyByte = 0xff; + bitutil::clear(&dummyByte, 8); + REQUIRE(dummyByte == 0xff); + bitutil::clear(&dummyByte, -1); + REQUIRE(dummyByte == 0xff); + dummyByte = 0x00; + bitutil::toggle(&dummyByte, 8); + REQUIRE(dummyByte == 0x00); + bitutil::toggle(&dummyByte, -1); + REQUIRE(dummyByte == 0x00); + + REQUIRE(bitutil::get(&dummyByte, 8, bitSet) == false); +} + + + diff --git a/tests/src/fsfw_tests/unit/globalfunctions/testOpDivider.cpp b/tests/src/fsfw_tests/unit/globalfunctions/testOpDivider.cpp new file mode 100644 index 00000000..c02ada83 --- /dev/null +++ b/tests/src/fsfw_tests/unit/globalfunctions/testOpDivider.cpp @@ -0,0 +1,64 @@ +#include "fsfw/globalfunctions/PeriodicOperationDivider.h" +#include + +TEST_CASE("OpDivider" , "[OpDivider]") { + auto opDivider = PeriodicOperationDivider(1); + REQUIRE(opDivider.getDivider() == 1); + REQUIRE(opDivider.getCounter() == 1); + REQUIRE(opDivider.check() == true); + REQUIRE(opDivider.checkAndIncrement() == true); + REQUIRE(opDivider.getCounter() == 1); + REQUIRE(opDivider.check() == true); + REQUIRE(opDivider.checkAndIncrement() == true); + REQUIRE(opDivider.checkAndIncrement() == true); + + opDivider.setDivider(0); + REQUIRE(opDivider.getCounter() == 1); + REQUIRE(opDivider.checkAndIncrement() == true); + REQUIRE(opDivider.getCounter() == 1); + REQUIRE(opDivider.checkAndIncrement() == true); + REQUIRE(opDivider.checkAndIncrement() == true); + + opDivider.setDivider(2); + opDivider.resetCounter(); + REQUIRE(opDivider.getDivider() == 2); + REQUIRE(opDivider.getCounter() == 1); + REQUIRE(opDivider.check() == false); + REQUIRE(opDivider.checkAndIncrement() == false); + REQUIRE(opDivider.getCounter() == 2); + REQUIRE(opDivider.check() == true); + REQUIRE(opDivider.checkAndIncrement() == true); + REQUIRE(opDivider.getCounter() == 1); + REQUIRE(opDivider.check() == false); + REQUIRE(opDivider.checkAndIncrement() == false); + REQUIRE(opDivider.getCounter() == 2); + REQUIRE(opDivider.checkAndIncrement() == true); + REQUIRE(opDivider.checkAndIncrement() == false); + REQUIRE(opDivider.checkAndIncrement() == true); + REQUIRE(opDivider.checkAndIncrement() == false); + + opDivider.setDivider(3); + opDivider.resetCounter(); + REQUIRE(opDivider.checkAndIncrement() == false); + REQUIRE(opDivider.checkAndIncrement() == false); + REQUIRE(opDivider.getCounter() == 3); + REQUIRE(opDivider.checkAndIncrement() == true); + REQUIRE(opDivider.getCounter() == 1); + REQUIRE(opDivider.checkAndIncrement() == false); + + auto opDividerNonResetting = PeriodicOperationDivider(2, false); + REQUIRE(opDividerNonResetting.getCounter() == 1); + REQUIRE(opDividerNonResetting.check() == false); + REQUIRE(opDividerNonResetting.checkAndIncrement() == false); + REQUIRE(opDividerNonResetting.getCounter() == 2); + REQUIRE(opDividerNonResetting.check() == true); + REQUIRE(opDividerNonResetting.checkAndIncrement() == true); + REQUIRE(opDividerNonResetting.getCounter() == 3); + REQUIRE(opDividerNonResetting.checkAndIncrement() == true); + REQUIRE(opDividerNonResetting.getCounter() == 4); + opDividerNonResetting.resetCounter(); + REQUIRE(opDividerNonResetting.getCounter() == 1); + REQUIRE(opDividerNonResetting.check() == false); + REQUIRE(opDividerNonResetting.checkAndIncrement() == false); + REQUIRE(opDividerNonResetting.getCounter() == 2); +} diff --git a/tests/src/fsfw_tests/unit/osal/CMakeLists.txt b/tests/src/fsfw_tests/unit/osal/CMakeLists.txt index 5ca5e400..293be2e8 100644 --- a/tests/src/fsfw_tests/unit/osal/CMakeLists.txt +++ b/tests/src/fsfw_tests/unit/osal/CMakeLists.txt @@ -1,4 +1,4 @@ -target_sources(${TARGET_NAME} PRIVATE +target_sources(${FSFW_TEST_TGT} PRIVATE TestMessageQueue.cpp TestSemaphore.cpp ) diff --git a/tests/src/fsfw_tests/unit/serialize/CMakeLists.txt b/tests/src/fsfw_tests/unit/serialize/CMakeLists.txt index 5a9d9a0f..96c80f4a 100644 --- a/tests/src/fsfw_tests/unit/serialize/CMakeLists.txt +++ b/tests/src/fsfw_tests/unit/serialize/CMakeLists.txt @@ -1,4 +1,4 @@ -target_sources(${TARGET_NAME} PRIVATE +target_sources(${FSFW_TEST_TGT} PRIVATE TestSerialBufferAdapter.cpp TestSerialization.cpp TestSerialLinkedPacket.cpp diff --git a/tests/src/fsfw_tests/unit/storagemanager/CMakeLists.txt b/tests/src/fsfw_tests/unit/storagemanager/CMakeLists.txt index ed7be7d5..7b6280df 100644 --- a/tests/src/fsfw_tests/unit/storagemanager/CMakeLists.txt +++ b/tests/src/fsfw_tests/unit/storagemanager/CMakeLists.txt @@ -1,4 +1,4 @@ -target_sources(${TARGET_NAME} PRIVATE +target_sources(${FSFW_TEST_TGT} PRIVATE TestNewAccessor.cpp TestPool.cpp ) diff --git a/tests/src/fsfw_tests/unit/testcfg/CMakeLists.txt b/tests/src/fsfw_tests/unit/testcfg/CMakeLists.txt new file mode 100644 index 00000000..f840e38b --- /dev/null +++ b/tests/src/fsfw_tests/unit/testcfg/CMakeLists.txt @@ -0,0 +1,23 @@ +target_sources(${FSFW_TEST_TGT} PRIVATE + ipc/MissionMessageTypes.cpp + pollingsequence/PollingSequenceFactory.cpp +) + +# Add include paths for the executable +target_include_directories(${FSFW_TEST_TGT} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +# If a special translation file for object IDs exists, compile it. +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/objects/translateObjects.cpp") + target_sources(${FSFW_TEST_TGT} PRIVATE + objects/translateObjects.cpp + ) +endif() + +# If a special translation file for events exists, compile it. +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/objects/translateObjects.cpp") + target_sources(${FSFW_TEST_TGT} PRIVATE + events/translateEvents.cpp + ) +endif() \ No newline at end of file diff --git a/tests/user/testcfg/FSFWConfig.h.in b/tests/src/fsfw_tests/unit/testcfg/FSFWConfig.h.in similarity index 67% rename from tests/user/testcfg/FSFWConfig.h.in rename to tests/src/fsfw_tests/unit/testcfg/FSFWConfig.h.in index d38f0648..f05ef40b 100644 --- a/tests/user/testcfg/FSFWConfig.h.in +++ b/tests/src/fsfw_tests/unit/testcfg/FSFWConfig.h.in @@ -7,33 +7,30 @@ //! Used to determine whether C++ ostreams are used which can increase //! the binary size significantly. If this is disabled, //! the C stdio functions can be used alternatively -#define FSFW_CPP_OSTREAM_ENABLED 0 +#define FSFW_CPP_OSTREAM_ENABLED 0 -//! More FSFW related printouts. Useful for development. -#define FSFW_ENHANCED_PRINTOUT 0 +//! More FSFW related printouts depending on level. Useful for development. +#define FSFW_VERBOSE_LEVEL 0 //! Can be used to completely disable printouts, even the C stdio ones. -//! By default, printouts will be disabled for the unit tests. -#if FSFW_CPP_OSTREAM_ENABLED == 0 && FSFW_ENHANCED_PRINTOUT == 0 - #ifndef FSFW_DISABLE_PRINTOUT - #define FSFW_DISABLE_PRINTOUT 1 - #endif +#if FSFW_CPP_OSTREAM_ENABLED == 0 && FSFW_VERBOSE_LEVEL == 0 + #define FSFW_DISABLE_PRINTOUT 1 #endif -//! Can be used to enable additional debugging printouts for developing the FSFW -#define FSFW_PRINT_VERBOSITY_LEVEL 0 +#define FSFW_USE_PUS_C_TELEMETRY 1 +#define FSFW_USE_PUS_C_TELECOMMANDS 1 //! Can be used to disable the ANSI color sequences for C stdio. -#define FSFW_COLORED_OUTPUT 0 +#define FSFW_COLORED_OUTPUT 1 //! If FSFW_OBJ_EVENT_TRANSLATION is set to one, //! additional output which requires the translation files translateObjects //! and translateEvents (and their compiled source files) -#define FSFW_OBJ_EVENT_TRANSLATION 0 +#define FSFW_OBJ_EVENT_TRANSLATION 0 #if FSFW_OBJ_EVENT_TRANSLATION == 1 //! Specify whether info events are printed too. -#define FSFW_DEBUG_INFO 1 +#define FSFW_DEBUG_INFO 1 #include "objects/translateObjects.h" #include "events/translateEvents.h" #else @@ -41,22 +38,22 @@ //! When using the newlib nano library, C99 support for stdio facilities //! will not be provided. This define should be set to 1 if this is the case. -#define FSFW_NO_C99_IO 1 +#define FSFW_NO_C99_IO 1 //! Specify whether a special mode store is used for Subsystem components. -#define FSFW_USE_MODESTORE 0 +#define FSFW_USE_MODESTORE 0 //! Defines if the real time scheduler for linux should be used. //! If set to 0, this will also disable priority settings for linux //! as most systems will not allow to set nice values without privileges //! For embedded linux system set this to 1. //! If set to 1 the binary needs "cap_sys_nice=eip" privileges to run -#define FSFW_USE_REALTIME_FOR_LINUX 1 +#define FSFW_USE_REALTIME_FOR_LINUX 1 namespace fsfwconfig { -//! Default timestamp size. The default timestamp will be an eight byte CDC -//! short timestamp. -static constexpr uint8_t FSFW_MISSION_TIMESTAMP_SIZE = 8; + +//! Default timestamp size. The default timestamp will be an seven byte CDC short timestamp. +static constexpr uint8_t FSFW_MISSION_TIMESTAMP_SIZE = 7; //! Configure the allocated pool sizes for the event manager. static constexpr size_t FSFW_EVENTMGMR_MATCHTREE_NODES = 240; @@ -65,13 +62,13 @@ static constexpr size_t FSFW_EVENTMGMR_RANGEMATCHERS = 120; //! Defines the FIFO depth of each commanding service base which //! also determines how many commands a CSB service can handle in one cycle -//! simulataneously. This will increase the required RAM for +//! simultaneously. This will increase the required RAM for //! each CSB service ! static constexpr uint8_t FSFW_CSB_FIFO_DEPTH = 6; static constexpr size_t FSFW_PRINT_BUFFER_SIZE = 124; -static constexpr size_t FSFW_MAX_TM_PACKET_SIZE = 1500; +static constexpr size_t FSFW_MAX_TM_PACKET_SIZE = 2048; } diff --git a/tests/src/fsfw_tests/unit/testcfg/OBSWConfig.h.in b/tests/src/fsfw_tests/unit/testcfg/OBSWConfig.h.in new file mode 100644 index 00000000..5d8a9255 --- /dev/null +++ b/tests/src/fsfw_tests/unit/testcfg/OBSWConfig.h.in @@ -0,0 +1,15 @@ +#ifndef CONFIG_TMTC_TMTCSIZE_H_ +#define CONFIG_TMTC_TMTCSIZE_H_ + +#include +#include + +#define OBSW_PRINT_MISSED_DEADLINES 0 +#define OBSW_VERBOSE_LEVEL 0 +#define OBSW_ADD_TEST_CODE 1 + +namespace config { +static constexpr uint32_t MAX_STORED_TELECOMMANDS = 2000; +} + +#endif /* CONFIG_TMTC_TMTCSIZE_H_ */ diff --git a/tests/user/testcfg/TestsConfig.h.in b/tests/src/fsfw_tests/unit/testcfg/TestsConfig.h.in similarity index 100% rename from tests/user/testcfg/TestsConfig.h.in rename to tests/src/fsfw_tests/unit/testcfg/TestsConfig.h.in diff --git a/tests/user/testcfg/devices/logicalAddresses.cpp b/tests/src/fsfw_tests/unit/testcfg/devices/logicalAddresses.cpp similarity index 100% rename from tests/user/testcfg/devices/logicalAddresses.cpp rename to tests/src/fsfw_tests/unit/testcfg/devices/logicalAddresses.cpp diff --git a/tests/user/testcfg/devices/logicalAddresses.h b/tests/src/fsfw_tests/unit/testcfg/devices/logicalAddresses.h similarity index 84% rename from tests/user/testcfg/devices/logicalAddresses.h rename to tests/src/fsfw_tests/unit/testcfg/devices/logicalAddresses.h index cdf87025..d7b73e15 100644 --- a/tests/user/testcfg/devices/logicalAddresses.h +++ b/tests/src/fsfw_tests/unit/testcfg/devices/logicalAddresses.h @@ -2,7 +2,7 @@ #define CONFIG_DEVICES_LOGICALADDRESSES_H_ #include -#include + #include namespace addresses { diff --git a/tests/user/testcfg/devices/powerSwitcherList.cpp b/tests/src/fsfw_tests/unit/testcfg/devices/powerSwitcherList.cpp similarity index 100% rename from tests/user/testcfg/devices/powerSwitcherList.cpp rename to tests/src/fsfw_tests/unit/testcfg/devices/powerSwitcherList.cpp diff --git a/tests/user/testcfg/devices/powerSwitcherList.h b/tests/src/fsfw_tests/unit/testcfg/devices/powerSwitcherList.h similarity index 100% rename from tests/user/testcfg/devices/powerSwitcherList.h rename to tests/src/fsfw_tests/unit/testcfg/devices/powerSwitcherList.h diff --git a/tests/user/testcfg/events/subsystemIdRanges.h b/tests/src/fsfw_tests/unit/testcfg/events/subsystemIdRanges.h similarity index 81% rename from tests/user/testcfg/events/subsystemIdRanges.h rename to tests/src/fsfw_tests/unit/testcfg/events/subsystemIdRanges.h index 24eee819..b14c4bc5 100644 --- a/tests/user/testcfg/events/subsystemIdRanges.h +++ b/tests/src/fsfw_tests/unit/testcfg/events/subsystemIdRanges.h @@ -1,8 +1,9 @@ #ifndef CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_ #define CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_ +#include "fsfw/events/fwSubsystemIdRanges.h" #include -#include + /** * @brief Custom subsystem IDs can be added here @@ -12,6 +13,7 @@ namespace SUBSYSTEM_ID { enum: uint8_t { SUBSYSTEM_ID_START = FW_SUBSYSTEM_ID_RANGE, + SUBSYSTEM_ID_END // [EXPORT] : [END] }; } diff --git a/tests/user/testcfg/ipc/MissionMessageTypes.cpp b/tests/src/fsfw_tests/unit/testcfg/ipc/MissionMessageTypes.cpp similarity index 100% rename from tests/user/testcfg/ipc/MissionMessageTypes.cpp rename to tests/src/fsfw_tests/unit/testcfg/ipc/MissionMessageTypes.cpp diff --git a/tests/user/testcfg/ipc/MissionMessageTypes.h b/tests/src/fsfw_tests/unit/testcfg/ipc/MissionMessageTypes.h similarity index 100% rename from tests/user/testcfg/ipc/MissionMessageTypes.h rename to tests/src/fsfw_tests/unit/testcfg/ipc/MissionMessageTypes.h diff --git a/tests/user/testcfg/objects/systemObjectList.h b/tests/src/fsfw_tests/unit/testcfg/objects/systemObjectList.h similarity index 84% rename from tests/user/testcfg/objects/systemObjectList.h rename to tests/src/fsfw_tests/unit/testcfg/objects/systemObjectList.h index efd21e0d..accf0d81 100644 --- a/tests/user/testcfg/objects/systemObjectList.h +++ b/tests/src/fsfw_tests/unit/testcfg/objects/systemObjectList.h @@ -1,8 +1,8 @@ #ifndef HOSTED_CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_ #define HOSTED_CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_ +#include "fsfw/objectmanager/frameworkObjects.h" #include -#include // The objects will be instantiated in the ID order namespace objects { @@ -11,10 +11,6 @@ namespace objects { FSFW_CONFIG_RESERVED_START = PUS_SERVICE_1_VERIFICATION, FSFW_CONFIG_RESERVED_END = TM_STORE, - CCSDS_DISTRIBUTOR = 10, - PUS_DISTRIBUTOR = 11, - TM_FUNNEL = 12, - UDP_BRIDGE = 15, UDP_POLLING_TASK = 16, diff --git a/tests/user/testcfg/pollingsequence/PollingSequenceFactory.cpp b/tests/src/fsfw_tests/unit/testcfg/pollingsequence/PollingSequenceFactory.cpp similarity index 97% rename from tests/user/testcfg/pollingsequence/PollingSequenceFactory.cpp rename to tests/src/fsfw_tests/unit/testcfg/pollingsequence/PollingSequenceFactory.cpp index e3ee874a..1d29ef86 100644 --- a/tests/user/testcfg/pollingsequence/PollingSequenceFactory.cpp +++ b/tests/src/fsfw_tests/unit/testcfg/pollingsequence/PollingSequenceFactory.cpp @@ -1,6 +1,6 @@ #include "PollingSequenceFactory.h" -#include +#include "tests/TestsConfig.h" #include #include diff --git a/tests/user/testcfg/pollingsequence/PollingSequenceFactory.h b/tests/src/fsfw_tests/unit/testcfg/pollingsequence/PollingSequenceFactory.h similarity index 100% rename from tests/user/testcfg/pollingsequence/PollingSequenceFactory.h rename to tests/src/fsfw_tests/unit/testcfg/pollingsequence/PollingSequenceFactory.h diff --git a/tests/user/testcfg/returnvalues/classIds.h b/tests/src/fsfw_tests/unit/testcfg/returnvalues/classIds.h similarity index 87% rename from tests/user/testcfg/returnvalues/classIds.h rename to tests/src/fsfw_tests/unit/testcfg/returnvalues/classIds.h index 606cc60b..1001d012 100644 --- a/tests/user/testcfg/returnvalues/classIds.h +++ b/tests/src/fsfw_tests/unit/testcfg/returnvalues/classIds.h @@ -1,7 +1,7 @@ #ifndef CONFIG_RETURNVALUES_CLASSIDS_H_ #define CONFIG_RETURNVALUES_CLASSIDS_H_ -#include +#include "fsfw/returnvalues/FwClassIds.h" /** * @brief CLASS_ID defintions which are required for custom returnvalues. diff --git a/tests/user/testcfg/tmtc/apid.h b/tests/src/fsfw_tests/unit/testcfg/tmtc/apid.h similarity index 100% rename from tests/user/testcfg/tmtc/apid.h rename to tests/src/fsfw_tests/unit/testcfg/tmtc/apid.h diff --git a/tests/user/testcfg/tmtc/pusIds.h b/tests/src/fsfw_tests/unit/testcfg/tmtc/pusIds.h similarity index 100% rename from tests/user/testcfg/tmtc/pusIds.h rename to tests/src/fsfw_tests/unit/testcfg/tmtc/pusIds.h diff --git a/tests/user/testtemplate/TestTemplate.cpp b/tests/src/fsfw_tests/unit/testtemplate/TestTemplate.cpp similarity index 90% rename from tests/user/testtemplate/TestTemplate.cpp rename to tests/src/fsfw_tests/unit/testtemplate/TestTemplate.cpp index 6b5fc3d2..a779d80c 100644 --- a/tests/user/testtemplate/TestTemplate.cpp +++ b/tests/src/fsfw_tests/unit/testtemplate/TestTemplate.cpp @@ -1,6 +1,5 @@ -#include -#include - +#include "fsfw_tests/unit/CatchDefinitions.h" +#include /** * @brief Template test file diff --git a/tests/src/fsfw_tests/unit/timemanager/CMakeLists.txt b/tests/src/fsfw_tests/unit/timemanager/CMakeLists.txt index 2c635711..6ce1c6c6 100644 --- a/tests/src/fsfw_tests/unit/timemanager/CMakeLists.txt +++ b/tests/src/fsfw_tests/unit/timemanager/CMakeLists.txt @@ -1,3 +1,3 @@ -target_sources(${TARGET_NAME} PRIVATE +target_sources(${FSFW_TEST_TGT} PRIVATE TestCountdown.cpp ) diff --git a/tests/src/fsfw_tests/unit/tmtcpacket/CMakeLists.txt b/tests/src/fsfw_tests/unit/tmtcpacket/CMakeLists.txt index a1a4c1b6..36838b24 100644 --- a/tests/src/fsfw_tests/unit/tmtcpacket/CMakeLists.txt +++ b/tests/src/fsfw_tests/unit/tmtcpacket/CMakeLists.txt @@ -1,3 +1,3 @@ -target_sources(${TARGET_NAME} PRIVATE +target_sources(${FSFW_TEST_TGT} PRIVATE PusTmTest.cpp ) diff --git a/tests/user/CMakeLists.txt b/tests/user/CMakeLists.txt deleted file mode 100644 index 2e1fdee3..00000000 --- a/tests/user/CMakeLists.txt +++ /dev/null @@ -1,261 +0,0 @@ -################################################################################ -# CMake support for the Flight Software Framework Tests -# Author: R. Mueller -################################################################################ - -################################################################################ -# Pre-Project preparation -################################################################################ -cmake_minimum_required(VERSION 3.13) - -# set(CMAKE_VERBOSE TRUE) -# set(CODE_COVERAGE_VERBOSE TRUE) - -set(CMAKE_SCRIPT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") - -option(TMTC_TEST "Build binary for manual or automatic TMTC tests" FALSE) -option(GENERATE_COVERAGE - "Specify whether coverage data is generated with GCOV" - TRUE -) - -set(FSFW_ADD_UNITTESTS ON) - -if(TMTC_TEST) - set(LINK_CATCH2 FALSE) -else() - set(LINK_CATCH2 TRUE) -endif() - -# Tests can be built with the Host OSAL or with the Linux OSAL. -if(NOT FSFW_OSAL) - set(FSFW_OSAL host CACHE STRING "OS for the FSFW.") -endif() - -option(FSFW_CUSTOM_UNITTEST_RUNNER - "Specify whether custom main or Catch2 main is used" TRUE -) - -# Project Name -project(fsfw-tests C CXX) - -################################################################################ -# Pre-Sources preparation -################################################################################ - -# Specify the C++ standard -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED True) - -# Set names and variables -set(TARGET_NAME ${CMAKE_PROJECT_NAME}) -if(FSFW_CUSTOM_UNITTEST_RUNNER) - set(CATCH2_TARGET Catch2) -else() - set(CATCH2_TARGET Catch2WithMain) -endif() -set(LIB_FSFW_NAME fsfw) - -# Set path names -set(FSFW_PATH fsfw) -set(CATCH2_PATH Catch2) -set(FSFW_TESTS_PATH fsfw/unittest) -set(TEST_SETUP_PATH unittest) -set(TMTC_TEST_PATH tests) - -# Analyse different OS and architecture/target options and -# determine BSP_PATH - -# FreeRTOS -if(FSFW_OSAL STREQUAL linux) - add_definitions(-DUNIX -DLINUX) - find_package(Threads REQUIRED) -# Hosted -else() - if(WIN32) - add_definitions(-DWIN32) - elseif(UNIX) - find_package(Threads REQUIRED) - add_definitions(-DUNIX -DLINUX) - endif() -endif() - -if(GENERATE_COVERAGE) - list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/cmake-modules) - if(CMAKE_COMPILER_IS_GNUCXX) - include(CodeCoverage) - # Add compile options on target base, we don't want coverage for Catch2 - # append_coverage_compiler_flags() - endif() -endif() - -set(FSFW_CONFIG_PATH testcfg) -set(FSFW_ADDITIONAL_INC_PATHS ${CMAKE_CURRENT_BINARY_DIR}) - -configure_file(${FSFW_CONFIG_PATH}/FSFWConfig.h.in FSFWConfig.h) -configure_file(${FSFW_CONFIG_PATH}/OBSWConfig.h.in OBSWConfig.h) -configure_file(${FSFW_CONFIG_PATH}/TestsConfig.h.in TestsConfig.h) - -################################################################################ -# Executable and Sources -################################################################################ - -# Add executable -add_executable(${TARGET_NAME}) - -# Add subdirectories -add_subdirectory(${FSFW_PATH}) -add_subdirectory(${FSFW_CONFIG_PATH}) - -if(LINK_CATCH2) - add_subdirectory(${CATCH2_PATH}) - add_subdirectory(${TEST_SETUP_PATH}) -else() - target_compile_definitions(${TARGET_NAME} PRIVATE - FSFW_DISABLE_PRINTOUT=0 - ) - target_compile_definitions(${LIB_FSFW_NAME} PRIVATE - FSFW_DISABLE_PRINTOUT=0 - ) - add_subdirectory(${TMTC_TEST_PATH}) - add_subdirectory(${FSFW_TESTS_PATH}) -endif() - - -################################################################################ -# Post-Sources preparation -################################################################################ - -# Add libraries for all sources. -target_link_libraries(${TARGET_NAME} PRIVATE - ${LIB_FSFW_NAME} -) - -if(LINK_CATCH2) - target_link_libraries(${TARGET_NAME} PRIVATE - ${CATCH2_TARGET} - ) -endif() - -if(GENERATE_COVERAGE) - if(CMAKE_COMPILER_IS_GNUCXX) - # set(CODE_COVERAGE_VERBOSE TRUE) - include(CodeCoverage) - - # Remove quotes. - separate_arguments(COVERAGE_COMPILER_FLAGS - NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}" - ) - - # Add compile options manually, we don't want coverage for Catch2 - target_compile_options(${TARGET_NAME} PRIVATE - "${COVERAGE_COMPILER_FLAGS}" - ) - target_compile_options(${LIB_FSFW_NAME} PRIVATE - "${COVERAGE_COMPILER_FLAGS}" - ) - - # Exclude internal unittest from coverage for now. - if(WIN32) - set(GCOVR_ADDITIONAL_ARGS - "--exclude-throw-branches" - "--exclude-unreachable-branches" - ) - set(COVERAGE_EXCLUDES - "/c/msys64/mingw64/*" "Catch2" - "${CMAKE_CURRENT_SOURCE_DIR}/fsfw/unittest/internal" - ) - elseif(UNIX) - set(COVERAGE_EXCLUDES - "/usr/include/*" "/usr/bin/*" "Catch2/*" - "fsfw/unittest/internal/*" - ) - endif() - - target_link_options(${TARGET_NAME} PRIVATE - -fprofile-arcs - -ftest-coverage - ) - target_link_options(${LIB_FSFW_NAME} PRIVATE - -fprofile-arcs - -ftest-coverage - ) - - if(WIN32) - setup_target_for_coverage_gcovr_html( - NAME ${TARGET_NAME}_coverage - EXECUTABLE ${TARGET_NAME} - DEPENDENCIES ${TARGET_NAME} - ) - else() - setup_target_for_coverage_lcov( - NAME ${TARGET_NAME}_coverage - EXECUTABLE ${TARGET_NAME} - DEPENDENCIES ${TARGET_NAME} - ) - endif() - endif() -endif() - -# Add include paths for all sources. -target_include_directories(${TARGET_NAME} PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR} - ${FSFW_CONFIG_PATH} - ${CMAKE_CURRENT_BINARY_DIR} -) - -if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(WARNING_FLAGS - -Wall - -Wextra - -Wshadow=local - -Wimplicit-fallthrough=1 - -Wno-unused-parameter - -Wno-psabi - ) - - # Remove unused sections. - target_compile_options(${TARGET_NAME} PRIVATE - "-ffunction-sections" - "-fdata-sections" - ) - - # Removed unused sections. - target_link_options(${TARGET_NAME} PRIVATE - "-Wl,--gc-sections" - ) - -elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - set(COMPILER_FLAGS "/permissive-") -endif() - -if(CMAKE_VERBOSE) - message(STATUS "Warning flags: ${WARNING_FLAGS}") -endif() - -# Compile options for all sources. -target_compile_options(${TARGET_NAME} PRIVATE - ${WARNING_FLAGS} -) - -if(NOT CMAKE_SIZE) - set(CMAKE_SIZE size) - if(WIN32) - set(FILE_SUFFIX ".exe") - endif() -endif() - -string(CONCAT POST_BUILD_COMMENT - "Build directory: ${CMAKE_BINARY_DIR}\n" - "Target OSAL: ${FSFW_OSAL}\n" - "Target Build Type: ${CMAKE_BUILD_TYPE}" -) - -add_custom_command(TARGET ${TARGET_NAME} - POST_BUILD - COMMAND ${CMAKE_SIZE} ${TARGET_NAME}${FILE_SUFFIX} - COMMENT ${POST_BUILD_COMMENT} -) - -include (${CMAKE_SCRIPT_PATH}/BuildType.cmake) -set_build_type() diff --git a/tests/user/README.md b/tests/user/README.md deleted file mode 100644 index d6a4bb85..00000000 --- a/tests/user/README.md +++ /dev/null @@ -1,19 +0,0 @@ -## FSFW Testing - -This folder contains testing and unit testing components. - -### Instructions - -The easiest way to run the unittest contained in this folder is to follow -the steps in the [test repository](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_tests). -This is recommended even if the goal is to set up a custom test repository to have -a starting point. - -To set up a custom test repository or project, following steps can be performed: - -1. Copy the user folder content into the project root. -2. Clone [Catch2](https://github.com/catchorg/Catch2) in the project root. -3. Use the `CMakeLists.txt` as a starting point to add tests and build the test - executable. - - diff --git a/tests/user/lcov.sh b/tests/user/lcov.sh deleted file mode 100644 index 4db16e5f..00000000 --- a/tests/user/lcov.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -lcov --capture --directory . --output-file coverage.info -genhtml coverage.info --output-directory _coverage diff --git a/tests/user/testcfg/CMakeLists.txt b/tests/user/testcfg/CMakeLists.txt deleted file mode 100644 index 46808ec4..00000000 --- a/tests/user/testcfg/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -target_sources(${TARGET_NAME} PRIVATE - ipc/MissionMessageTypes.cpp - pollingsequence/PollingSequenceFactory.cpp -) - -# Add include paths for the executable -target_include_directories(${TARGET_NAME} PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR} -) \ No newline at end of file diff --git a/tests/user/testcfg/OBSWConfig.h.in b/tests/user/testcfg/OBSWConfig.h.in deleted file mode 100644 index 34eda31f..00000000 --- a/tests/user/testcfg/OBSWConfig.h.in +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef TESTCFG_OBSWCONFIG_H_ -#define TESTCFG_OBSWCONFIG_H_ - - - - - -#endif /* TESTCFG_OBSWCONFIG_H_ */