Merge branch 'development' into mueller/arrayprinter-improvements

This commit is contained in:
Robin Müller 2021-10-22 11:31:57 +02:00
commit f16dcebf6b
70 changed files with 1212 additions and 605 deletions

2
.gitignore vendored
View File

@ -2,3 +2,5 @@
.project .project
.settings .settings
.metadata .metadata
/build*

View File

@ -1,5 +1,9 @@
cmake_minimum_required(VERSION 3.13) cmake_minimum_required(VERSION 3.13)
set(FSFW_VERSION 2)
set(FSFW_SUBVERSION 0)
set(FSFW_REVISION 0)
option(FSFW_GENERATE_SECTIONS option(FSFW_GENERATE_SECTIONS
"Generate function and data sections. Required to remove unused code" ON "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) option(FSFW_REMOVE_UNUSED_CODE "Remove unused code" ON)
endif() 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) option(FSFW_WARNING_SHADOW_LOCAL_GCC "Enable -Wshadow=local warning in GCC" ON)
# Options to exclude parts of the FSFW from compilation. # Options to exclude parts of the FSFW from compilation.
option(FSFW_ADD_INTERNAL_TESTS "Add internal unit tests" ON) option(FSFW_ADD_INTERNAL_TESTS "Add internal unit tests" ON)
@ -26,11 +35,72 @@ option(FSFW_ADD_TMSTORAGE "Compile with tm storage components" OFF)
option(FSFW_ADD_SGP4_PROPAGATOR "Add SGP4 propagator code" OFF) option(FSFW_ADD_SGP4_PROPAGATOR "Add SGP4 propagator code" OFF)
set(LIB_FSFW_NAME fsfw) set(LIB_FSFW_NAME fsfw)
set(FSFW_TEST_TGT fsfw-tests)
add_library(${LIB_FSFW_NAME}) 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(FSFW_CORE_INC_PATH "inc")
set_property(CACHE FSFW_OSAL PROPERTY STRINGS host linux rtems freertos) 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(FSFW_BUILD_UNITTESTS)
configure_file(src/fsfw/FSFW.h.in fsfw/FSFW.h)
configure_file(src/fsfw/FSFWVersion.h.in fsfw/FSFWVersion.h)
else()
configure_file(src/fsfw/FSFW.h.in FSFW.h)
configure_file(src/fsfw/FSFWVersion.h.in FSFWVersion.h)
endif()
if(NOT CMAKE_CXX_STANDARD) if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_STANDARD_REQUIRED True)
@ -63,29 +133,29 @@ endif()
set(FSFW_OSAL_DEFINITION FSFW_OSAL_HOST) set(FSFW_OSAL_DEFINITION FSFW_OSAL_HOST)
if(FSFW_OSAL MATCHES host) if(FSFW_OSAL MATCHES host)
set(OS_FSFW_NAME "Host") set(FSFW_OS_NAME "Host")
set(FSFW_OSAL_HOST ON) set(FSFW_OSAL_HOST ON)
elseif(FSFW_OSAL MATCHES linux) elseif(FSFW_OSAL MATCHES linux)
set(OS_FSFW_NAME "Linux") set(FSFW_OS_NAME "Linux")
set(FSFW_OSAL_LINUX ON) set(FSFW_OSAL_LINUX ON)
elseif(FSFW_OSAL MATCHES freertos) elseif(FSFW_OSAL MATCHES freertos)
set(OS_FSFW_NAME "FreeRTOS") set(FSFW_OS_NAME "FreeRTOS")
set(FSFW_OSAL_FREERTOS ON) set(FSFW_OSAL_FREERTOS ON)
target_link_libraries(${LIB_FSFW_NAME} PRIVATE target_link_libraries(${LIB_FSFW_NAME} PRIVATE
${LIB_OS_NAME} ${LIB_OS_NAME}
) )
elseif(FSFW_OSAL STREQUAL rtems) elseif(FSFW_OSAL STREQUAL rtems)
set(OS_FSFW_NAME "RTEMS") set(FSFW_OS_NAME "RTEMS")
set(FSFW_OSAL_RTEMS ON) set(FSFW_OSAL_RTEMS ON)
else() else()
message(WARNING message(WARNING
"Invalid operating system for FSFW specified! Setting to host.." "Invalid operating system for FSFW specified! Setting to host.."
) )
set(OS_FSFW_NAME "Host") set(FSFW_OS_NAME "Host")
set(OS_FSFW "host") set(OS_FSFW "host")
endif() endif()
message(STATUS "Compiling FSFW for the ${OS_FSFW_NAME} operating system.") message(STATUS "Compiling FSFW for the ${FSFW_OS_NAME} operating system.")
add_subdirectory(src) add_subdirectory(src)
add_subdirectory(tests) add_subdirectory(tests)
@ -94,12 +164,81 @@ if(FSFW_ADD_HAL)
endif() endif()
add_subdirectory(contrib) 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. # 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 this is not given, we include the default configuration and emit a warning.
if(NOT FSFW_CONFIG_PATH) if(NOT FSFW_CONFIG_PATH)
message(WARNING "Flight Software Framework configuration path not set!") message(WARNING "Flight Software Framework configuration path not set!")
message(WARNING "Setting default configuration!") set(DEF_CONF_PATH misc/defaultcfg/fsfwconfig)
add_subdirectory(defaultcfg/fsfwconfig) message(WARNING "Setting default configuration from ${DEF_CONF_PATH} ..")
add_subdirectory(${DEF_CONF_PATH})
set(FSFW_CONFIG_PATH ${DEF_CONF_PATH})
endif() endif()
# FSFW might be part of a possibly complicated folder structure, so we # FSFW might be part of a possibly complicated folder structure, so we
@ -187,3 +326,16 @@ target_compile_options(${LIB_FSFW_NAME} PRIVATE
target_link_libraries(${LIB_FSFW_NAME} PRIVATE target_link_libraries(${LIB_FSFW_NAME} PRIVATE
${FSFW_ADDITIONAL_LINK_LIBS} ${FSFW_ADDITIONAL_LINK_LIBS}
) )
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}
)

View File

@ -22,17 +22,90 @@ Currently, the FSFW provides the following OSALs:
- FreeRTOS - FreeRTOS
- RTEMS - 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 ## 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 Generally, the FSFW is included in a project by 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. 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 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(<YourProjectName> 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 ## Index

View File

@ -9,12 +9,13 @@ using gpioId_t = uint16_t;
namespace gpio { namespace gpio {
enum Levels { enum Levels: uint8_t {
LOW = 0, LOW = 0,
HIGH = 1 HIGH = 1,
NONE = 99
}; };
enum Direction { enum Direction: uint8_t {
IN = 0, IN = 0,
OUT = 1 OUT = 1
}; };
@ -24,16 +25,18 @@ enum GpioOperation {
WRITE WRITE
}; };
enum GpioTypes { enum class GpioTypes {
NONE, NONE,
GPIO_REGULAR_BY_CHIP, GPIO_REGULAR_BY_CHIP,
GPIO_REGULAR_BY_LABEL, GPIO_REGULAR_BY_LABEL,
GPIO_REGULAR_BY_LINE_NAME,
CALLBACK CALLBACK
}; };
static constexpr gpioId_t NO_GPIO = -1; static constexpr gpioId_t NO_GPIO = -1;
using gpio_cb_t = void (*) (gpioId_t gpioId, gpio::GpioOperation gpioOp, int value, void* args); using gpio_cb_t = void (*) (gpioId_t gpioId, gpio::GpioOperation gpioOp, gpio::Levels value,
void* args);
} }
@ -57,7 +60,7 @@ public:
GpioBase() = default; GpioBase() = default;
GpioBase(gpio::GpioTypes gpioType, std::string consumer, gpio::Direction direction, GpioBase(gpio::GpioTypes gpioType, std::string consumer, gpio::Direction direction,
int initValue): gpio::Levels initValue):
gpioType(gpioType), consumer(consumer),direction(direction), initValue(initValue) {} gpioType(gpioType), consumer(consumer),direction(direction), initValue(initValue) {}
virtual~ GpioBase() {}; virtual~ GpioBase() {};
@ -66,15 +69,21 @@ public:
gpio::GpioTypes gpioType = gpio::GpioTypes::NONE; gpio::GpioTypes gpioType = gpio::GpioTypes::NONE;
std::string consumer; std::string consumer;
gpio::Direction direction = gpio::Direction::IN; gpio::Direction direction = gpio::Direction::IN;
int initValue = 0; gpio::Levels initValue = gpio::Levels::NONE;
}; };
class GpiodRegularBase: public GpioBase { class GpiodRegularBase: public GpioBase {
public: public:
GpiodRegularBase(gpio::GpioTypes gpioType, std::string consumer, gpio::Direction direction, GpiodRegularBase(gpio::GpioTypes gpioType, std::string consumer, gpio::Direction direction,
int initValue, int lineNum): GpioBase(gpioType, consumer, direction, initValue), gpio::Levels initValue, int lineNum):
lineNum(lineNum) { GpioBase(gpioType, consumer, direction, initValue), lineNum(lineNum) {
} }
// line number will be configured at a later point for the open by line name configuration
GpiodRegularBase(gpio::GpioTypes gpioType, std::string consumer, gpio::Direction direction,
gpio::Levels initValue): GpioBase(gpioType, consumer, direction, initValue) {
}
int lineNum = 0; int lineNum = 0;
struct gpiod_line* lineHandle = nullptr; struct gpiod_line* lineHandle = nullptr;
}; };
@ -87,7 +96,7 @@ public:
} }
GpiodRegularByChip(std::string chipname_, int lineNum_, std::string consumer_, GpiodRegularByChip(std::string chipname_, int lineNum_, std::string consumer_,
gpio::Direction direction_, int initValue_) : gpio::Direction direction_, gpio::Levels initValue_) :
GpiodRegularBase(gpio::GpioTypes::GPIO_REGULAR_BY_CHIP, GpiodRegularBase(gpio::GpioTypes::GPIO_REGULAR_BY_CHIP,
consumer_, direction_, initValue_, lineNum_), consumer_, direction_, initValue_, lineNum_),
chipname(chipname_){ chipname(chipname_){
@ -105,7 +114,7 @@ public:
class GpiodRegularByLabel: public GpiodRegularBase { class GpiodRegularByLabel: public GpiodRegularBase {
public: public:
GpiodRegularByLabel(std::string label_, int lineNum_, std::string consumer_, GpiodRegularByLabel(std::string label_, int lineNum_, std::string consumer_,
gpio::Direction direction_, int initValue_) : gpio::Direction direction_, gpio::Levels initValue_) :
GpiodRegularBase(gpio::GpioTypes::GPIO_REGULAR_BY_LABEL, consumer_, GpiodRegularBase(gpio::GpioTypes::GPIO_REGULAR_BY_LABEL, consumer_,
direction_, initValue_, lineNum_), direction_, initValue_, lineNum_),
label(label_) { label(label_) {
@ -120,9 +129,30 @@ public:
std::string label; std::string label;
}; };
/**
* @brief Passing this GPIO configuration to the GPIO IF object will try to open the GPIO by its
* line name. This line name can be set in the device tree and must be unique. Otherwise
* the driver will open the first line with the given name.
*/
class GpiodRegularByLineName: public GpiodRegularBase {
public:
GpiodRegularByLineName(std::string lineName_, std::string consumer_, gpio::Direction direction_,
gpio::Levels initValue_) :
GpiodRegularBase(gpio::GpioTypes::GPIO_REGULAR_BY_LINE_NAME, consumer_, direction_,
initValue_), lineName(lineName_) {
}
GpiodRegularByLineName(std::string lineName_, std::string consumer_) :
GpiodRegularBase(gpio::GpioTypes::GPIO_REGULAR_BY_LINE_NAME, consumer_,
gpio::Direction::IN, gpio::LOW), lineName(lineName_) {
}
std::string lineName;
};
class GpioCallback: public GpioBase { class GpioCallback: public GpioBase {
public: public:
GpioCallback(std::string consumer, gpio::Direction direction_, int initValue_, GpioCallback(std::string consumer, gpio::Direction direction_, gpio::Levels initValue_,
gpio::gpio_cb_t callback, void* callbackArgs): gpio::gpio_cb_t callback, void* callbackArgs):
GpioBase(gpio::GpioTypes::CALLBACK, consumer, direction_, initValue_), GpioBase(gpio::GpioTypes::CALLBACK, consumer, direction_, initValue_),
callback(callback), callbackArgs(callbackArgs) {} callback(callback), callbackArgs(callbackArgs) {}

View File

@ -73,7 +73,7 @@ ReturnValue_t MgmLIS3MDLHandler::buildTransitionDeviceCommand(
switch (internalState) { switch (internalState) {
case(InternalState::STATE_NONE): case(InternalState::STATE_NONE):
case(InternalState::STATE_NORMAL): { case(InternalState::STATE_NORMAL): {
return HasReturnvaluesIF::RETURN_OK; return DeviceHandlerBase::NOTHING_TO_SEND;
} }
case(InternalState::STATE_FIRST_CONTACT): { case(InternalState::STATE_FIRST_CONTACT): {
*id = MGMLIS3MDL::IDENTIFY_DEVICE; *id = MGMLIS3MDL::IDENTIFY_DEVICE;

View File

@ -1,8 +1,9 @@
#include "fsfw_hal/linux/gpio/LinuxLibgpioIF.h" #include "LinuxLibgpioIF.h"
#include "fsfw_hal/common/gpio/gpioDefinitions.h" #include "fsfw_hal/common/gpio/gpioDefinitions.h"
#include "fsfw_hal/common/gpio/GpioCookie.h" #include "fsfw_hal/common/gpio/GpioCookie.h"
#include <fsfw/serviceinterface/ServiceInterface.h> #include "fsfw/serviceinterface/ServiceInterface.h"
#include <utility> #include <utility>
#include <unistd.h> #include <unistd.h>
@ -66,6 +67,14 @@ ReturnValue_t LinuxLibgpioIF::configureGpios(GpioMap& mapToAdd) {
configureGpioByLabel(gpioConfig.first, *regularGpio); configureGpioByLabel(gpioConfig.first, *regularGpio);
break; break;
} }
case(gpio::GpioTypes::GPIO_REGULAR_BY_LINE_NAME):{
auto regularGpio = dynamic_cast<GpiodRegularByLineName*>(gpioConfig.second);
if(regularGpio == nullptr) {
return GPIO_INVALID_INSTANCE;
}
configureGpioByLineName(gpioConfig.first, *regularGpio);
break;
}
case(gpio::GpioTypes::CALLBACK): { case(gpio::GpioTypes::CALLBACK): {
auto gpioCallback = dynamic_cast<GpioCallback*>(gpioConfig.second); auto gpioCallback = dynamic_cast<GpioCallback*>(gpioConfig.second);
if(gpioCallback->callback == nullptr) { if(gpioCallback->callback == nullptr) {
@ -84,13 +93,13 @@ ReturnValue_t LinuxLibgpioIF::configureGpioByLabel(gpioId_t gpioId,
std::string& label = gpioByLabel.label; std::string& label = gpioByLabel.label;
struct gpiod_chip* chip = gpiod_chip_open_by_label(label.c_str()); struct gpiod_chip* chip = gpiod_chip_open_by_label(label.c_str());
if (chip == nullptr) { if (chip == nullptr) {
sif::warning << "LinuxLibgpioIF::configureRegularGpio: Failed to open gpio from gpio " sif::warning << "LinuxLibgpioIF::configureGpioByLabel: Failed to open gpio from gpio "
<< "group with label " << label << ". Gpio ID: " << gpioId << std::endl; << "group with label " << label << ". Gpio ID: " << gpioId << std::endl;
return RETURN_FAILED; return RETURN_FAILED;
} }
std::string failOutput = "label: " + label; std::string failOutput = "label: " + label;
return configureRegularGpio(gpioId, gpioByLabel.gpioType, chip, gpioByLabel, failOutput); return configureRegularGpio(gpioId, chip, gpioByLabel, failOutput);
} }
ReturnValue_t LinuxLibgpioIF::configureGpioByChip(gpioId_t gpioId, ReturnValue_t LinuxLibgpioIF::configureGpioByChip(gpioId_t gpioId,
@ -98,16 +107,41 @@ ReturnValue_t LinuxLibgpioIF::configureGpioByChip(gpioId_t gpioId,
std::string& chipname = gpioByChip.chipname; std::string& chipname = gpioByChip.chipname;
struct gpiod_chip* chip = gpiod_chip_open_by_name(chipname.c_str()); struct gpiod_chip* chip = gpiod_chip_open_by_name(chipname.c_str());
if (chip == nullptr) { if (chip == nullptr) {
sif::warning << "LinuxLibgpioIF::configureRegularGpio: Failed to open chip " sif::warning << "LinuxLibgpioIF::configureGpioByChip: Failed to open chip "
<< chipname << ". Gpio ID: " << gpioId << std::endl; << chipname << ". Gpio ID: " << gpioId << std::endl;
return RETURN_FAILED; return RETURN_FAILED;
} }
std::string failOutput = "chipname: " + chipname; std::string failOutput = "chipname: " + chipname;
return configureRegularGpio(gpioId, gpioByChip.gpioType, chip, gpioByChip, failOutput); return configureRegularGpio(gpioId, chip, gpioByChip, failOutput);
} }
ReturnValue_t LinuxLibgpioIF::configureRegularGpio(gpioId_t gpioId, gpio::GpioTypes gpioType, ReturnValue_t LinuxLibgpioIF::configureGpioByLineName(gpioId_t gpioId,
struct gpiod_chip* chip, GpiodRegularBase& regularGpio, std::string failOutput) { GpiodRegularByLineName &gpioByLineName) {
std::string& lineName = gpioByLineName.lineName;
char chipname[MAX_CHIPNAME_LENGTH];
unsigned int lineOffset;
int result = gpiod_ctxless_find_line(lineName.c_str(), chipname, MAX_CHIPNAME_LENGTH,
&lineOffset);
if (result != LINE_FOUND) {
parseFindeLineResult(result, lineName);
return RETURN_FAILED;
}
gpioByLineName.lineNum = static_cast<int>(lineOffset);
struct gpiod_chip* chip = gpiod_chip_open_by_name(chipname);
if (chip == nullptr) {
sif::warning << "LinuxLibgpioIF::configureGpioByLineName: Failed to open chip "
<< chipname << ". <Gpio ID: " << gpioId << std::endl;
return RETURN_FAILED;
}
std::string failOutput = "line name: " + lineName;
return configureRegularGpio(gpioId, chip, gpioByLineName, failOutput);
}
ReturnValue_t LinuxLibgpioIF::configureRegularGpio(gpioId_t gpioId, struct gpiod_chip* chip,
GpiodRegularBase& regularGpio, std::string failOutput) {
unsigned int lineNum; unsigned int lineNum;
gpio::Direction direction; gpio::Direction direction;
std::string consumer; std::string consumer;
@ -132,22 +166,10 @@ ReturnValue_t LinuxLibgpioIF::configureRegularGpio(gpioId_t gpioId, gpio::GpioTy
case(gpio::OUT): { case(gpio::OUT): {
result = gpiod_line_request_output(lineHandle, consumer.c_str(), result = gpiod_line_request_output(lineHandle, consumer.c_str(),
regularGpio.initValue); regularGpio.initValue);
if (result < 0) {
sif::error << "LinuxLibgpioIF::configureRegularGpio: Failed to request line " << lineNum <<
" from GPIO instance with ID: " << gpioId << std::endl;
gpiod_line_release(lineHandle);
return RETURN_FAILED;
}
break; break;
} }
case(gpio::IN): { case(gpio::IN): {
result = gpiod_line_request_input(lineHandle, consumer.c_str()); result = gpiod_line_request_input(lineHandle, consumer.c_str());
if (result < 0) {
sif::error << "LinuxLibgpioIF::configureGpios: Failed to request line "
<< lineNum << " from GPIO instance with ID: " << gpioId << std::endl;
gpiod_line_release(lineHandle);
return RETURN_FAILED;
}
break; break;
} }
default: { default: {
@ -156,6 +178,18 @@ ReturnValue_t LinuxLibgpioIF::configureRegularGpio(gpioId_t gpioId, gpio::GpioTy
return GPIO_INVALID_INSTANCE; return GPIO_INVALID_INSTANCE;
} }
if (result < 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "LinuxLibgpioIF::configureRegularGpio: Failed to request line " <<
lineNum << " from GPIO instance with ID: " << gpioId << std::endl;
#else
sif::printError("LinuxLibgpioIF::configureRegularGpio: "
"Failed to request line %d from GPIO instance with ID: %d\n", lineNum, gpioId);
#endif
gpiod_line_release(lineHandle);
return RETURN_FAILED;
}
} }
/** /**
* Write line handle to GPIO configuration instance so it can later be used to set or * Write line handle to GPIO configuration instance so it can later be used to set or
@ -173,8 +207,9 @@ ReturnValue_t LinuxLibgpioIF::pullHigh(gpioId_t gpioId) {
} }
auto gpioType = gpioMapIter->second->gpioType; auto gpioType = gpioMapIter->second->gpioType;
if(gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_CHIP or if (gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_CHIP
gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_LABEL) { or gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_LABEL
or gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_LINE_NAME) {
auto regularGpio = dynamic_cast<GpiodRegularBase*>(gpioMapIter->second); auto regularGpio = dynamic_cast<GpiodRegularBase*>(gpioMapIter->second);
if(regularGpio == nullptr) { if(regularGpio == nullptr) {
return GPIO_TYPE_FAILURE; return GPIO_TYPE_FAILURE;
@ -187,7 +222,7 @@ ReturnValue_t LinuxLibgpioIF::pullHigh(gpioId_t gpioId) {
return GPIO_INVALID_INSTANCE; return GPIO_INVALID_INSTANCE;
} }
gpioCallback->callback(gpioMapIter->first, gpio::GpioOperation::WRITE, gpioCallback->callback(gpioMapIter->first, gpio::GpioOperation::WRITE,
1, gpioCallback->callbackArgs); gpio::Levels::HIGH, gpioCallback->callbackArgs);
return RETURN_OK; return RETURN_OK;
} }
return GPIO_TYPE_FAILURE; return GPIO_TYPE_FAILURE;
@ -196,13 +231,18 @@ ReturnValue_t LinuxLibgpioIF::pullHigh(gpioId_t gpioId) {
ReturnValue_t LinuxLibgpioIF::pullLow(gpioId_t gpioId) { ReturnValue_t LinuxLibgpioIF::pullLow(gpioId_t gpioId) {
gpioMapIter = gpioMap.find(gpioId); gpioMapIter = gpioMap.find(gpioId);
if (gpioMapIter == gpioMap.end()) { if (gpioMapIter == gpioMap.end()) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LinuxLibgpioIF::pullLow: Unknown GPIO ID " << gpioId << std::endl; sif::warning << "LinuxLibgpioIF::pullLow: Unknown GPIO ID " << gpioId << std::endl;
#else
sif::printWarning("LinuxLibgpioIF::pullLow: Unknown GPIO ID %d\n", gpioId);
#endif
return UNKNOWN_GPIO_ID; return UNKNOWN_GPIO_ID;
} }
auto& gpioType = gpioMapIter->second->gpioType; auto& gpioType = gpioMapIter->second->gpioType;
if(gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_CHIP or if (gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_CHIP
gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_LABEL) { or gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_LABEL
or gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_LINE_NAME) {
auto regularGpio = dynamic_cast<GpiodRegularBase*>(gpioMapIter->second); auto regularGpio = dynamic_cast<GpiodRegularBase*>(gpioMapIter->second);
if(regularGpio == nullptr) { if(regularGpio == nullptr) {
return GPIO_TYPE_FAILURE; return GPIO_TYPE_FAILURE;
@ -215,7 +255,7 @@ ReturnValue_t LinuxLibgpioIF::pullLow(gpioId_t gpioId) {
return GPIO_INVALID_INSTANCE; return GPIO_INVALID_INSTANCE;
} }
gpioCallback->callback(gpioMapIter->first, gpio::GpioOperation::WRITE, gpioCallback->callback(gpioMapIter->first, gpio::GpioOperation::WRITE,
0, gpioCallback->callbackArgs); gpio::Levels::LOW, gpioCallback->callbackArgs);
return RETURN_OK; return RETURN_OK;
} }
return GPIO_TYPE_FAILURE; return GPIO_TYPE_FAILURE;
@ -225,8 +265,13 @@ ReturnValue_t LinuxLibgpioIF::driveGpio(gpioId_t gpioId,
GpiodRegularBase& regularGpio, gpio::Levels logicLevel) { GpiodRegularBase& regularGpio, gpio::Levels logicLevel) {
int result = gpiod_line_set_value(regularGpio.lineHandle, logicLevel); int result = gpiod_line_set_value(regularGpio.lineHandle, logicLevel);
if (result < 0) { if (result < 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LinuxLibgpioIF::driveGpio: Failed to pull GPIO with ID " << gpioId << sif::warning << "LinuxLibgpioIF::driveGpio: Failed to pull GPIO with ID " << gpioId <<
" to logic level " << logicLevel << std::endl; " to logic level " << logicLevel << std::endl;
#else
sif::printWarning("LinuxLibgpioIF::driveGpio: Failed to pull GPIO with ID %d to "
"logic level %d\n", gpioId, logicLevel);
#endif
return DRIVE_GPIO_FAILURE; return DRIVE_GPIO_FAILURE;
} }
@ -236,12 +281,18 @@ ReturnValue_t LinuxLibgpioIF::driveGpio(gpioId_t gpioId,
ReturnValue_t LinuxLibgpioIF::readGpio(gpioId_t gpioId, int* gpioState) { ReturnValue_t LinuxLibgpioIF::readGpio(gpioId_t gpioId, int* gpioState) {
gpioMapIter = gpioMap.find(gpioId); gpioMapIter = gpioMap.find(gpioId);
if (gpioMapIter == gpioMap.end()){ if (gpioMapIter == gpioMap.end()){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LinuxLibgpioIF::readGpio: Unknown GPIOD ID " << gpioId << std::endl; sif::warning << "LinuxLibgpioIF::readGpio: Unknown GPIOD ID " << gpioId << std::endl;
#else
sif::printWarning("LinuxLibgpioIF::readGpio: Unknown GPIOD ID %d\n", gpioId);
#endif
return UNKNOWN_GPIO_ID; return UNKNOWN_GPIO_ID;
} }
auto gpioType = gpioMapIter->second->gpioType; auto gpioType = gpioMapIter->second->gpioType;
if(gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_CHIP or if (gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_CHIP
gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_LABEL) { or gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_LABEL
or gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_LINE_NAME) {
auto regularGpio = dynamic_cast<GpiodRegularBase*>(gpioMapIter->second); auto regularGpio = dynamic_cast<GpiodRegularBase*>(gpioMapIter->second);
if(regularGpio == nullptr) { if(regularGpio == nullptr) {
return GPIO_TYPE_FAILURE; return GPIO_TYPE_FAILURE;
@ -249,10 +300,14 @@ ReturnValue_t LinuxLibgpioIF::readGpio(gpioId_t gpioId, int* gpioState) {
*gpioState = gpiod_line_get_value(regularGpio->lineHandle); *gpioState = gpiod_line_get_value(regularGpio->lineHandle);
} }
else { else {
auto gpioCallback = dynamic_cast<GpioCallback*>(gpioMapIter->second);
if(gpioCallback->callback == nullptr) {
return GPIO_INVALID_INSTANCE;
}
gpioCallback->callback(gpioMapIter->first, gpio::GpioOperation::READ,
gpio::Levels::NONE, gpioCallback->callbackArgs);
return RETURN_OK;
} }
return RETURN_OK; return RETURN_OK;
} }
@ -262,13 +317,14 @@ ReturnValue_t LinuxLibgpioIF::checkForConflicts(GpioMap& mapToAdd){
for(auto& gpioConfig: mapToAdd) { for(auto& gpioConfig: mapToAdd) {
switch(gpioConfig.second->gpioType) { switch(gpioConfig.second->gpioType) {
case(gpio::GpioTypes::GPIO_REGULAR_BY_CHIP): case(gpio::GpioTypes::GPIO_REGULAR_BY_CHIP):
case(gpio::GpioTypes::GPIO_REGULAR_BY_LABEL): { case(gpio::GpioTypes::GPIO_REGULAR_BY_LABEL):
case(gpio::GpioTypes::GPIO_REGULAR_BY_LINE_NAME): {
auto regularGpio = dynamic_cast<GpiodRegularBase*>(gpioConfig.second); auto regularGpio = dynamic_cast<GpiodRegularBase*>(gpioConfig.second);
if(regularGpio == nullptr) { if(regularGpio == nullptr) {
return GPIO_TYPE_FAILURE; return GPIO_TYPE_FAILURE;
} }
/* Check for conflicts and remove duplicates if necessary */ // Check for conflicts and remove duplicates if necessary
result = checkForConflictsRegularGpio(gpioConfig.first, *regularGpio, mapToAdd); result = checkForConflictsById(gpioConfig.first, gpioConfig.second->gpioType, mapToAdd);
if(result != HasReturnvaluesIF::RETURN_OK) { if(result != HasReturnvaluesIF::RETURN_OK) {
status = result; status = result;
} }
@ -279,66 +335,108 @@ ReturnValue_t LinuxLibgpioIF::checkForConflicts(GpioMap& mapToAdd){
if(callbackGpio == nullptr) { if(callbackGpio == nullptr) {
return GPIO_TYPE_FAILURE; return GPIO_TYPE_FAILURE;
} }
/* Check for conflicts and remove duplicates if necessary */ // Check for conflicts and remove duplicates if necessary
result = checkForConflictsCallbackGpio(gpioConfig.first, callbackGpio, mapToAdd); result = checkForConflictsById(gpioConfig.first,
gpioConfig.second->gpioType, mapToAdd);
if(result != HasReturnvaluesIF::RETURN_OK) { if(result != HasReturnvaluesIF::RETURN_OK) {
status = result; status = result;
} }
break; break;
} }
default: { default: {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "Invalid GPIO type detected for GPIO ID " << gpioConfig.first
<< std::endl;
#else
sif::printWarning("Invalid GPIO type detected for GPIO ID %d\n", gpioConfig.first);
#endif
status = GPIO_TYPE_FAILURE;
} }
} }
} }
return status; return status;
} }
ReturnValue_t LinuxLibgpioIF::checkForConflictsById(gpioId_t gpioIdToCheck,
ReturnValue_t LinuxLibgpioIF::checkForConflictsRegularGpio(gpioId_t gpioIdToCheck, gpio::GpioTypes expectedType, GpioMap& mapToAdd) {
GpiodRegularBase& gpioToCheck, GpioMap& mapToAdd) { // Cross check with private map
/* Cross check with private map */
gpioMapIter = gpioMap.find(gpioIdToCheck); gpioMapIter = gpioMap.find(gpioIdToCheck);
if(gpioMapIter != gpioMap.end()) { if(gpioMapIter != gpioMap.end()) {
auto& gpioType = gpioMapIter->second->gpioType; auto& gpioType = gpioMapIter->second->gpioType;
if(gpioType != gpio::GpioTypes::GPIO_REGULAR_BY_CHIP and bool eraseDuplicateDifferentType = false;
gpioType != gpio::GpioTypes::GPIO_REGULAR_BY_LABEL) { switch(expectedType) {
sif::warning << "LinuxLibgpioIF::checkForConflicts: ID already exists for different " case(gpio::GpioTypes::NONE): {
"GPIO type" << gpioIdToCheck << ". Removing duplicate." << std::endl; break;
mapToAdd.erase(gpioIdToCheck);
return HasReturnvaluesIF::RETURN_OK;
} }
auto ownRegularGpio = dynamic_cast<GpiodRegularBase*>(gpioMapIter->second); case(gpio::GpioTypes::GPIO_REGULAR_BY_CHIP):
if(ownRegularGpio == nullptr) { case(gpio::GpioTypes::GPIO_REGULAR_BY_LABEL):
return GPIO_TYPE_FAILURE; case(gpio::GpioTypes::GPIO_REGULAR_BY_LINE_NAME): {
if(gpioType == gpio::GpioTypes::NONE or gpioType == gpio::GpioTypes::CALLBACK) {
eraseDuplicateDifferentType = true;
}
break;
}
case(gpio::GpioTypes::CALLBACK): {
if(gpioType != gpio::GpioTypes::CALLBACK) {
eraseDuplicateDifferentType = true;
}
}
}
if(eraseDuplicateDifferentType) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LinuxLibgpioIF::checkForConflicts: ID already exists for "
"different GPIO type " << gpioIdToCheck <<
". Removing duplicate from map to add" << std::endl;
#else
sif::printWarning("LinuxLibgpioIF::checkForConflicts: ID already exists for "
"different GPIO type %d. Removing duplicate from map to add\n", gpioIdToCheck);
#endif
mapToAdd.erase(gpioIdToCheck);
return GPIO_DUPLICATE_DETECTED;
} }
/* Remove element from map to add because a entry for this GPIO // Remove element from map to add because a entry for this GPIO already exists
already exists */ #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LinuxLibgpioIF::checkForConflictsRegularGpio: Duplicate GPIO definition" sif::warning << "LinuxLibgpioIF::checkForConflictsRegularGpio: Duplicate GPIO "
<< " detected. Duplicate will be removed from map to add." << std::endl; "definition with ID " << gpioIdToCheck << " detected. " <<
"Duplicate will be removed from map to add" << std::endl;
#else
sif::printWarning("LinuxLibgpioIF::checkForConflictsRegularGpio: Duplicate GPIO definition "
"with ID %d detected. Duplicate will be removed from map to add\n", gpioIdToCheck);
#endif
mapToAdd.erase(gpioIdToCheck); mapToAdd.erase(gpioIdToCheck);
return GPIO_DUPLICATE_DETECTED;
} }
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
ReturnValue_t LinuxLibgpioIF::checkForConflictsCallbackGpio(gpioId_t gpioIdToCheck, void LinuxLibgpioIF::parseFindeLineResult(int result, std::string& lineName) {
GpioCallback *callbackGpio, GpioMap& mapToAdd) { switch (result) {
/* Cross check with private map */ #if FSFW_CPP_OSTREAM_ENABLED == 1
gpioMapIter = gpioMap.find(gpioIdToCheck); case LINE_NOT_EXISTS:
if(gpioMapIter != gpioMap.end()) { case LINE_ERROR: {
if(gpioMapIter->second->gpioType != gpio::GpioTypes::CALLBACK) { sif::warning << "LinuxLibgpioIF::parseFindeLineResult: Line with name " << lineName <<
sif::warning << "LinuxLibgpioIF::checkForConflicts: ID already exists for different " " does not exist" << std::endl;
"GPIO type" << gpioIdToCheck << ". Removing duplicate." << std::endl; break;
mapToAdd.erase(gpioIdToCheck); }
return HasReturnvaluesIF::RETURN_OK; default: {
sif::warning << "LinuxLibgpioIF::parseFindeLineResult: Unknown return code for line "
"with name " << lineName << std::endl;
break;
}
#else
case LINE_NOT_EXISTS:
case LINE_ERROR: {
sif::printWarning("LinuxLibgpioIF::parseFindeLineResult: Line with name %s "
"does not exist\n", lineName);
break;
}
default: {
sif::printWarning("LinuxLibgpioIF::parseFindeLineResult: Unknown return code for line "
"with name %s\n", lineName);
break;
}
#endif
} }
/* Remove element from map to add because a entry for this GPIO
already exists */
sif::warning << "LinuxLibgpioIF::checkForConflictsRegularGpio: Duplicate GPIO definition"
<< " detected. Duplicate will be removed from map to add." << std::endl;
mapToAdd.erase(gpioIdToCheck);
}
return HasReturnvaluesIF::RETURN_OK;
} }

View File

@ -1,19 +1,19 @@
#ifndef LINUX_GPIO_LINUXLIBGPIOIF_H_ #ifndef LINUX_GPIO_LINUXLIBGPIOIF_H_
#define LINUX_GPIO_LINUXLIBGPIOIF_H_ #define LINUX_GPIO_LINUXLIBGPIOIF_H_
#include "../../common/gpio/GpioIF.h" #include "fsfw/returnvalues/FwClassIds.h"
#include <returnvalues/classIds.h> #include "fsfw_hal/common/gpio/GpioIF.h"
#include <fsfw/objectmanager/SystemObject.h> #include "fsfw/objectmanager/SystemObject.h"
class GpioCookie; class GpioCookie;
class GpiodRegularIF; class GpiodRegularIF;
/** /**
* @brief This class implements the GpioIF for a linux based system. The * @brief This class implements the GpioIF for a linux based system.
* implementation is based on the libgpiod lib which requires linux 4.8 * @details
* or higher. * This implementation is based on the libgpiod lib which requires Linux 4.8 or higher.
* @note The Petalinux SDK from Xilinx supports libgpiod since Petalinux * @note
* 2019.1. * The Petalinux SDK from Xilinx supports libgpiod since Petalinux 2019.1.
*/ */
class LinuxLibgpioIF : public GpioIF, public SystemObject { class LinuxLibgpioIF : public GpioIF, public SystemObject {
public: public:
@ -28,6 +28,8 @@ public:
HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 3); HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 3);
static constexpr ReturnValue_t GPIO_INVALID_INSTANCE = static constexpr ReturnValue_t GPIO_INVALID_INSTANCE =
HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 4); HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 4);
static constexpr ReturnValue_t GPIO_DUPLICATE_DETECTED =
HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 5);
LinuxLibgpioIF(object_id_t objectId); LinuxLibgpioIF(object_id_t objectId);
virtual ~LinuxLibgpioIF(); virtual ~LinuxLibgpioIF();
@ -38,7 +40,13 @@ public:
ReturnValue_t readGpio(gpioId_t gpioId, int* gpioState) override; ReturnValue_t readGpio(gpioId_t gpioId, int* gpioState) override;
private: private:
/* Holds the information and configuration of all used GPIOs */
static const size_t MAX_CHIPNAME_LENGTH = 11;
static const int LINE_NOT_EXISTS = 0;
static const int LINE_ERROR = -1;
static const int LINE_FOUND = 1;
// Holds the information and configuration of all used GPIOs
GpioUnorderedMap gpioMap; GpioUnorderedMap gpioMap;
GpioUnorderedMapIter gpioMapIter; GpioUnorderedMapIter gpioMapIter;
@ -53,8 +61,10 @@ private:
ReturnValue_t configureGpioByLabel(gpioId_t gpioId, GpiodRegularByLabel& gpioByLabel); ReturnValue_t configureGpioByLabel(gpioId_t gpioId, GpiodRegularByLabel& gpioByLabel);
ReturnValue_t configureGpioByChip(gpioId_t gpioId, GpiodRegularByChip& gpioByChip); ReturnValue_t configureGpioByChip(gpioId_t gpioId, GpiodRegularByChip& gpioByChip);
ReturnValue_t configureRegularGpio(gpioId_t gpioId, gpio::GpioTypes gpioType, ReturnValue_t configureGpioByLineName(gpioId_t gpioId,
struct gpiod_chip* chip, GpiodRegularBase& regularGpio, std::string failOutput); GpiodRegularByLineName &gpioByLineName);
ReturnValue_t configureRegularGpio(gpioId_t gpioId, struct gpiod_chip* chip,
GpiodRegularBase& regularGpio, std::string failOutput);
/** /**
* @brief This function checks if GPIOs are already registered and whether * @brief This function checks if GPIOs are already registered and whether
@ -67,9 +77,7 @@ private:
*/ */
ReturnValue_t checkForConflicts(GpioMap& mapToAdd); ReturnValue_t checkForConflicts(GpioMap& mapToAdd);
ReturnValue_t checkForConflictsRegularGpio(gpioId_t gpiodId, GpiodRegularBase& regularGpio, ReturnValue_t checkForConflictsById(gpioId_t gpiodId, gpio::GpioTypes type,
GpioMap& mapToAdd);
ReturnValue_t checkForConflictsCallbackGpio(gpioId_t gpiodId, GpioCallback* regularGpio,
GpioMap& mapToAdd); GpioMap& mapToAdd);
/** /**
@ -77,6 +85,7 @@ private:
*/ */
ReturnValue_t configureGpios(GpioMap& mapToAdd); ReturnValue_t configureGpios(GpioMap& mapToAdd);
void parseFindeLineResult(int result, std::string& lineName);
}; };
#endif /* LINUX_GPIO_LINUXLIBGPIOIF_H_ */ #endif /* LINUX_GPIO_LINUXLIBGPIOIF_H_ */

76
scripts/coverage.py Executable file
View File

@ -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()

View File

@ -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_ */

View File

@ -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_ */

View File

@ -1,13 +1,17 @@
#include "fsfw/osal/common/TcpTmTcServer.h"
#include "fsfw/osal/common/TcpTmTcBridge.h"
#include "fsfw/osal/common/tcpipHelpers.h"
#include "fsfw/platform.h" #include "fsfw/platform.h"
#include "fsfw/FSFW.h"
#include "TcpTmTcServer.h"
#include "TcpTmTcBridge.h"
#include "tcpipHelpers.h"
#include "fsfw/tmtcservices/SpacePacketParser.h"
#include "fsfw/tasks/TaskFactory.h"
#include "fsfw/globalfunctions/arrayprinter.h"
#include "fsfw/container/SharedRingBuffer.h" #include "fsfw/container/SharedRingBuffer.h"
#include "fsfw/ipc/MessageQueueSenderIF.h" #include "fsfw/ipc/MessageQueueSenderIF.h"
#include "fsfw/ipc/MutexGuard.h" #include "fsfw/ipc/MutexGuard.h"
#include "fsfw/objectmanager/ObjectManager.h" #include "fsfw/objectmanager/ObjectManager.h"
#include "fsfw/serviceinterface/ServiceInterface.h" #include "fsfw/serviceinterface/ServiceInterface.h"
#include "fsfw/tmtcservices/TmTcMessage.h" #include "fsfw/tmtcservices/TmTcMessage.h"
@ -18,19 +22,14 @@
#include <netdb.h> #include <netdb.h>
#endif #endif
#ifndef FSFW_TCP_RECV_WIRETAPPING_ENABLED
#define FSFW_TCP_RECV_WIRETAPPING_ENABLED 0
#endif
const std::string TcpTmTcServer::DEFAULT_SERVER_PORT = tcpip::DEFAULT_SERVER_PORT; const std::string TcpTmTcServer::DEFAULT_SERVER_PORT = tcpip::DEFAULT_SERVER_PORT;
TcpTmTcServer::TcpTmTcServer(object_id_t objectId, object_id_t tmtcTcpBridge, TcpTmTcServer::TcpTmTcServer(object_id_t objectId, object_id_t tmtcTcpBridge,
size_t receptionBufferSize, std::string customTcpServerPort): size_t receptionBufferSize, size_t ringBufferSize, std::string customTcpServerPort,
SystemObject(objectId), tmtcBridgeId(tmtcTcpBridge), ReceptionModes receptionMode):
tcpPort(customTcpServerPort), receptionBuffer(receptionBufferSize) { SystemObject(objectId), tmtcBridgeId(tmtcTcpBridge), receptionMode(receptionMode),
if(tcpPort == "") { tcpConfig(customTcpServerPort), receptionBuffer(receptionBufferSize),
tcpPort = DEFAULT_SERVER_PORT; ringBuffer(ringBufferSize, true) {
}
} }
ReturnValue_t TcpTmTcServer::initialize() { ReturnValue_t TcpTmTcServer::initialize() {
@ -41,6 +40,17 @@ ReturnValue_t TcpTmTcServer::initialize() {
return result; return result;
} }
switch(receptionMode) {
case(ReceptionModes::SPACE_PACKETS): {
spacePacketParser = new SpacePacketParser(validPacketIds);
if(spacePacketParser == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
#if defined PLATFORM_UNIX
tcpConfig.tcpFlags |= MSG_DONTWAIT;
#endif
}
}
tcStore = ObjectManager::instance()->get<StorageManagerIF>(objects::TC_STORE); tcStore = ObjectManager::instance()->get<StorageManagerIF>(objects::TC_STORE);
if (tcStore == nullptr) { if (tcStore == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
@ -63,7 +73,7 @@ ReturnValue_t TcpTmTcServer::initialize() {
hints.ai_flags = AI_PASSIVE; hints.ai_flags = AI_PASSIVE;
// Listen to all addresses (0.0.0.0) by using AI_PASSIVE in the hint flags // Listen to all addresses (0.0.0.0) by using AI_PASSIVE in the hint flags
retval = getaddrinfo(nullptr, tcpPort.c_str(), &hints, &addrResult); retval = getaddrinfo(nullptr, tcpConfig.tcpPort.c_str(), &hints, &addrResult);
if (retval != 0) { if (retval != 0) {
handleError(Protocol::TCP, ErrorSources::GETADDRINFO_CALL); handleError(Protocol::TCP, ErrorSources::GETADDRINFO_CALL);
return HasReturnvaluesIF::RETURN_FAILED; return HasReturnvaluesIF::RETURN_FAILED;
@ -105,7 +115,7 @@ ReturnValue_t TcpTmTcServer::performOperation(uint8_t opCode) {
// Listen for connection requests permanently for lifetime of program // Listen for connection requests permanently for lifetime of program
while(true) { while(true) {
retval = listen(listenerTcpSocket, tcpBacklog); retval = listen(listenerTcpSocket, tcpConfig.tcpBacklog);
if(retval == SOCKET_ERROR) { if(retval == SOCKET_ERROR) {
handleError(Protocol::TCP, ErrorSources::LISTEN_CALL, 500); handleError(Protocol::TCP, ErrorSources::LISTEN_CALL, 500);
continue; continue;
@ -123,11 +133,12 @@ ReturnValue_t TcpTmTcServer::performOperation(uint8_t opCode) {
handleServerOperation(connSocket); handleServerOperation(connSocket);
// Done, shut down connection and go back to listening for client requests // Done, shut down connection and go back to listening for client requests
retval = shutdown(connSocket, SHUT_SEND); retval = shutdown(connSocket, SHUT_BOTH);
if(retval != 0) { if(retval != 0) {
handleError(Protocol::TCP, ErrorSources::SHUTDOWN_CALL); handleError(Protocol::TCP, ErrorSources::SHUTDOWN_CALL);
} }
closeSocket(connSocket); closeSocket(connSocket);
connSocket = 0;
} }
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
@ -144,41 +155,88 @@ ReturnValue_t TcpTmTcServer::initializeAfterTaskCreation() {
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
void TcpTmTcServer::handleServerOperation(socket_t connSocket) { void TcpTmTcServer::handleServerOperation(socket_t& connSocket) {
int retval = 0; #if defined PLATFORM_WIN
do { setSocketNonBlocking(connSocket);
// Read all telecommands sent by the client #endif
retval = recv(connSocket,
while (true) {
int retval = recv(
connSocket,
reinterpret_cast<char*>(receptionBuffer.data()), reinterpret_cast<char*>(receptionBuffer.data()),
receptionBuffer.capacity(), receptionBuffer.capacity(),
tcpFlags); tcpConfig.tcpFlags
if (retval > 0) { );
handleTcReception(retval); if(retval == 0) {
size_t availableReadData = ringBuffer.getAvailableReadData();
if(availableReadData > lastRingBufferSize) {
handleTcRingBufferData(availableReadData);
}
return;
}
else if(retval > 0) {
// The ring buffer was configured for overwrite, so the returnvalue does not need to
// be checked for now
ringBuffer.writeData(receptionBuffer.data(), retval);
}
else if(retval < 0) {
int errorValue = getLastSocketError();
#if defined PLATFORM_UNIX
int wouldBlockValue = EAGAIN;
#elif defined PLATFORM_WIN
int wouldBlockValue = WSAEWOULDBLOCK;
#endif
if(errorValue == wouldBlockValue) {
// No data available. Check whether any packets have been read, then send back
// telemetry if available
bool tcAvailable = false;
bool tmSent = false;
size_t availableReadData = ringBuffer.getAvailableReadData();
if(availableReadData > lastRingBufferSize) {
tcAvailable = true;
handleTcRingBufferData(availableReadData);
}
ReturnValue_t result = handleTmSending(connSocket, tmSent);
if(result == CONN_BROKEN) {
return;
}
if(not tcAvailable and not tmSent) {
TaskFactory::delayTask(tcpConfig.tcpLoopDelay);
} }
else if(retval == 0) {
// Client has finished sending telecommands, send telemetry now
handleTmSending(connSocket);
} }
else { else {
// Should not happen tcpip::handleError(tcpip::Protocol::TCP, tcpip::ErrorSources::RECV_CALL, 300);
tcpip::handleError(tcpip::Protocol::TCP, tcpip::ErrorSources::RECV_CALL); }
}
} }
} while(retval > 0);
} }
ReturnValue_t TcpTmTcServer::handleTcReception(size_t bytesRecvd) { ReturnValue_t TcpTmTcServer::handleTcReception(uint8_t* spacePacket, size_t packetSize) {
#if FSFW_TCP_RECV_WIRETAPPING_ENABLED == 1 if(wiretappingEnabled) {
arrayprinter::print(receptionBuffer.data(), bytesRead); #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "Received TC:" << std::endl;
#else
sif::printInfo("Received TC:\n");
#endif #endif
arrayprinter::print(spacePacket, packetSize);
}
if(spacePacket == nullptr or packetSize == 0) {
return HasReturnvaluesIF::RETURN_FAILED;
}
store_address_t storeId; store_address_t storeId;
ReturnValue_t result = tcStore->addData(&storeId, receptionBuffer.data(), bytesRecvd); ReturnValue_t result = tcStore->addData(&storeId, spacePacket, packetSize);
if (result != HasReturnvaluesIF::RETURN_OK) { if (result != HasReturnvaluesIF::RETURN_OK) {
#if FSFW_VERBOSE_LEVEL >= 1 #if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning<< "TcpTmTcServer::handleServerOperation: Data storage failed." << std::endl; sif::warning << "TcpTmTcServer::handleServerOperation: Data storage with packet size" <<
sif::warning << "Packet size: " << bytesRecvd << std::endl; packetSize << " failed" << std::endl;
#else
sif::printWarning("TcpTmTcServer::handleServerOperation: Data storage with packet size %d "
"failed\n", packetSize);
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ #endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
#endif /* FSFW_VERBOSE_LEVEL >= 1 */ #endif /* FSFW_VERBOSE_LEVEL >= 1 */
return result;
} }
TmTcMessage message(storeId); TmTcMessage message(storeId);
@ -187,8 +245,11 @@ ReturnValue_t TcpTmTcServer::handleTcReception(size_t bytesRecvd) {
if (result != HasReturnvaluesIF::RETURN_OK) { if (result != HasReturnvaluesIF::RETURN_OK) {
#if FSFW_VERBOSE_LEVEL >= 1 #if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "UdpTcPollingTask::handleSuccessfullTcRead: " sif::warning << "TcpTmTcServer::handleServerOperation: "
" Sending message to queue failed" << std::endl; " Sending message to queue failed" << std::endl;
#else
sif::printWarning("TcpTmTcServer::handleServerOperation: "
" Sending message to queue failed\n");
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ #endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
#endif /* FSFW_VERBOSE_LEVEL >= 1 */ #endif /* FSFW_VERBOSE_LEVEL >= 1 */
tcStore->deleteData(storeId); tcStore->deleteData(storeId);
@ -196,21 +257,26 @@ ReturnValue_t TcpTmTcServer::handleTcReception(size_t bytesRecvd) {
return result; return result;
} }
void TcpTmTcServer::setTcpBacklog(uint8_t tcpBacklog) {
this->tcpBacklog = tcpBacklog;
}
std::string TcpTmTcServer::getTcpPort() const { std::string TcpTmTcServer::getTcpPort() const {
return tcpPort; return tcpConfig.tcpPort;
} }
ReturnValue_t TcpTmTcServer::handleTmSending(socket_t connSocket) { void TcpTmTcServer::setSpacePacketParsingOptions(std::vector<uint16_t> validPacketIds) {
this->validPacketIds = validPacketIds;
}
TcpTmTcServer::TcpConfig& TcpTmTcServer::getTcpConfigStruct() {
return tcpConfig;
}
ReturnValue_t TcpTmTcServer::handleTmSending(socket_t connSocket, bool& tmSent) {
// Access to the FIFO is mutex protected because it is filled by the bridge // Access to the FIFO is mutex protected because it is filled by the bridge
MutexGuard(tmtcBridge->mutex, tmtcBridge->timeoutType, tmtcBridge->mutexTimeoutMs); MutexGuard(tmtcBridge->mutex, tmtcBridge->timeoutType, tmtcBridge->mutexTimeoutMs);
store_address_t storeId; store_address_t storeId;
while((not tmtcBridge->tmFifo->empty()) and while((not tmtcBridge->tmFifo->empty()) and
(tmtcBridge->packetSentCounter < tmtcBridge->sentPacketsPerCycle)) { (tmtcBridge->packetSentCounter < tmtcBridge->sentPacketsPerCycle)) {
tmtcBridge->tmFifo->retrieve(&storeId); // Send can fail, so only peek from the FIFO
tmtcBridge->tmFifo->peek(&storeId);
// Using the store accessor will take care of deleting TM from the store automatically // Using the store accessor will take care of deleting TM from the store automatically
ConstStorageAccessor storeAccessor(storeId); ConstStorageAccessor storeAccessor(storeId);
@ -218,13 +284,134 @@ ReturnValue_t TcpTmTcServer::handleTmSending(socket_t connSocket) {
if(result != HasReturnvaluesIF::RETURN_OK) { if(result != HasReturnvaluesIF::RETURN_OK) {
return result; return result;
} }
if(wiretappingEnabled) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "Sending TM:" << std::endl;
#else
sif::printInfo("Sending TM:\n");
#endif
arrayprinter::print(storeAccessor.data(), storeAccessor.size());
}
int retval = send(connSocket, int retval = send(connSocket,
reinterpret_cast<const char*>(storeAccessor.data()), reinterpret_cast<const char*>(storeAccessor.data()),
storeAccessor.size(), storeAccessor.size(),
tcpTmFlags); tcpConfig.tcpTmFlags);
if(retval != static_cast<int>(storeAccessor.size())) { if(retval == static_cast<int>(storeAccessor.size())) {
tcpip::handleError(tcpip::Protocol::TCP, tcpip::ErrorSources::SEND_CALL); // Packet sent, clear FIFO entry
tmtcBridge->tmFifo->pop();
tmSent = true;
}
else if(retval <= 0) {
// Assume that the client has closed the connection here for now
handleSocketError(storeAccessor);
return CONN_BROKEN;
} }
} }
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
ReturnValue_t TcpTmTcServer::handleTcRingBufferData(size_t availableReadData) {
ReturnValue_t status = HasReturnvaluesIF::RETURN_OK;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
size_t readAmount = availableReadData;
lastRingBufferSize = availableReadData;
if(readAmount >= ringBuffer.getMaxSize()) {
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1
// Possible configuration error, too much data or/and data coming in too fast,
// requiring larger buffers
sif::warning << "TcpTmTcServer::handleServerOperation: Ring buffer reached " <<
"fill count" << std::endl;
#else
sif::printWarning("TcpTmTcServer::handleServerOperation: Ring buffer reached "
"fill count");
#endif
#endif
}
if(readAmount >= receptionBuffer.size()) {
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1
// Possible configuration error, too much data or/and data coming in too fast,
// requiring larger buffers
sif::warning << "TcpTmTcServer::handleServerOperation: "
"Reception buffer too small " << std::endl;
#else
sif::printWarning("TcpTmTcServer::handleServerOperation: Reception buffer too small\n");
#endif
#endif
readAmount = receptionBuffer.size();
}
ringBuffer.readData(receptionBuffer.data(), readAmount, true);
const uint8_t* bufPtr = receptionBuffer.data();
const uint8_t** bufPtrPtr = &bufPtr;
size_t startIdx = 0;
size_t foundSize = 0;
size_t readLen = 0;
while(readLen < readAmount) {
result = spacePacketParser->parseSpacePackets(bufPtrPtr, readAmount,
startIdx, foundSize, readLen);
switch(result) {
case(SpacePacketParser::NO_PACKET_FOUND):
case(SpacePacketParser::SPLIT_PACKET): {
break;
}
case(HasReturnvaluesIF::RETURN_OK): {
result = handleTcReception(receptionBuffer.data() + startIdx, foundSize);
if(result != HasReturnvaluesIF::RETURN_OK) {
status = result;
}
}
}
ringBuffer.deleteData(foundSize);
lastRingBufferSize = ringBuffer.getAvailableReadData();
std::memset(receptionBuffer.data() + startIdx, 0, foundSize);
}
return status;
}
void TcpTmTcServer::enableWiretapping(bool enable) {
this->wiretappingEnabled = enable;
}
void TcpTmTcServer::handleSocketError(ConstStorageAccessor &accessor) {
// Don't delete data
accessor.release();
auto socketError = getLastSocketError();
switch(socketError) {
#if defined PLATFORM_WIN
case(WSAECONNRESET): {
// See https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-send
// Remote client might have shut down connection
return;
}
#else
case(EPIPE): {
// See https://man7.org/linux/man-pages/man2/send.2.html
// Remote client might have shut down connection
return;
}
#endif
default: {
tcpip::handleError(tcpip::Protocol::TCP, tcpip::ErrorSources::SEND_CALL);
}
}
}
#if defined PLATFORM_WIN
void TcpTmTcServer::setSocketNonBlocking(socket_t &connSocket) {
u_long iMode = 1;
int iResult = ioctlsocket(connSocket, FIONBIO, &iMode);
if(iResult != NO_ERROR) {
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "TcpTmTcServer::handleServerOperation: Setting socket"
" non-blocking failed with error " << iResult;
#else
sif::printWarning("TcpTmTcServer::handleServerOperation: Setting socket"
" non-blocking failed with error %d\n", iResult);
#endif
#endif
}
}
#endif

View File

@ -6,6 +6,7 @@
#include "fsfw/platform.h" #include "fsfw/platform.h"
#include "fsfw/osal/common/tcpipHelpers.h" #include "fsfw/osal/common/tcpipHelpers.h"
#include "fsfw/ipc/messageQueueDefinitions.h" #include "fsfw/ipc/messageQueueDefinitions.h"
#include "fsfw/container/SimpleRingBuffer.h"
#include "fsfw/ipc/MessageQueueIF.h" #include "fsfw/ipc/MessageQueueIF.h"
#include "fsfw/objectmanager/frameworkObjects.h" #include "fsfw/objectmanager/frameworkObjects.h"
#include "fsfw/objectmanager/SystemObject.h" #include "fsfw/objectmanager/SystemObject.h"
@ -20,6 +21,7 @@
#include <vector> #include <vector>
class TcpTmTcBridge; class TcpTmTcBridge;
class SpacePacketParser;
/** /**
* @brief TCP server implementation * @brief TCP server implementation
@ -42,9 +44,38 @@ class TcpTmTcServer:
public TcpIpBase, public TcpIpBase,
public ExecutableObjectIF { public ExecutableObjectIF {
public: public:
enum class ReceptionModes {
SPACE_PACKETS
};
struct TcpConfig {
public:
TcpConfig(std::string tcpPort): tcpPort(tcpPort) {}
/**
* Passed to the recv call
*/
int tcpFlags = 0;
int tcpBacklog = 3;
/**
* If no telecommands packets are being received and no telemetry is being sent,
* the TCP server will delay periodically by this amount to decrease the CPU load
*/
uint32_t tcpLoopDelay = DEFAULT_LOOP_DELAY_MS ;
/**
* Passed to the send call
*/
int tcpTmFlags = 0;
const std::string tcpPort;
};
static const std::string DEFAULT_SERVER_PORT; static const std::string DEFAULT_SERVER_PORT;
static constexpr size_t ETHERNET_MTU_SIZE = 1500; static constexpr size_t ETHERNET_MTU_SIZE = 1500;
static constexpr size_t RING_BUFFER_SIZE = ETHERNET_MTU_SIZE * 3;
static constexpr uint32_t DEFAULT_LOOP_DELAY_MS = 200;
/** /**
* TCP Server Constructor * TCP Server Constructor
@ -55,11 +86,21 @@ public:
* @param customTcpServerPort The user can specify another port than the default (7301) here. * @param customTcpServerPort The user can specify another port than the default (7301) here.
*/ */
TcpTmTcServer(object_id_t objectId, object_id_t tmtcTcpBridge, TcpTmTcServer(object_id_t objectId, object_id_t tmtcTcpBridge,
size_t receptionBufferSize = ETHERNET_MTU_SIZE + 1, size_t receptionBufferSize = RING_BUFFER_SIZE,
std::string customTcpServerPort = ""); size_t ringBufferSize = RING_BUFFER_SIZE,
std::string customTcpServerPort = DEFAULT_SERVER_PORT,
ReceptionModes receptionMode = ReceptionModes::SPACE_PACKETS);
virtual~ TcpTmTcServer(); virtual~ TcpTmTcServer();
void setTcpBacklog(uint8_t tcpBacklog); void enableWiretapping(bool enable);
/**
* Get a handle to the TCP configuration struct, which can be used to configure TCP
* properties
* @return
*/
TcpConfig& getTcpConfigStruct();
void setSpacePacketParsingOptions(std::vector<uint16_t> validPacketIds);
ReturnValue_t initialize() override; ReturnValue_t initialize() override;
ReturnValue_t performOperation(uint8_t opCode) override; ReturnValue_t performOperation(uint8_t opCode) override;
@ -71,25 +112,33 @@ protected:
StorageManagerIF* tcStore = nullptr; StorageManagerIF* tcStore = nullptr;
StorageManagerIF* tmStore = nullptr; StorageManagerIF* tmStore = nullptr;
private: private:
static constexpr ReturnValue_t CONN_BROKEN = HasReturnvaluesIF::makeReturnCode(1, 0);
//! TMTC bridge is cached. //! TMTC bridge is cached.
object_id_t tmtcBridgeId = objects::NO_OBJECT; object_id_t tmtcBridgeId = objects::NO_OBJECT;
TcpTmTcBridge* tmtcBridge = nullptr; TcpTmTcBridge* tmtcBridge = nullptr;
bool wiretappingEnabled = false;
std::string tcpPort; ReceptionModes receptionMode;
int tcpFlags = 0; TcpConfig tcpConfig;
socket_t listenerTcpSocket = 0;
struct sockaddr tcpAddress; struct sockaddr tcpAddress;
socket_t listenerTcpSocket = 0;
MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE; MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE;
int tcpAddrLen = sizeof(tcpAddress);
int tcpBacklog = 3;
std::vector<uint8_t> receptionBuffer; std::vector<uint8_t> receptionBuffer;
int tcpSockOpt = 0; SimpleRingBuffer ringBuffer;
int tcpTmFlags = 0; std::vector<uint16_t> validPacketIds;
SpacePacketParser* spacePacketParser = nullptr;
uint8_t lastRingBufferSize = 0;
void handleServerOperation(socket_t connSocket); virtual void handleServerOperation(socket_t& connSocket);
ReturnValue_t handleTcReception(size_t bytesRecvd); ReturnValue_t handleTcReception(uint8_t* spacePacket, size_t packetSize);
ReturnValue_t handleTmSending(socket_t connSocket); ReturnValue_t handleTmSending(socket_t connSocket, bool& tmSent);
ReturnValue_t handleTcRingBufferData(size_t availableReadData);
void handleSocketError(ConstStorageAccessor& accessor);
#if defined PLATFORM_WIN
void setSocketNonBlocking(socket_t& connSocket);
#endif
}; };
#endif /* FSFW_OSAL_COMMON_TCP_TMTC_SERVER_H_ */ #endif /* FSFW_OSAL_COMMON_TCP_TMTC_SERVER_H_ */

View File

@ -21,6 +21,9 @@ void tcpip::determineErrorStrings(Protocol protocol, ErrorSources errorSrc, std:
if(errorSrc == ErrorSources::SETSOCKOPT_CALL) { if(errorSrc == ErrorSources::SETSOCKOPT_CALL) {
srcString = "setsockopt call"; srcString = "setsockopt call";
} }
if(errorSrc == ErrorSources::BIND_CALL) {
srcString = "bind call";
}
else if(errorSrc == ErrorSources::SOCKET_CALL) { else if(errorSrc == ErrorSources::SOCKET_CALL) {
srcString = "socket call"; srcString = "socket call";
} }

View File

@ -80,6 +80,7 @@ enum: uint8_t {
FIXED_SLOT_TASK_IF, //FTIF FIXED_SLOT_TASK_IF, //FTIF
MGM_LIS3MDL, //MGMLIS3 MGM_LIS3MDL, //MGMLIS3
MGM_RM3100, //MGMRM3100 MGM_RM3100, //MGMRM3100
SPACE_PACKET_PARSER, //SPPA
FW_CLASS_ID_COUNT // [EXPORT] : [END] FW_CLASS_ID_COUNT // [EXPORT] : [END]
}; };

View File

@ -29,12 +29,28 @@ PUSDistributor::TcMqMapIter PUSDistributor::selectDestination() {
tcStatus = checker.checkPacket(currentPacket); tcStatus = checker.checkPacket(currentPacket);
if(tcStatus != HasReturnvaluesIF::RETURN_OK) { if(tcStatus != HasReturnvaluesIF::RETURN_OK) {
#if FSFW_VERBOSE_LEVEL >= 1 #if FSFW_VERBOSE_LEVEL >= 1
const char* keyword = "unnamed error";
if(tcStatus == TcPacketCheck::INCORRECT_CHECKSUM) {
keyword = "checksum";
}
else if(tcStatus == TcPacketCheck::INCORRECT_PRIMARY_HEADER) {
keyword = "incorrect primary header";
}
else if(tcStatus == TcPacketCheck::ILLEGAL_APID) {
keyword = "illegal APID";
}
else if(tcStatus == TcPacketCheck::INCORRECT_SECONDARY_HEADER) {
keyword = "incorrect secondary header";
}
else if(tcStatus == TcPacketCheck::INCOMPLETE_PACKET) {
keyword = "incomplete packet";
}
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << "PUSDistributor::handlePacket: Packet format invalid, code " << sif::warning << "PUSDistributor::handlePacket: Packet format invalid, "
static_cast<int>(tcStatus) << std::endl; << keyword << " error" << std::endl;
#else #else
sif::printDebug("PUSDistributor::handlePacket: Packet format invalid, code %d\n", sif::printWarning("PUSDistributor::handlePacket: Packet format invalid, "
static_cast<int>(tcStatus)); "%s error\n", keyword);
#endif #endif
#endif #endif
} }

View File

@ -40,6 +40,7 @@ public:
* @li \c false else. * @li \c false else.
*/ */
bool addWholeData(const uint8_t* p_data, uint32_t packet_size); bool addWholeData(const uint8_t* p_data, uint32_t packet_size);
protected: protected:
/** /**
* This structure defines the data structure of a Space Packet as local data. * This structure defines the data structure of a Space Packet as local data.
@ -68,4 +69,24 @@ protected:
SpacePacketData localData; SpacePacketData localData;
}; };
namespace spacepacket {
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 getTcSpacketIdFromApid(uint16_t apid,
bool secondaryHeaderFlag = true) {
return getSpacePacketIdFromApid(true, apid, secondaryHeaderFlag);
}
constexpr uint16_t getTmSpacketIdFromApid(uint16_t apid,
bool secondaryHeaderFlag = true) {
return getSpacePacketIdFromApid(false, apid, secondaryHeaderFlag);
}
}
#endif /* SPACEPACKET_H_ */ #endif /* SPACEPACKET_H_ */

View File

@ -119,6 +119,6 @@ void TmPacketStoredBase::handleStoreFailure(const char *const packetType, Return
break; break;
} }
#endif #endif
}
#endif #endif
} }
}

View File

@ -6,4 +6,5 @@ target_sources(${LIB_FSFW_NAME}
TmTcBridge.cpp TmTcBridge.cpp
TmTcMessage.cpp TmTcMessage.cpp
VerificationReporter.cpp VerificationReporter.cpp
SpacePacketParser.cpp
) )

View File

@ -0,0 +1,77 @@
#include <fsfw/serviceinterface/ServiceInterface.h>
#include <fsfw/tmtcservices/SpacePacketParser.h>
#include <algorithm>
SpacePacketParser::SpacePacketParser(std::vector<uint16_t> validPacketIds):
validPacketIds(validPacketIds) {
}
ReturnValue_t SpacePacketParser::parseSpacePackets(const uint8_t *buffer,
const size_t maxSize, size_t& startIndex, size_t& foundSize) {
const uint8_t** tempPtr = &buffer;
size_t readLen = 0;
return parseSpacePackets(tempPtr, maxSize, startIndex, foundSize, readLen);
}
ReturnValue_t SpacePacketParser::parseSpacePackets(const uint8_t **buffer, const size_t maxSize,
size_t &startIndex, size_t &foundSize, size_t& readLen) {
if(buffer == nullptr or maxSize < 5) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "SpacePacketParser::parseSpacePackets: Frame invalid" << std::endl;
#else
sif::printWarning("SpacePacketParser::parseSpacePackets: Frame invalid\n");
#endif
return HasReturnvaluesIF::RETURN_FAILED;
}
const uint8_t* bufPtr = *buffer;
auto verifyLengthField = [&](size_t idx) {
uint16_t lengthField = bufPtr[idx + 4] << 8 | bufPtr[idx + 5];
size_t packetSize = lengthField + 7;
startIndex = idx;
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
if(lengthField == 0) {
// Skip whole header for now
foundSize = 6;
result = NO_PACKET_FOUND;
}
else if(packetSize + idx > maxSize) {
// Don't increment buffer and read length here, user has to decide what to do
foundSize = packetSize;
return SPLIT_PACKET;
}
else {
foundSize = packetSize;
}
*buffer += foundSize;
readLen += idx + foundSize;
return result;
};
size_t idx = 0;
// Space packet ID as start marker
if(validPacketIds.size() > 0) {
while(idx < maxSize - 5) {
uint16_t currentPacketId = bufPtr[idx] << 8 | bufPtr[idx + 1];
if(std::find(validPacketIds.begin(), validPacketIds.end(), currentPacketId) !=
validPacketIds.end()) {
if(idx + 5 >= maxSize) {
return SPLIT_PACKET;
}
return verifyLengthField(idx);
}
else {
idx++;
}
}
startIndex = 0;
foundSize = maxSize;
*buffer += foundSize;
readLen += foundSize;
return NO_PACKET_FOUND;
}
// Assume that the user verified a valid start of a space packet
else {
return verifyLengthField(idx);
}
}

View File

@ -0,0 +1,78 @@
#ifndef FRAMEWORK_TMTCSERVICES_PUSPARSER_H_
#define FRAMEWORK_TMTCSERVICES_PUSPARSER_H_
#include "fsfw/container/DynamicFIFO.h"
#include "fsfw/returnvalues/FwClassIds.h"
#include <utility>
#include <cstdint>
/**
* @brief This small helper class scans a given buffer for space packets.
* Can be used if space packets are serialized in a tightly packed frame.
* @details
* The parser uses the length field field and the 16-bit TC packet ID of the space packets to find
* find space packets in a given data stream
* @author R. Mueller
*/
class SpacePacketParser {
public:
//! The first entry is the index inside the buffer while the second index
//! is the size of the PUS packet starting at that index.
using IndexSizePair = std::pair<size_t, size_t>;
static constexpr uint8_t INTERFACE_ID = CLASS_ID::SPACE_PACKET_PARSER;
static constexpr ReturnValue_t NO_PACKET_FOUND = MAKE_RETURN_CODE(0x00);
static constexpr ReturnValue_t SPLIT_PACKET = MAKE_RETURN_CODE(0x01);
/**
* @brief Parser constructor.
* @param validPacketIds This vector contains the allowed 16-bit TC packet ID start markers
* The parser will search for these stark markers to detect the start of a space packet.
* It is also possible to pass an empty vector here, but this is not recommended.
* If an empty vector is passed, the parser will assume that the start of the given stream
* contains the start of a new space packet.
*/
SpacePacketParser(std::vector<uint16_t> validPacketIds);
/**
* Parse a given frame for space packets but also increment the given buffer and assign the
* total number of bytes read so far
* @param buffer Parser will look for space packets in this buffer
* @param maxSize Maximum size of the buffer
* @param startIndex Start index of a found space packet
* @param foundSize Found size of the space packet
* @param readLen Length read so far. This value is incremented by the number of parsed
* bytes which also includes the size of a found packet
* -@c NO_PACKET_FOUND if no packet was found in the given buffer or the length field is
* invalid. foundSize will be set to the size of the space packet header. buffer and
* readLen will be incremented accordingly.
* -@c SPLIT_PACKET if a packet was found but the detected size exceeds maxSize. foundSize
* will be set to the detected packet size and startIndex will be set to the start of the
* detected packet. buffer and read length will not be incremented but the found length
* will be assigned.
* -@c RETURN_OK if a packet was found
*/
ReturnValue_t parseSpacePackets(const uint8_t **buffer, const size_t maxSize,
size_t& startIndex, size_t& foundSize, size_t& readLen);
/**
* Parse a given frame for space packets
* @param buffer Parser will look for space packets in this buffer
* @param maxSize Maximum size of the buffer
* @param startIndex Start index of a found space packet
* @param foundSize Found size of the space packet
* -@c NO_PACKET_FOUND if no packet was found in the given buffer or the length field is
* invalid. foundSize will be set to the size of the space packet header
* -@c SPLIT_PACKET if a packet was found but the detected size exceeds maxSize. foundSize
* will be set to the detected packet size and startIndex will be set to the start of the
* detected packet
* -@c RETURN_OK if a packet was found
*/
ReturnValue_t parseSpacePackets(const uint8_t* buffer, const size_t maxSize,
size_t& startIndex, size_t& foundSize);
private:
std::vector<uint16_t> validPacketIds;
};
#endif /* FRAMEWORK_TMTCSERVICES_PUSPARSER_H_ */

View File

@ -3,6 +3,6 @@ if(FSFW_ADD_INTERNAL_TESTS)
add_subdirectory(internal) add_subdirectory(internal)
endif() endif()
if(FSFW_ADD_UNITTESTS) if(FSFW_BUILD_UNITTESTS)
add_subdirectory(unit) add_subdirectory(unit)
endif() endif()

View File

@ -1,9 +1,9 @@
#include "fsfw_tests/internal/InternalUnitTester.h" #include "fsfw_tests/internal/InternalUnitTester.h"
#include "fsfw_tests/internal/UnittDefinitions.h" #include "fsfw_tests/internal/UnittDefinitions.h"
#include "fsfw_tests/internal/osal/IntTestMq.h" #include "fsfw_tests/internal/osal/testMq.h"
#include "fsfw_tests/internal/osal/IntTestSemaphore.h" #include "fsfw_tests/internal/osal/testSemaphore.h"
#include "fsfw_tests/internal/osal/IntTestMutex.h" #include "fsfw_tests/internal/osal/testMutex.h"
#include "fsfw_tests/internal/serialize/IntTestSerialization.h" #include "fsfw_tests/internal/serialize/IntTestSerialization.h"
#include "fsfw_tests/internal/globalfunctions/TestArrayPrinter.h" #include "fsfw_tests/internal/globalfunctions/TestArrayPrinter.h"

View File

@ -1,5 +1,5 @@
target_sources(${LIB_FSFW_NAME} PRIVATE target_sources(${LIB_FSFW_NAME} PRIVATE
IntTestMq.cpp testMq.cpp
IntTestMutex.cpp testMutex.cpp
IntTestSemaphore.cpp testSemaphore.cpp
) )

View File

@ -1,4 +1,4 @@
#include "fsfw_tests/internal/osal/IntTestMq.h" #include "testMq.h"
#include "fsfw_tests/internal/UnittDefinitions.h" #include "fsfw_tests/internal/UnittDefinitions.h"
#include <fsfw/ipc/MessageQueueIF.h> #include <fsfw/ipc/MessageQueueIF.h>

View File

@ -1,4 +1,4 @@
#include "fsfw_tests/internal/osal/IntTestMutex.h" #include "testMutex.h"
#include "fsfw_tests/internal/UnittDefinitions.h" #include "fsfw_tests/internal/UnittDefinitions.h"
#include "fsfw/platform.h" #include "fsfw/platform.h"

View File

@ -1,5 +1,5 @@
#include "fsfw/FSFW.h" #include "fsfw/FSFW.h"
#include "fsfw_tests/internal/osal/IntTestSemaphore.h" #include "testSemaphore.h"
#include "fsfw_tests/internal/UnittDefinitions.h" #include "fsfw_tests/internal/UnittDefinitions.h"
#include "fsfw/tasks/SemaphoreFactory.h" #include "fsfw/tasks/SemaphoreFactory.h"

View File

@ -1,16 +1,17 @@
target_sources(${TARGET_NAME} PRIVATE target_sources(${FSFW_TEST_TGT} PRIVATE
CatchDefinitions.cpp CatchDefinitions.cpp
CatchFactory.cpp CatchFactory.cpp
printChar.cpp printChar.cpp
) )
if(FSFW_CUSTOM_UNITTEST_RUNNER) # if(FSFW_CUSTOM_UNITTEST_RUNNER)
target_sources(${TARGET_NAME} PRIVATE target_sources(${FSFW_TEST_TGT} PRIVATE
CatchRunner.cpp CatchRunner.cpp
CatchSetup.cpp CatchSetup.cpp
) )
endif() # endif()
add_subdirectory(testcfg)
add_subdirectory(action) add_subdirectory(action)
add_subdirectory(container) add_subdirectory(container)
add_subdirectory(osal) add_subdirectory(osal)

View File

@ -1,4 +1,5 @@
#include "CatchFactory.h" #include "CatchFactory.h"
#include "tests/TestsConfig.h"
#include "datapoollocal/LocalPoolOwnerBase.h" #include "datapoollocal/LocalPoolOwnerBase.h"
#include "mocks/HkReceiverMock.h" #include "mocks/HkReceiverMock.h"

View File

@ -1,7 +1,7 @@
#ifndef FSFW_CATCHFACTORY_H_ #ifndef FSFW_CATCHFACTORY_H_
#define FSFW_CATCHFACTORY_H_ #define FSFW_CATCHFACTORY_H_
#include "TestsConfig.h" #include "tests/TestsConfig.h"
#include "fsfw/objectmanager/SystemObjectIF.h" #include "fsfw/objectmanager/SystemObjectIF.h"
#include "fsfw/objectmanager/ObjectManager.h" #include "fsfw/objectmanager/ObjectManager.h"

View File

@ -14,7 +14,7 @@
extern int customSetup(); extern int customSetup();
int fsfwtest::customMain(int argc, char* argv[]) { int main(int argc, char* argv[]) {
customSetup(); customSetup();
// Catch internal function call // Catch internal function call

View File

@ -7,7 +7,7 @@ namespace fsfwtest {
* Can be called by upper level main() if default Catch2 main is overriden * Can be called by upper level main() if default Catch2 main is overriden
* @return * @return
*/ */
int customMain(int argc, char* argv[]); //int customMain(int argc, char* argv[]);
} }

View File

@ -1,3 +1,3 @@
target_sources(${TARGET_NAME} PRIVATE target_sources(${FSFW_TEST_TGT} PRIVATE
TestActionHelper.cpp TestActionHelper.cpp
) )

View File

@ -1,4 +1,4 @@
target_sources(${TARGET_NAME} PRIVATE target_sources(${FSFW_TEST_TGT} PRIVATE
RingBufferTest.cpp RingBufferTest.cpp
TestArrayList.cpp TestArrayList.cpp
TestDynamicFifo.cpp TestDynamicFifo.cpp

View File

@ -1,4 +1,4 @@
target_sources(${TARGET_NAME} PRIVATE target_sources(${FSFW_TEST_TGT} PRIVATE
LocalPoolVariableTest.cpp LocalPoolVariableTest.cpp
LocalPoolVectorTest.cpp LocalPoolVectorTest.cpp
DataSetTest.cpp DataSetTest.cpp

View File

@ -1,4 +1,5 @@
#include "LocalPoolOwnerBase.h" #include "LocalPoolOwnerBase.h"
#include "tests/TestsConfig.h"
#include "fsfw_tests/unit/CatchDefinitions.h" #include "fsfw_tests/unit/CatchDefinitions.h"
#include <fsfw/objectmanager/ObjectManager.h> #include <fsfw/objectmanager/ObjectManager.h>

View File

@ -1,7 +1,7 @@
#ifndef FSFW_UNITTEST_TESTS_DATAPOOLLOCAL_LOCALPOOLOWNERBASE_H_ #ifndef FSFW_UNITTEST_TESTS_DATAPOOLLOCAL_LOCALPOOLOWNERBASE_H_
#define 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 "../mocks/MessageQueueMockBase.h"
#include <fsfw/datapoollocal/HasLocalDataPoolIF.h> #include <fsfw/datapoollocal/HasLocalDataPoolIF.h>

View File

@ -1,4 +1,5 @@
#include "LocalPoolOwnerBase.h" #include "LocalPoolOwnerBase.h"
#include "tests/TestsConfig.h"
#include "fsfw_tests/unit/CatchDefinitions.h" #include "fsfw_tests/unit/CatchDefinitions.h"
#include <fsfw/objectmanager/ObjectManager.h> #include <fsfw/objectmanager/ObjectManager.h>

View File

@ -1,4 +1,5 @@
#include "LocalPoolOwnerBase.h" #include "LocalPoolOwnerBase.h"
#include "tests/TestsConfig.h"
#include "fsfw_tests/unit/CatchDefinitions.h" #include "fsfw_tests/unit/CatchDefinitions.h"
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>

View File

@ -1,3 +1,3 @@
target_sources(${TARGET_NAME} PRIVATE target_sources(${FSFW_TEST_TGT} PRIVATE
testDleEncoder.cpp testDleEncoder.cpp
) )

View File

@ -1,4 +1,4 @@
target_sources(${TARGET_NAME} PRIVATE target_sources(${FSFW_TEST_TGT} PRIVATE
TestMessageQueue.cpp TestMessageQueue.cpp
TestSemaphore.cpp TestSemaphore.cpp
) )

View File

@ -1,4 +1,4 @@
target_sources(${TARGET_NAME} PRIVATE target_sources(${FSFW_TEST_TGT} PRIVATE
TestSerialBufferAdapter.cpp TestSerialBufferAdapter.cpp
TestSerialization.cpp TestSerialization.cpp
TestSerialLinkedPacket.cpp TestSerialLinkedPacket.cpp

View File

@ -1,4 +1,4 @@
target_sources(${TARGET_NAME} PRIVATE target_sources(${FSFW_TEST_TGT} PRIVATE
TestNewAccessor.cpp TestNewAccessor.cpp
TestPool.cpp TestPool.cpp
) )

View File

@ -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()

View File

@ -9,22 +9,19 @@
//! the C stdio functions can be used alternatively //! 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. //! More FSFW related printouts depending on level. Useful for development.
#define FSFW_ENHANCED_PRINTOUT 0 #define FSFW_VERBOSE_LEVEL 0
//! Can be used to completely disable printouts, even the C stdio ones. //! 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_VERBOSE_LEVEL == 0
#if FSFW_CPP_OSTREAM_ENABLED == 0 && FSFW_ENHANCED_PRINTOUT == 0
#ifndef FSFW_DISABLE_PRINTOUT
#define FSFW_DISABLE_PRINTOUT 1 #define FSFW_DISABLE_PRINTOUT 1
#endif #endif
#endif
//! Can be used to enable additional debugging printouts for developing the FSFW #define FSFW_USE_PUS_C_TELEMETRY 1
#define FSFW_PRINT_VERBOSITY_LEVEL 0 #define FSFW_USE_PUS_C_TELECOMMANDS 1
//! Can be used to disable the ANSI color sequences for C stdio. //! 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, //! If FSFW_OBJ_EVENT_TRANSLATION is set to one,
//! additional output which requires the translation files translateObjects //! additional output which requires the translation files translateObjects
@ -54,9 +51,9 @@
#define FSFW_USE_REALTIME_FOR_LINUX 1 #define FSFW_USE_REALTIME_FOR_LINUX 1
namespace fsfwconfig { namespace fsfwconfig {
//! Default timestamp size. The default timestamp will be an eight byte CDC
//! short timestamp. //! Default timestamp size. The default timestamp will be an seven byte CDC short timestamp.
static constexpr uint8_t FSFW_MISSION_TIMESTAMP_SIZE = 8; static constexpr uint8_t FSFW_MISSION_TIMESTAMP_SIZE = 7;
//! Configure the allocated pool sizes for the event manager. //! Configure the allocated pool sizes for the event manager.
static constexpr size_t FSFW_EVENTMGMR_MATCHTREE_NODES = 240; 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 //! Defines the FIFO depth of each commanding service base which
//! also determines how many commands a CSB service can handle in one cycle //! 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 ! //! each CSB service !
static constexpr uint8_t FSFW_CSB_FIFO_DEPTH = 6; static constexpr uint8_t FSFW_CSB_FIFO_DEPTH = 6;
static constexpr size_t FSFW_PRINT_BUFFER_SIZE = 124; 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;
} }

View File

@ -0,0 +1,15 @@
#ifndef CONFIG_TMTC_TMTCSIZE_H_
#define CONFIG_TMTC_TMTCSIZE_H_
#include <cstdint>
#include <cstddef>
#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_ */

View File

@ -2,7 +2,7 @@
#define CONFIG_DEVICES_LOGICALADDRESSES_H_ #define CONFIG_DEVICES_LOGICALADDRESSES_H_
#include <fsfw/devicehandlers/CookieIF.h> #include <fsfw/devicehandlers/CookieIF.h>
#include <fsfw/unittest/config/objects/systemObjectList.h>
#include <cstdint> #include <cstdint>
namespace addresses { namespace addresses {

View File

@ -1,8 +1,9 @@
#ifndef CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_ #ifndef CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_
#define CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_ #define CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_
#include "fsfw/events/fwSubsystemIdRanges.h"
#include <cstdint> #include <cstdint>
#include <fsfw/events/fwSubsystemIdRanges.h>
/** /**
* @brief Custom subsystem IDs can be added here * @brief Custom subsystem IDs can be added here
@ -12,6 +13,7 @@
namespace SUBSYSTEM_ID { namespace SUBSYSTEM_ID {
enum: uint8_t { enum: uint8_t {
SUBSYSTEM_ID_START = FW_SUBSYSTEM_ID_RANGE, SUBSYSTEM_ID_START = FW_SUBSYSTEM_ID_RANGE,
SUBSYSTEM_ID_END // [EXPORT] : [END]
}; };
} }

View File

@ -1,8 +1,8 @@
#ifndef HOSTED_CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_ #ifndef HOSTED_CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_
#define HOSTED_CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_ #define HOSTED_CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_
#include "fsfw/objectmanager/frameworkObjects.h"
#include <cstdint> #include <cstdint>
#include <fsfw/objectmanager/frameworkObjects.h>
// The objects will be instantiated in the ID order // The objects will be instantiated in the ID order
namespace objects { namespace objects {
@ -11,10 +11,6 @@ namespace objects {
FSFW_CONFIG_RESERVED_START = PUS_SERVICE_1_VERIFICATION, FSFW_CONFIG_RESERVED_START = PUS_SERVICE_1_VERIFICATION,
FSFW_CONFIG_RESERVED_END = TM_STORE, FSFW_CONFIG_RESERVED_END = TM_STORE,
CCSDS_DISTRIBUTOR = 10,
PUS_DISTRIBUTOR = 11,
TM_FUNNEL = 12,
UDP_BRIDGE = 15, UDP_BRIDGE = 15,
UDP_POLLING_TASK = 16, UDP_POLLING_TASK = 16,

View File

@ -1,6 +1,6 @@
#include "PollingSequenceFactory.h" #include "PollingSequenceFactory.h"
#include <TestsConfig.h> #include "tests/TestsConfig.h"
#include <fsfw/serviceinterface/ServiceInterface.h> #include <fsfw/serviceinterface/ServiceInterface.h>
#include <fsfw/devicehandlers/DeviceHandlerIF.h> #include <fsfw/devicehandlers/DeviceHandlerIF.h>

View File

@ -1,7 +1,7 @@
#ifndef CONFIG_RETURNVALUES_CLASSIDS_H_ #ifndef CONFIG_RETURNVALUES_CLASSIDS_H_
#define CONFIG_RETURNVALUES_CLASSIDS_H_ #define CONFIG_RETURNVALUES_CLASSIDS_H_
#include <fsfw/returnvalues/FwClassIds.h> #include "fsfw/returnvalues/FwClassIds.h"
/** /**
* @brief CLASS_ID defintions which are required for custom returnvalues. * @brief CLASS_ID defintions which are required for custom returnvalues.

View File

@ -1,6 +1,5 @@
#include <fsfw/unittest/catch2/catch.hpp> #include "fsfw_tests/unit/CatchDefinitions.h"
#include <fsfw/unittest/core/CatchDefinitions.h> #include <catch2/catch_test_macros.hpp>
/** /**
* @brief Template test file * @brief Template test file

View File

@ -1,3 +1,3 @@
target_sources(${TARGET_NAME} PRIVATE target_sources(${FSFW_TEST_TGT} PRIVATE
TestCountdown.cpp TestCountdown.cpp
) )

View File

@ -1,3 +1,3 @@
target_sources(${TARGET_NAME} PRIVATE target_sources(${FSFW_TEST_TGT} PRIVATE
PusTmTest.cpp PusTmTest.cpp
) )

View File

@ -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()

View File

@ -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.

View File

@ -1,3 +0,0 @@
#!/bin/bash
lcov --capture --directory . --output-file coverage.info
genhtml coverage.info --output-directory _coverage

View File

@ -1,11 +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}
)

View File

@ -1,8 +0,0 @@
#ifndef TESTCFG_OBSWCONFIG_H_
#define TESTCFG_OBSWCONFIG_H_
#endif /* TESTCFG_OBSWCONFIG_H_ */