Compare commits

..

80 Commits

Author SHA1 Message Date
3d1be94e12 more checks and printouts 2021-09-21 19:27:33 +02:00
8e65d2d3fc refactored GPIO components 2021-09-21 17:31:03 +02:00
8374d495fa Merge remote-tracking branch 'origin/eive/develop' into mueller/master 2021-09-21 16:02:41 +02:00
e02879184b Merge pull request 'added option to open gpio by label instead of gpiochip*' (#15) from meier/gpioOpenByLabel into eive/develop
Reviewed-on: eive/fsfw#15
2021-09-21 16:01:28 +02:00
Jakob Meier
70a3749dbe added option to open gpio by label instead of gpiochip* 2021-09-20 18:38:18 +02:00
87b68e84be Merge branch 'mueller/master' of https://egit.irs.uni-stuttgart.de/eive/fsfw into mueller/master 2021-09-20 18:33:03 +02:00
7000ba05c5 Merge pull request 'Update EIVE FSFW' (#14) from mueller/master into eive/develop
Reviewed-on: eive/fsfw#14
2021-09-20 18:32:47 +02:00
9948c4f31d Merge branch 'eive/develop' into mueller/master 2021-09-20 18:32:28 +02:00
784a0140f4 tweak op divider divisor 2021-09-20 18:31:52 +02:00
635432d7ba missing return 2021-09-20 18:29:57 +02:00
e8050183f4 better printout 2021-09-17 16:52:31 +02:00
7d44aab98e some tweaks for op divider 2021-09-17 13:07:43 +02:00
4489a61a00 Merge pull request 'Update FSFW' (#13) from mueller/update-fsfw into eive/develop
Reviewed-on: eive/fsfw#13
2021-09-17 07:55:57 +02:00
b1a56a71cd Added LIS3MDL to FSFW, per op divider tweak 2021-09-16 18:50:20 +02:00
6d0d04ac23 minor bugfix 2021-09-16 17:33:37 +02:00
8f3edc90ba Merge branch 'eive/develop' into mueller/update-fsfw 2021-09-16 11:43:30 +02:00
7b5334ccec Merge remote-tracking branch 'upstream/development' into eive/develop 2021-09-16 11:40:12 +02:00
a58016859b Merge branch 'develop' into mueller/master 2021-09-16 11:38:56 +02:00
0df8d35802 comment format 2021-09-16 11:36:32 +02:00
823c6ec5fc added RM3100 handler 2021-09-16 11:33:50 +02:00
bc6b29e652 use warning instead of debug 2021-09-15 18:48:09 +02:00
d986ab7720 bugfix for TM packet stored PUS C 2021-09-15 18:37:44 +02:00
bdd7d59d82 reverted some changes 2021-09-15 17:05:52 +02:00
edf33cc10a Merge remote-tracking branch 'upstream/development' into mueller/master 2021-09-15 16:57:42 +02:00
6db5011b14 spi and gyro l3g updates 2021-09-15 16:55:24 +02:00
97494a84df Merge remote-tracking branch 'upstream/development' into mueller/master 2021-09-13 10:59:26 +02:00
40adca5f1d set reply recipient 2021-09-08 17:24:33 +02:00
a8167f5431 added another helper function 2021-09-08 17:02:08 +02:00
41f3d7cf9a better name for function 2021-09-08 16:58:30 +02:00
e6e1936293 Merge remote-tracking branch 'origin/mueller/dhb-periodoc-reply-fix' into mueller/master 2021-09-08 16:21:13 +02:00
15f35f200a Merge remote-tracking branch 'origin/mueller/dhb-bugfixes-improvements' into mueller/master 2021-09-08 16:20:42 +02:00
6b20bb197a Merge branch 'development' into mueller/dhb-bugfixes-improvements 2021-09-08 16:20:04 +02:00
215d01b3ca Merge branch 'mueller/dhb-bugfixes-improvements' into mueller/master 2021-09-08 16:09:03 +02:00
dfe49cc1e5 DHB improvements 2021-09-08 16:08:13 +02:00
73eb11f4f1 bugfixes and improvements 2021-09-08 16:01:46 +02:00
924c150af2 Possible bugfix in DHB
The delayCycles variables needs to be initialized differently
for periodic replies.
It is initialized to the maxDelayCycles value now
2021-09-06 12:05:30 +02:00
469eba3ce2 raised limit 2021-09-06 11:35:14 +02:00
fd2916af11 moved TCP cfg 2021-08-23 09:40:02 +02:00
afd375a7f8 minor fix for canonical read handling 2021-08-22 20:24:50 +02:00
5454169e20 UartComIF: set O_NONBLOCK in canonical mode
When using the non-canonical mode, the file descriptor can be opened
in blocking mode because the VTIME and VMIN termios parameters are
used to configure non-blocking mode. However, in canonical mode, the fd needs to be opened with O_NONBLOCK
2021-08-22 19:48:35 +02:00
7d0377845b printout for unknown command 2021-08-20 15:46:34 +02:00
882da68a2f Merge branch 'mueller/unix-file-guard-fix' into mueller/master 2021-08-19 17:17:03 +02:00
04a1fe7f10 Merge pull request 'Update FSFW' (#12) from mueller/update-fsfw into eive/develop
Reviewed-on: eive/fsfw#12
2021-08-18 12:47:31 +02:00
5f79f987ae Merge branch 'eive/develop' into mueller/update-fsfw 2021-08-18 11:27:02 +02:00
1183e5739d using upstream action helper
Will be merged upstream soon
2021-08-18 11:23:45 +02:00
e3697d6d8c fixed printout 2021-08-17 19:50:01 +02:00
406b77ea81 moved SPI wiretapping cfg 2021-08-17 16:34:25 +02:00
8a9eb27458 Merge pull request 'FSFW Update' (#11) from mueller/master into eive/develop
Reviewed-on: eive/fsfw#11
2021-08-11 13:13:11 +02:00
1ac372cb89 getter function for UDP port 2021-08-09 18:22:22 +02:00
fb36dc4501 More improvements for TCP/UDP port definition 2021-08-09 18:12:25 +02:00
ba5e2ad8bb Cleaning up TCP and UDP code
Same port number used as before, but some inconsistencies fixed
2021-08-09 16:57:24 +02:00
5a6c81130d Merge remote-tracking branch 'upstream/development' into mueller/master 2021-08-09 16:18:38 +02:00
22e29144b6 Merge remote-tracking branch 'origin/eive/develop' into mueller/master 2021-08-09 11:12:37 +02:00
52bff3985f Merge pull request 'set sequence flags in space packet base' (#9) from meier/spacePacketFlag into eive/develop
Reviewed-on: eive/fsfw#9
2021-08-09 11:11:46 +02:00
Jakob.Meier
133820f463 Merge branch 'eive/develop' into meier/spacePacketFlag 2021-08-08 15:32:24 +02:00
Jakob.Meier
8d3fceea8f set sequence flags in space packet base 2021-08-08 15:26:18 +02:00
3704d2b829 bugfix 2021-08-05 18:24:56 +02:00
6073abb12d added some utility to timer module 2021-08-05 18:13:01 +02:00
47bec654a0 Merge pull request 'Update EIVE FSFW' (#8) from mueller/master into eive/develop
Reviewed-on: eive/fsfw#8
2021-08-05 16:35:23 +02:00
b2c102b2c1 Service Interface and Bugfix
1. Service Interface looks better now
2. Bugfix in CommandExecutor blocking mode
2021-08-05 16:13:22 +02:00
4202205182 getter function for current state 2021-08-05 16:02:17 +02:00
c8472beb5f added new command executor 2021-08-05 15:42:47 +02:00
1a4a85ceb2 Merge branch 'mueller/master' of https://egit.irs.uni-stuttgart.de/eive/fsfw into mueller/master 2021-08-03 18:38:27 +02:00
7922bf76da corrected README 2021-08-03 18:38:18 +02:00
bb88490cc6 Merge remote-tracking branch 'upstream/mueller/restructuring' into mueller/master 2021-08-03 15:55:38 +02:00
296c587e3d additional nullptr check 2021-08-03 15:29:22 +02:00
0ff81294e7 Merge branch 'eive/develop' of https://egit.irs.uni-stuttgart.de/eive/fsfw into eive/develop 2021-08-02 12:49:50 +02:00
b6e243b8b3 var name clarification 2021-08-02 12:49:40 +02:00
3bbcc42d39 Merge pull request 'type missmatch' (#7) from mohr-patch-1 into eive/develop
Reviewed-on: eive/fsfw#7
2021-07-26 19:04:06 +02:00
fc4324a2fa type missmatch 2021-07-26 18:35:53 +02:00
54c028f913 naming adaptions 2021-07-26 13:50:45 +02:00
1f6a5e635f naming fixes 2021-07-26 11:46:58 +02:00
c5b4b01362 Merge branch 'mueller/master' into eive/develop 2021-07-24 15:04:01 +02:00
c5420c7b53 bumped subversion 2021-07-24 14:39:43 +02:00
3e422f51bd corrected version number 2021-07-24 13:43:13 +02:00
d1be0f9843 Merge pull request 'Update framework' (#6) from mueller/master into eive/develop
Reviewed-on: eive/fsfw#6
2021-07-23 18:26:39 +02:00
Jakob.Meier
b83259592a uart flush function 2021-07-23 18:06:36 +02:00
a7a4e0f219 api consistent now 2021-07-19 19:03:20 +02:00
bdc5f593e2 Merge remote-tracking branch 'upstream/mueller/master' into mueller/master 2021-07-19 18:50:20 +02:00
10f7185e81 added includes 2021-07-19 13:08:40 +02:00
203 changed files with 1953 additions and 5775 deletions

View File

@@ -1,7 +0,0 @@
---
BasedOnStyle: Google
IndentWidth: 2
---
Language: Cpp
ColumnLimit: 100
---

2
.gitignore vendored
View File

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

View File

@@ -1,12 +1,5 @@
cmake_minimum_required(VERSION 3.13)
set(FSFW_VERSION 2)
set(FSFW_SUBVERSION 0)
set(FSFW_REVISION 0)
# Add the cmake folder so the FindSphinx module is found
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
option(FSFW_GENERATE_SECTIONS
"Generate function and data sections. Required to remove unused code" ON
)
@@ -14,12 +7,6 @@ if(FSFW_GENERATE_SECTIONS)
option(FSFW_REMOVE_UNUSED_CODE "Remove unused code" ON)
endif()
option(FSFW_BUILD_UNITTESTS "Build unittest binary in addition to static library" OFF)
option(FSFW_BUILD_DOCS "Build documentation with Sphinx and Doxygen" OFF)
if(FSFW_BUILD_UNITTESTS)
option(FSFW_TESTS_GEN_COV "Generate coverage data for unittests" ON)
endif()
option(FSFW_WARNING_SHADOW_LOCAL_GCC "Enable -Wshadow=local warning in GCC" ON)
# Options to exclude parts of the FSFW from compilation.
option(FSFW_ADD_INTERNAL_TESTS "Add internal unit tests" ON)
@@ -39,65 +26,11 @@ option(FSFW_ADD_TMSTORAGE "Compile with tm storage components" OFF)
option(FSFW_ADD_SGP4_PROPAGATOR "Add SGP4 propagator code" OFF)
set(LIB_FSFW_NAME fsfw)
set(FSFW_TEST_TGT fsfw-tests)
set(FSFW_DUMMY_TGT fsfw-dummy)
project(${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)
project(${FSFW_TEST_TGT} CXX C)
add_executable(${FSFW_TEST_TGT})
if(FSFW_TESTS_GEN_COV)
message(STATUS "Generating coverage data for the library")
message(STATUS "Targets linking against ${LIB_FSFW_NAME} "
"will be compiled with coverage data as well"
)
include(FetchContent)
FetchContent_Declare(
cmake-modules
GIT_REPOSITORY https://github.com/bilke/cmake-modules.git
)
FetchContent_MakeAvailable(cmake-modules)
set(CMAKE_BUILD_TYPE "Debug")
list(APPEND CMAKE_MODULE_PATH ${cmake-modules_SOURCE_DIR})
include(CodeCoverage)
endif()
endif()
set(FSFW_CORE_INC_PATH "inc")
set_property(CACHE FSFW_OSAL PROPERTY STRINGS host linux rtems freertos)
# Configure Files
target_include_directories(${LIB_FSFW_NAME} PRIVATE
${CMAKE_CURRENT_BINARY_DIR}
)
target_include_directories(${LIB_FSFW_NAME} INTERFACE
${CMAKE_CURRENT_BINARY_DIR}
)
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
@@ -130,37 +63,29 @@ endif()
set(FSFW_OSAL_DEFINITION FSFW_OSAL_HOST)
if(FSFW_OSAL MATCHES host)
set(FSFW_OS_NAME "Host")
set(OS_FSFW_NAME "Host")
set(FSFW_OSAL_HOST ON)
elseif(FSFW_OSAL MATCHES linux)
set(FSFW_OS_NAME "Linux")
set(OS_FSFW_NAME "Linux")
set(FSFW_OSAL_LINUX ON)
elseif(FSFW_OSAL MATCHES freertos)
set(FSFW_OS_NAME "FreeRTOS")
set(OS_FSFW_NAME "FreeRTOS")
set(FSFW_OSAL_FREERTOS ON)
target_link_libraries(${LIB_FSFW_NAME} PRIVATE
${LIB_OS_NAME}
)
elseif(FSFW_OSAL STREQUAL rtems)
set(FSFW_OS_NAME "RTEMS")
set(OS_FSFW_NAME "RTEMS")
set(FSFW_OSAL_RTEMS ON)
else()
message(WARNING
"Invalid operating system for FSFW specified! Setting to host.."
)
set(FSFW_OS_NAME "Host")
set(OS_FSFW_NAME "Host")
set(OS_FSFW "host")
endif()
if(FSFW_BUILD_UNITTESTS OR FSFW_BUILD_DOCS)
configure_file(src/fsfw/FSFW.h.in fsfw/FSFW.h)
configure_file(src/fsfw/FSFWVersion.h.in fsfw/FSFWVersion.h)
else()
configure_file(src/fsfw/FSFW.h.in FSFW.h)
configure_file(src/fsfw/FSFWVersion.h.in FSFWVersion.h)
endif()
message(STATUS "Compiling FSFW for the ${FSFW_OS_NAME} operating system.")
message(STATUS "Compiling FSFW for the ${OS_FSFW_NAME} operating system.")
add_subdirectory(src)
add_subdirectory(tests)
@@ -168,87 +93,13 @@ if(FSFW_ADD_HAL)
add_subdirectory(hal)
endif()
add_subdirectory(contrib)
if(FSFW_BUILD_DOCS)
add_subdirectory(docs)
endif()
if(FSFW_BUILD_UNITTESTS)
if(FSFW_TESTS_GEN_COV)
if(CMAKE_COMPILER_IS_GNUCXX)
include(CodeCoverage)
# Remove quotes.
separate_arguments(COVERAGE_COMPILER_FLAGS
NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}"
)
# Add compile options manually, we don't want coverage for Catch2
target_compile_options(${FSFW_TEST_TGT} PRIVATE
"${COVERAGE_COMPILER_FLAGS}"
)
target_compile_options(${LIB_FSFW_NAME} PRIVATE
"${COVERAGE_COMPILER_FLAGS}"
)
# Exclude directories here
if(WIN32)
set(GCOVR_ADDITIONAL_ARGS
"--exclude-throw-branches"
"--exclude-unreachable-branches"
)
set(COVERAGE_EXCLUDES
"/c/msys64/mingw64/*"
)
elseif(UNIX)
set(COVERAGE_EXCLUDES
"/usr/include/*" "/usr/bin/*" "Catch2/*"
"/usr/local/include/*" "*/fsfw_tests/*"
"*/catch2-src/*"
)
endif()
target_link_options(${FSFW_TEST_TGT} PRIVATE
-fprofile-arcs
-ftest-coverage
)
target_link_options(${LIB_FSFW_NAME} PRIVATE
-fprofile-arcs
-ftest-coverage
)
# Need to specify this as an interface, otherwise there will the compile issues
target_link_options(${LIB_FSFW_NAME} INTERFACE
-fprofile-arcs
-ftest-coverage
)
if(WIN32)
setup_target_for_coverage_gcovr_html(
NAME ${FSFW_TEST_TGT}_coverage
EXECUTABLE ${FSFW_TEST_TGT}
DEPENDENCIES ${FSFW_TEST_TGT}
)
else()
setup_target_for_coverage_lcov(
NAME ${FSFW_TEST_TGT}_coverage
EXECUTABLE ${FSFW_TEST_TGT}
DEPENDENCIES ${FSFW_TEST_TGT}
)
endif()
endif()
endif()
target_link_libraries(${FSFW_TEST_TGT} PRIVATE Catch2::Catch2 ${LIB_FSFW_NAME})
endif()
# The project CMakeLists file has to set the FSFW_CONFIG_PATH and add it.
# If this is not given, we include the default configuration and emit a warning.
if(NOT FSFW_CONFIG_PATH)
set(DEF_CONF_PATH misc/defaultcfg/fsfwconfig)
if(NOT FSFW_BUILD_DOCS)
message(WARNING "Flight Software Framework configuration path not set!")
message(WARNING "Setting default configuration from ${DEF_CONF_PATH} ..")
endif()
add_subdirectory(${DEF_CONF_PATH})
set(FSFW_CONFIG_PATH ${DEF_CONF_PATH})
message(WARNING "Flight Software Framework configuration path not set!")
message(WARNING "Setting default configuration!")
add_subdirectory(defaultcfg/fsfwconfig)
endif()
# FSFW might be part of a possibly complicated folder structure, so we
@@ -335,17 +186,4 @@ target_compile_options(${LIB_FSFW_NAME} PRIVATE
target_link_libraries(${LIB_FSFW_NAME} PRIVATE
${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}
)
)

105
README.md
View File

@@ -22,107 +22,28 @@ Currently, the FSFW provides the following OSALs:
- FreeRTOS
- RTEMS
The recommended hardware is a microprocessor with more than 1 MB of RAM and 1 MB of non-volatile
memory. For reference, current applications use a Cobham Gaisler UT699 (LEON3FT), a
ISISPACE IOBC or a Zynq-7020 SoC. The `fsfw` was also successfully run on the
STM32H743ZI-Nucleo board and on a Raspberry Pi and is currently running on the active
satellite mission Flying Laptop.
The recommended hardware is a microprocessor with more than 1 MB of RAM and 1 MB of non-volatile Memory. For reference, current applications use a Cobham Gaisler UT699 (LEON3FT), a ISISPACE IOBC or a Zynq-7020 SoC. The `fsfw` was also successfully run on the STM32H743ZI-Nucleo board and on a Raspberry Pi and is currently running on the active satellite mission Flying Laptop.
## Getting started
The [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.
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.
Generally, the FSFW is included in a project by providing
a configuration folder, building the static library and linking against it.
There are some functions like `printChar` which are different depending on the target architecture
and need to be implemented by the mission developer.
Generally, the FSFW is included in a project by compiling the FSFW sources and providing
a configuration folder and adding it to the include path. There are some functions like `printChar` which are different depending on the target architecture and need to be implemented by the mission developer.
A template configuration folder was provided and can be copied into the project root to have
a starting point. The [configuration section](docs/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.
## Formatting the sources
The formatting is done by the `clang-format` tool. The configuration is contained within the
`.clang-format` file in the repository root. As long as `clang-format` is installed, you
can run the `apply-clang-format.sh` helper script to format all source files consistently.
a starting point. The [configuration section](doc/README-config.md#top) provides more specific information about the possible options.
## Index
[1. High-level overview](docs/README-highlevel.md#top) <br>
[2. Core components](docs/README-core.md#top) <br>
[3. Configuration](docs/README-config.md#top) <br>
[4. OSAL overview](docs/README-osal.md#top) <br>
[5. PUS services](docs/README-pus.md#top) <br>
[6. Device Handler overview](docs/README-devicehandlers.md#top) <br>
[7. Controller overview](docs/README-controllers.md#top) <br>
[8. Local Data Pools](docs/README-localpools.md#top) <br>
[1. High-level overview](doc/README-highlevel.md#top) <br>
[2. Core components](doc/README-core.md#top) <br>
[3. Configuration](doc/README-config.md#top) <br>
[4. OSAL overview](doc/README-osal.md#top) <br>
[5. PUS services](doc/README-pus.md#top) <br>
[6. Device Handler overview](doc/README-devicehandlers.md#top) <br>
[7. Controller overview](doc/README-controllers.md#top) <br>
[8. Local Data Pools](doc/README-localpools.md#top) <br>

View File

@@ -1,8 +0,0 @@
FROM ubuntu:focal
RUN apt-get update
RUN apt-get --yes upgrade
#tzdata is a dependency, won't install otherwise
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get --yes install gcc g++ cmake make lcov git valgrind nano

View File

@@ -1,72 +0,0 @@
pipeline {
agent any
environment {
BUILDDIR = 'build-unittests'
}
stages {
stage('Create Docker') {
agent {
dockerfile {
dir 'automation'
additionalBuildArgs '--no-cache'
reuseNode true
}
}
steps {
sh 'rm -rf $BUILDDIR'
}
}
stage('Configure') {
agent {
dockerfile {
dir 'automation'
reuseNode true
}
}
steps {
dir(BUILDDIR) {
sh 'cmake -DFSFW_OSAL=host -DFSFW_BUILD_UNITTESTS=ON ..'
}
}
}
stage('Build') {
agent {
dockerfile {
dir 'automation'
reuseNode true
}
}
steps {
dir(BUILDDIR) {
sh 'cmake --build . -j'
}
}
}
stage('Unittests') {
agent {
dockerfile {
dir 'automation'
reuseNode true
}
}
steps {
dir(BUILDDIR) {
sh 'cmake --build . -- fsfw-tests_coverage -j'
}
}
}
stage('Valgrind') {
agent {
dockerfile {
dir 'automation'
reuseNode true
}
}
steps {
dir(BUILDDIR) {
sh 'valgrind --leak-check=full --error-exitcode=1 ./fsfw-tests'
}
}
}
}
}

View File

@@ -1,13 +0,0 @@
# Look for an executable called sphinx-build
find_program(SPHINX_EXECUTABLE
NAMES sphinx-build
DOC "Path to sphinx-build executable")
include(FindPackageHandleStandardArgs)
# Handle standard arguments to find_package like REQUIRED and QUIET
find_package_handle_standard_args(
Sphinx
"Failed to find sphinx-build executable"
SPHINX_EXECUTABLE
)

View File

@@ -31,9 +31,7 @@ cohesive pool variables. These sets simply iterator over the list of variables a
`read` and `commit` functions of each variable. The following diagram shows the
high-level architecture of the local data pools.
.. image:: ../misc/logo/FSFW_Logo_V3_bw.png
:alt: FSFW Logo
<img align="center" src="./images/PoolArchitecture.png" width="50%"> <br>
An example is shown for using the local data pools with a Gyroscope.
For example, the following code shows an implementation to access data from a Gyroscope taken

View File

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

1
docs/.gitignore vendored
View File

@@ -1 +0,0 @@
/_build

View File

@@ -1,66 +0,0 @@
# This is based on this excellent posting provided by Sy:
# https://devblogs.microsoft.com/cppblog/clear-functional-c-documentation-with-sphinx-breathe-doxygen-cmake/
find_package(Doxygen REQUIRED)
find_package(Sphinx REQUIRED)
get_target_property(LIB_FSFW_PUBLIC_HEADER_DIRS ${LIB_FSFW_NAME} INTERFACE_INCLUDE_DIRECTORIES)
# TODO: Add HAL as well
file(GLOB_RECURSE LIB_FSFW_PUBLIC_HEADERS ${PROJECT_SOURCE_DIR}/src/*.h)
file(GLOB_RECURSE RST_DOC_FILES ${PROJECT_SOURCE_DIR}/docs/*.rst)
set(DOXYGEN_INPUT_DIR ${PROJECT_SOURCE_DIR}/src)
set(DOXYGEN_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/doxygen)
set(DOXYGEN_INDEX_FILE ${DOXYGEN_OUTPUT_DIR}/xml/index.xml)
set(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in)
set(DOXYFILE_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
# Replace variables inside @@ with the current values
configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT} @ONLY)
# Doxygen won't create this for us
file(MAKE_DIRECTORY ${DOXYGEN_OUTPUT_DIR})
# Only regenerate Doxygen when the Doxyfile or public headers change
add_custom_command(
OUTPUT ${DOXYGEN_INDEX_FILE}
DEPENDS ${LIB_FSFW_PUBLIC_HEADERS}
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYFILE_OUT}
MAIN_DEPENDENCY ${DOXYFILE_OUT} ${DOXYFILE_IN}
COMMENT "Generating docs"
VERBATIM
)
# Nice named target so we can run the job easily
add_custom_target(Doxygen ALL DEPENDS ${DOXYGEN_INDEX_FILE})
set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR})
set(SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR}/sphinx)
set(SPHINX_INDEX_FILE ${SPHINX_BUILD}/index.html)
# Only regenerate Sphinx when:
# - Doxygen has rerun
# - Our doc files have been updated
# - The Sphinx config has been updated
add_custom_command(
OUTPUT ${SPHINX_INDEX_FILE}
COMMAND
${SPHINX_EXECUTABLE} -b html
# Tell Breathe where to find the Doxygen output
-Dbreathe_projects.fsfw=${DOXYGEN_OUTPUT_DIR}/xml
${SPHINX_SOURCE} ${SPHINX_BUILD}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS
# Other docs files you want to track should go here (or in some variable)
${RST_DOC_FILES}
${DOXYGEN_INDEX_FILE}
MAIN_DEPENDENCY ${SPHINX_SOURCE}/conf.py
COMMENT "Generating documentation with Sphinx"
)
# Nice named target so we can run the job easily
add_custom_target(Sphinx ALL DEPENDS ${SPHINX_INDEX_FILE})
# Add an install target to install the docs
include(GNUInstallDirs)
install(DIRECTORY ${SPHINX_BUILD}
DESTINATION ${CMAKE_INSTALL_DOCDIR})

View File

@@ -1,7 +0,0 @@
INPUT = "@DOXYGEN_INPUT_DIR@"
RECURSIVE = YES
OUTPUT_DIRECTORY = "@DOXYGEN_OUTPUT_DIR@"
GENERATE_XML = YES

View File

@@ -1,20 +0,0 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View File

@@ -1,16 +0,0 @@
API
====
.. toctree::
:maxdepth: 4
api/objectmanager
api/task
api/ipc
api/returnvalue
api/event
api/modes
api/health
api/action
api/devicehandler
api/controller

View File

@@ -1,15 +0,0 @@
Action Module API
=================
``ActionHelper``
-----------------
.. doxygenclass:: ActionHelper
:members:
``HasActionsIF``
-----------------
.. doxygenclass:: HasActionsIF
:members:
:protected-members:

View File

@@ -1,16 +0,0 @@
Controller API
=================
``ControllerBase``
-------------------------
.. doxygenclass:: ControllerBase
:members:
:protected-members:
``ExtendedControllerBase``
-----------------------------
.. doxygenclass:: ExtendedControllerBase
:members:
:protected-members:

View File

@@ -1,16 +0,0 @@
Device Handler Base API
=========================
``DeviceHandlerBase``
-----------------------
.. doxygenclass:: DeviceHandlerBase
:members:
:protected-members:
``DeviceHandlerIF``
-----------------------
.. doxygenclass:: DeviceHandlerIF
:members:
:protected-members:

View File

@@ -1,6 +0,0 @@
.. _eventapi:
Event API
============
.. doxygenfile:: Event.h

View File

@@ -1,9 +0,0 @@
Health API
===========
``HasHealthIF``
------------------
.. doxygenclass:: HasHealthIF
:members:
:protected-members:

View File

@@ -1,9 +0,0 @@
IPC Module API
=================
``MessageQueueIF``
-------------------
.. doxygenclass:: MessageQueueIF
:members:
:protected-members:

View File

@@ -1,10 +0,0 @@
Modes API
=========
``HasModesIF``
---------------
.. doxygenclass:: HasModesIF
:members:
:protected-members:

View File

@@ -1,30 +0,0 @@
Object Manager API
=========================
``SystemObject``
--------------------
.. doxygenclass:: SystemObject
:members:
:protected-members:
``ObjectManager``
-----------------------
.. doxygenclass:: ObjectManager
:members:
:protected-members:
``SystemObjectIF``
--------------------
.. doxygenclass:: SystemObjectIF
:members:
:protected-members:
``ObjectManagerIF``
-----------------------
.. doxygenclass:: ObjectManagerIF
:members:
:protected-members:

View File

@@ -1,10 +0,0 @@
.. _retvalapi:
Returnvalue API
==================
.. doxygenfile:: HasReturnvaluesIF.h
.. _fwclassids:
.. doxygenfile:: FwClassIds.h

View File

@@ -1,8 +0,0 @@
Task API
=========
``ExecutableObjectIF``
-----------------------
.. doxygenclass:: ExecutableObjectIF
:members:

View File

@@ -1,56 +0,0 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'Flight Software Framework'
copyright = '2021, Institute of Space Systems (IRS)'
author = 'Institute of Space Systems (IRS)'
# The full version, including alpha/beta/rc tags
release = '2.0.1'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [ "breathe" ]
breathe_default_project = "fsfw"
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = []

View File

@@ -1,41 +0,0 @@
Configuring the FSFW
=====================
The FSFW can be configured via the ``fsfwconfig`` folder. A template folder has been provided in
``misc/defaultcfg`` to have a starting point for this. The folder should be added
to the include path. The primary configuration file is the ``FSFWConfig.h`` folder. Some
of the available options will be explained in more detail here.
Auto-Translation of Events
----------------------------
The FSFW allows the automatic translation of events, which allows developers to track triggered
events directly via console output. Using this feature requires:
1. ``FSFW_OBJ_EVENT_TRANSLATION`` set to 1 in the configuration file.
2. Special auto-generated translation files which translate event IDs and object IDs into
human readable strings. These files can be generated using the
`fsfwgen Python scripts <https://egit.irs.uni-stuttgart.de/fsfw/fsfw-gen>`_.
3. The generated translation files for the object IDs should be named ``translatesObjects.cpp``
and ``translateObjects.h`` and should be copied to the ``fsfwconfig/objects`` folder
4. The generated translation files for the event IDs should be named ``translateEvents.cpp`` and
``translateEvents.h`` and should be copied to the ``fsfwconfig/events`` folder
An example implementations of these translation file generators can be found as part
of the `SOURCE project here <https://git.ksat-stuttgart.de/source/sourceobsw/-/tree/develop/generators>`_
or the `FSFW example <https://egit.irs.uni-stuttgart.de/fsfw/fsfw-example-hosted/src/branch/master/generators>`_
Configuring the Event Manager
----------------------------------
The number of allowed subscriptions can be modified with the following
parameters:
.. code-block:: cpp
namespace fsfwconfig {
//! Configure the allocated pool sizes for the event manager.
static constexpr size_t FSFW_EVENTMGMR_MATCHTREE_NODES = 240;
static constexpr size_t FSFW_EVENTMGMT_EVENTIDMATCHERS = 120;
static constexpr size_t FSFW_EVENTMGMR_RANGEMATCHERS = 120;
}

View File

@@ -1,2 +0,0 @@
Controllers
=============

View File

@@ -1,70 +0,0 @@
.. _core:
Core Modules
=============
The core modules provide the most important functionalities of the Flight Software Framework.
Clock
------
- This is a class of static functions that can be used at anytime
- Leap Seconds must be set if any time conversions from UTC to other times is used
Object Manager
---------------
- Must be created during program startup
- The component which handles all references. All :cpp:class:`SystemObject`\s register at this
component.
- All :cpp:class:`SystemObject`\s needs to have a unique Object ID. Those can be managed like
framework objects.
- A reference to an object can be retrieved by calling the ``get`` function of
:cpp:class:`ObjectManagerIF`. The target type must be specified as a template argument.
A ``nullptr`` check of the returning pointer must be done. This function is based on
run-time type information.
.. code-block:: cpp
template <typename T> T* ObjectManagerIF::get(object_id_t id);
- A typical way to create all objects on startup is a handing a static produce function to the
ObjectManager on creation. By calling ``ObjectManager::instance()->initialize(produceFunc)`` the
produce function will be called and all :cpp:class:`SystemObject`\s will be initialized
afterwards.
Event Manager
---------------
- Component which allows routing of events
- Other objects can subscribe to specific events, ranges of events or all events of an object.
- Subscriptions can be done during runtime but should be done during initialization
- Amounts of allowed subscriptions can be configured in ``FSFWConfig.h``
Health Table
---------------
- A component which holds every health state
- Provides a thread safe way to access all health states without the need of message exchanges
Stores
--------------
- The message based communication can only exchange a few bytes of information inside the message
itself. Therefore, additional information can be exchanged with Stores. With this, only the
store address must be exchanged in the message.
- Internally, the FSFW uses an IPC Store to exchange data between processes. For incoming TCs a TC
Store is used. For outgoing TM a TM store is used.
- All of them should use the Thread Safe Class storagemanager/PoolManager
Tasks
---------
There are two different types of tasks:
- The PeriodicTask just executes objects that are of type ExecutableObjectIF in the order of the
insertion to the Tasks.
- FixedTimeslotTask executes a list of calls in the order of the given list. This is intended for
DeviceHandlers, where polling should be in a defined order. An example can be found in
``defaultcfg/fsfwconfig/pollingSequence`` folder

View File

@@ -1,3 +0,0 @@
Device Handlers
==================

View File

@@ -1,115 +0,0 @@
Getting Started
================
Getting started
----------------
The `Hosted FSFW example`_ 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 providing
a configuration folder, building the static library and linking against it.
There are some functions like ``printChar`` which are different depending on the target architecture
and need to be implemented by the mission developer.
A template configuration folder was provided and can be copied into the project root to have
a starting point. The [configuration section](docs/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
.. code-block:: console
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
.. code-block:: 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
.. code-block:: 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`_.
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
.. code-block:: console
mkdir build-tests && cd build-tests
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 `Code coverage`_ CMake module.
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
.. code-block:: console
cmake --build . -- fsfw-tests_coverage -j
The ``helper.py`` script located in the ``script`` folder can also be used to create, build
and open the unittests conveniently. Try ``helper.py -h`` for more information.
Building the documentation
----------------------------
The FSFW documentation is built using the tools Sphinx, doxygen and breathe based on the
instructions provided in `this blogpost <https://devblogs.microsoft.com/cppblog/clear-functional-c-documentation-with-sphinx-breathe-doxygen-cmake/>`_. You can set up a
documentation build system using the following commands
.. code-block:: bash
mkdir build-docs && cd build-docs
cmake -DFSFW_BUILD_DOCS=ON -DFSFW_OSAL=host ..
Then you can generate the documentation using
.. code-block:: bash
cmake --build . -j
You can find the generated documentation inside the ``docs/sphinx`` folder inside the build
folder. Simply open the ``index.html`` in the webbrowser of your choice.
The ``helper.py`` script located in the ``script`` folder can also be used to create, build
and open the documentation conveniently. Try ``helper.py -h`` for more information.
.. _`Hosted FSFW example`: https://egit.irs.uni-stuttgart.de/fsfw/fsfw-example-hosted
.. _`Catch2 library`: https://github.com/catchorg/Catch2
.. _`Code coverage`: https://github.com/bilke/cmake-modules/tree/master

View File

@@ -1,149 +0,0 @@
.. _highlevel:
High-level overview
===================
Structure
----------
The general structure is driven by the usage of interfaces provided by objects.
The FSFW uses C++11 as baseline. The intention behind this is that this C++ Standard should be
widely available, even with older compilers.
The FSFW uses dynamic allocation during the initialization but provides static containers during runtime.
This simplifies the instantiation of objects and allows the usage of some standard containers.
Dynamic Allocation after initialization is discouraged and different solutions are provided in the
FSFW to achieve that. The fsfw uses run-time type information but exceptions are not allowed.
Failure Handling
-----------------
Functions should return a defined :cpp:type:`ReturnValue_t` to signal to the caller that something has
gone wrong. Returnvalues must be unique. For this the function :cpp:func:`HasReturnvaluesIF::makeReturnCode`
or the :ref:`macro MAKE_RETURN_CODE <retvalapi>` can be used. The ``CLASS_ID`` is a unique ID for that type of object.
See the :ref:`FSFW Class IDs file <fwclassids>`. The user can add custom ``CLASS_ID``\s via the
``fsfwconfig`` folder.
OSAL
------------
The FSFW provides operation system abstraction layers for Linux, FreeRTOS and RTEMS.
The OSAL provides periodic tasks, message queues, clocks and semaphores as well as mutexes.
The :ref:`OSAL README <osal>` provides more detailed information on provided components
and how to use them.
Core Components
----------------
The FSFW has following core components. More detailed informations can be found in the
:ref:`core component section <core>`:
1. Tasks: Abstraction for different (periodic) task types like periodic tasks or tasks
with fixed timeslots
2. ObjectManager: This module stores all `SystemObjects` by mapping a provided unique object ID
to the object handles.
3. Static Stores: Different stores are provided to store data of variable size (like telecommands
or small telemetry) in a pool structure without using dynamic memory allocation.
These pools are allocated up front.
4. Clock: This module provided common time related functions
5. EventManager: This module allows routing of events generated by `SystemObjects`
6. HealthTable: A component which stores the health states of objects
Static IDs in the framework
--------------------------------
Some parts of the framework use a static routing address for communication.
An example setup of IDs can be found in the example config in ``misc/defaultcfg/fsfwconfig/objects``
inside the function ``Factory::setStaticFrameworkObjectIds``.
Events
----------------
Events are tied to objects. EventIds can be generated by calling the
:ref:`macro MAKE_EVENT <eventapi>` or the function :cpp:func:`event::makeEvent`.
This works analog to the returnvalues. Every object that needs own Event IDs has to get a
unique ``SUBSYSTEM_ID``. Every :cpp:class:`SystemObject` can call
:cpp:func:`SystemObject::triggerEvent` from the parent class.
Therefore, event messages contain the specific EventId and the objectId of the object that
has triggered.
Internal Communication
-------------------------
Components communicate mostly via Messages through Queues.
Those queues are created by calling the singleton ``QueueFactory::instance()->create`` which
will create `MessageQueue` instances for the used OSAL.
External Communication
--------------------------
The external communication with the mission control system is mostly up to the user implementation.
The FSFW provides PUS Services which can be used to but don't need to be used.
The services can be seen as a conversion from a TC to a message based communication and back.
TMTC Communication
~~~~~~~~~~~~~~~~~~~
The FSFW provides some components to facilitate TMTC handling via the PUS commands.
For example, a UDP or TCP PUS server socket can be opened on a specific port using the
files located in ``osal/common``. The FSFW example uses this functionality to allow sending
telecommands and receiving telemetry using the
`TMTC commander application <https://github.com/robamu-org/tmtccmd>`_.
Simple commands like the PUS Service 17 ping service can be tested by simply running the
``tmtc_client_cli.py`` or ``tmtc_client_gui.py`` utility in
the `example tmtc folder <https://egit.irs.uni-stuttgart.de/fsfw/fsfw_example_public/src/branch/master/tmtc>`_
while the `fsfw_example` application is running.
More generally, any class responsible for handling incoming telecommands and sending telemetry
can implement the generic ``TmTcBridge`` class located in ``tmtcservices``. Many applications
also use a dedicated polling task for reading telecommands which passes telecommands
to the ``TmTcBridge`` implementation.
CCSDS Frames, CCSDS Space Packets and PUS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If the communication is based on CCSDS Frames and Space Packets, several classes can be used to
distributed the packets to the corresponding services. Those can be found in ``tcdistribution``.
If Space Packets are used, a timestamper has to be provided by the user.
An example can be found in the ``timemanager`` folder, which uses ``CCSDSTime::CDS_short``.
Device Handlers
--------------------------
DeviceHandlers are another important component of the FSFW. The idea is, to have a software
counterpart of every physical device to provide a simple mode, health and commanding interface.
By separating the underlying Communication Interface with
``DeviceCommunicationIF``, a device handler (DH) can be tested on different hardware.
The DH has mechanisms to monitor the communication with the physical device which allow
for FDIR reaction. Device Handlers can be created by implementing ``DeviceHandlerBase``.
A standard FDIR component for the DH will be created automatically but can
be overwritten by the user. More information on DeviceHandlers can be found in the
related [documentation section](doc/README-devicehandlers.md#top).
Modes and Health
--------------------
The two interfaces ``HasModesIF`` and ``HasHealthIF`` provide access for commanding and monitoring
of components. On-board mode management is implement in hierarchy system.
- Device handlers and controllers are the lowest part of the hierarchy.
- The next layer are assemblies. Those assemblies act as a component which handle
redundancies of handlers. Assemblies share a common core with the top level subsystem components
- The top level subsystem components are used to group assemblies, controllers and device handlers.
For example, a spacecraft can have a atttitude control subsystem and a power subsystem.
Those assemblies are intended to act as auto-generated components from a database which describes
the subsystem modes. The definitions contain transition and target tables which contain the DH,
Assembly and Controller Modes to be commanded.
Transition tables contain as many steps as needed to reach the mode from any other mode, e.g. a
switch into any higher AOCS mode might first turn on the sensors, than the actuators and the
controller as last component.
The target table is used to describe the state that is checked continuously by the subsystem.
All of this allows System Modes to be generated as Subsystem object as well from the same database.
This System contains list of subsystem modes in the transition and target tables.
Therefore, it allows a modular system to create system modes and easy commanding of those, because
only the highest components must be commanded.
The health state represents if the component is able to perform its tasks.
This can be used to signal the system to avoid using this component instead of a redundant one.
The on-board FDIR uses the health state for isolation and recovery.

View File

@@ -1,69 +0,0 @@
.. Flight Software Framework documentation master file, created by
sphinx-quickstart on Tue Nov 30 10:56:03 2021.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Flight Software Framework (FSFW) documentation
================================================
.. image:: ../misc/logo/FSFW_Logo_V3_bw.png
:alt: FSFW Logo
The Flight Software Framework is a C++ Object Oriented Framework for unmanned,
automated systems like Satellites.
The initial version of the Flight Software Framework was developed during
the Flying Laptop Project by the University of Stuttgart in cooperation
with Airbus Defence and Space GmbH.
Quick facts
---------------
The framework is designed for systems, which communicate with external devices, perform control
loops, receive telecommands and send telemetry, and need to maintain a high level of availability.
Therefore, a mode and health system provides control over the states of the software and the
controlled devices. In addition, a simple mechanism of event based fault detection, isolation and
recovery is implemented as well.
The FSFW provides abstraction layers for operating systems to provide a uniform operating system
abstraction layer (OSAL). Some components of this OSAL are required internally by the FSFW but is
also very useful for developers to implement the same application logic on different operating
systems with a uniform interface.
Currently, the FSFW provides the following OSALs:
- Linux
- Host
- FreeRTOS
- RTEMS
The recommended hardware is a microprocessor with more than 1 MB of RAM and 1 MB of non-volatile
memory. For reference, current applications use a Cobham Gaisler UT699 (LEON3FT), a
ISISPACE IOBC or a Zynq-7020 SoC. The ``fsfw`` was also successfully run on the
STM32H743ZI-Nucleo board and on a Raspberry Pi and is currently running on the active
satellite mission Flying Laptop.
Index
-------
.. toctree::
:maxdepth: 2
:caption: Contents:
getting_started
highlevel
core
config
osal
pus
devicehandlers
controllers
localpools
api
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@@ -1,181 +0,0 @@
Local Data Pools
=========================================
The following text is targeted towards mission software developers which would like
to use the local data pools provided by the FSFW to store data like sensor values so they can be
used by other software objects like controllers as well. If a custom class should have a local
pool which can be used by other software objects as well, following steps have to be performed:
1. Create a ``LocalDataPoolManager`` member object in the custom class
2. Implement the ``HasLocalDataPoolIF`` with specifies the interface between the local pool
manager and the class owning the local pool.
The local data pool manager is also able to process housekeeping service requests in form
of messages, generate periodic housekeeping packet, generate notification and snapshots of changed
variables and datasets and process notifications and snapshots coming from other objects.
The two former tasks are related to the external interface using telemetry and telecommands (TMTC)
while the later two are related to data consumers like controllers only acting on data change
detected by the data creator instead of checking the data manually each cycle. Two important
framework classes ``DeviceHandlerBase`` and ``ExtendedControllerBase`` already perform the two steps
shown above so the steps required are altered slightly.
Storing and Accessing pool data
-------------------------------------
The pool manager is responsible for thread-safe access of the pool data, but the actual
access to the pool data from the point of view of a mission software developer happens via proxy
classes like pool variable classes. These classes store a copy
of the pool variable with the matching datatype and copy the actual data from the local pool
on a ``read`` call. Changed variables can then be written to the local pool with a ``commit`` call.
The ``read`` and ``commit`` calls are thread-safe and can be called concurrently from data creators
and data consumers. Generally, a user will create a dataset class which in turn groups all
cohesive pool variables. These sets simply iterator over the list of variables and call the
``read`` and ``commit`` functions of each variable. The following diagram shows the
high-level architecture of the local data pools.
.. image:: ../docs/images/PoolArchitecture.png
:alt: Pool Architecture
An example is shown for using the local data pools with a Gyroscope.
For example, the following code shows an implementation to access data from a Gyroscope taken
from the SOURCE CubeSat project:
.. code-block:: cpp
class GyroPrimaryDataset: public StaticLocalDataSet<3 * sizeof(float)> {
public:
/**
* Constructor for data users
* @param gyroId
*/
GyroPrimaryDataset(object_id_t gyroId):
StaticLocalDataSet(sid_t(gyroId, gyrodefs::GYRO_DATA_SET_ID)) {
setAllVariablesReadOnly();
}
lp_var_t<float> angVelocityX = lp_var_t<float>(sid.objectId,
gyrodefs::ANGULAR_VELOCITY_X, this);
lp_var_t<float> angVelocityY = lp_var_t<float>(sid.objectId,
gyrodefs::ANGULAR_VELOCITY_Y, this);
lp_var_t<float> angVelocityZ = lp_var_t<float>(sid.objectId,
gyrodefs::ANGULAR_VELOCITY_Z, this);
private:
friend class GyroHandler;
/**
* Constructor for data creator
* @param hkOwner
*/
GyroPrimaryDataset(HasLocalDataPoolIF* hkOwner):
StaticLocalDataSet(hkOwner, gyrodefs::GYRO_DATA_SET_ID) {}
};
There is a public constructor for users which sets all variables to read-only and there is a
constructor for the GyroHandler data creator by marking it private and declaring the ``GyroHandler``
as a friend class. Both the atittude controller and the ``GyroHandler`` can now
use the same class definition to access the pool variables with ``read`` and ``commit`` semantics
in a thread-safe way. Generally, each class requiring access will have the set class as a member
class. The data creator will also be generally a ``DeviceHandlerBase`` subclass and some additional
steps are necessary to expose the set for housekeeping purposes.
Using the local data pools in a ``DeviceHandlerBase`` subclass
--------------------------------------------------------------
It is very common to store data generated by devices like a sensor into a pool which can
then be used by other objects. Therefore, the ``DeviceHandlerBase`` already has a
local pool. Using the aforementioned example, the ``GyroHandler`` will now have the set class
as a member:
.. code-block:: cpp
class GyroHandler: ... {
public:
...
private:
...
GyroPrimaryDataset gyroData;
...
};
The constructor used for the creators expects the owner class as a parameter, so we initialize
the object in the `GyroHandler` constructor like this:
.. code-block:: cpp
GyroHandler::GyroHandler(object_id_t objectId, object_id_t comIF,
CookieIF *comCookie, uint8_t switchId):
DeviceHandlerBase(objectId, comIF, comCookie), switchId(switchId),
gyroData(this) {}
We need to assign the set to a reply ID used in the ``DeviceHandlerBase``.
The combination of the ``GyroHandler`` object ID and the reply ID will be the 64-bit structure ID
``sid_t`` and is used to globally identify the set, for example when requesting housekeeping data or
generating update messages. We need to assign our custom set class in some way so that the local
pool manager can access the custom data sets as well.
By default, the ``getDataSetHandle`` will take care of this tasks. The default implementation for a
``DeviceHandlerBase`` subclass will use the internal command map to retrieve
a handle to a dataset from a given reply ID. Therefore,
we assign the set in the ``fillCommandAndReplyMap`` function:
.. code-block:: cpp
void GyroHandler::fillCommandAndReplyMap() {
...
this->insertInCommandAndReplyMap(gyrodefs::GYRO_DATA, 3, &gyroData);
...
}
Now, we need to create the actual pool entries as well, using the ``initializeLocalDataPool``
function. Here, we also immediately subscribe for periodic housekeeping packets
with an interval of 4 seconds. They are still disabled in this example and can be enabled
with a housekeeping service command.
.. code-block:: cpp
ReturnValue_t GyroHandler::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
LocalDataPoolManager &poolManager) {
localDataPoolMap.emplace(gyrodefs::ANGULAR_VELOCITY_X,
new PoolEntry<float>({0.0}));
localDataPoolMap.emplace(gyrodefs::ANGULAR_VELOCITY_Y,
new PoolEntry<float>({0.0}));
localDataPoolMap.emplace(gyrodefs::ANGULAR_VELOCITY_Z,
new PoolEntry<float>({0.0}));
localDataPoolMap.emplace(gyrodefs::GENERAL_CONFIG_REG42,
new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(gyrodefs::RANGE_CONFIG_REG43,
new PoolEntry<uint8_t>({0}));
poolManager.subscribeForPeriodicPacket(gyroData.getSid(), false, 4.0, false);
return HasReturnvaluesIF::RETURN_OK;
}
Now, if we receive some sensor data and converted them into the right format,
we can write it into the pool like this, using a guard class to ensure the set is commited back
in any case:
.. code-block:: cpp
PoolReadGuard readHelper(&gyroData);
if(readHelper.getReadResult() == HasReturnvaluesIF::RETURN_OK) {
if(not gyroData.isValid()) {
gyroData.setValidity(true, true);
}
gyroData.angVelocityX = angularVelocityX;
gyroData.angVelocityY = angularVelocityY;
gyroData.angVelocityZ = angularVelocityZ;
}
The guard class will commit the changed data on destruction automatically.
Using the local data pools in a ``ExtendedControllerBase`` subclass
----------------------------------------------------------------------
Coming soon

View File

@@ -1,35 +0,0 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

View File

@@ -1,63 +0,0 @@
.. _osal:
Operating System Abstraction Layer (OSAL)
============================================
Some specific information on the provided OSALs are provided.
Linux
-------
This OSAL can be used to compile for Linux host systems like Ubuntu 20.04 or for
embedded Linux targets like the Raspberry Pi. This OSAL generally requires threading support
and real-time functionalities. For most UNIX systems, this is done by adding ``-lrt`` and
``-lpthread`` to the linked libraries in the compilation process. The CMake build support provided
will do this automatically for the ``fsfw`` target. It should be noted that most UNIX systems need
to be configured specifically to allow the real-time functionalities required by the FSFW.
Hosted OSAL
-------------------
This is the newest OSAL. Support for Semaphores has not been implemented yet and will propably be
implemented as soon as C++20 with Semaphore support has matured. This OSAL can be used to run the
FSFW on any host system, but currently has only been tested on Windows 10 and Ubuntu 20.04. Unlike
the other OSALs, it uses dynamic memory allocation (e.g. for the message queue implementation).
Cross-platform serial port (USB) support might be added soon.
FreeRTOS OSAL
------------------
FreeRTOS is not included and the developer needs to take care of compiling the FreeRTOS sources and
adding the ``FreeRTOSConfig.h`` file location to the include path. This OSAL has only been tested
extensively with the pre-emptive scheduler configuration so far but it should in principle also be
possible to use a cooperative scheduler. It is recommended to use the `heap_4` allocation scheme.
When using newlib (nano), it is also recommended to add ``#define configUSE_NEWLIB_REENTRANT`` to
the FreeRTOS configuration file to ensure thread-safety.
When using this OSAL, developers also need to provide an implementation for the
``vRequestContextSwitchFromISR`` function. This has been done because the call to request a context
switch from an ISR is generally located in the ``portmacro.h`` header and is different depending on
the target architecture or device.
RTEMS OSAL
---------------
The RTEMS OSAL was the first implemented OSAL which is also used on the active satellite Flying Laptop.
TCP/IP socket abstraction
------------------------------
The Linux and Host OSAL provide abstraction layers for the socket API. Currently, only UDP sockets
have been imlemented. This is very useful to test TMTC handling either on the host computer
directly (targeting localhost with a TMTC application) or on embedded Linux devices, sending
TMTC packets via Ethernet.
Example Applications
----------------------
There are example applications available for each OSAL
- `Hosted OSAL <https://egit.irs.uni-stuttgart.de/fsfw/fsfw-example-hosted>`_
- `Linux OSAL for MCUs <https://egit.irs.uni-stuttgart.de/fsfw/fsfw-example-linux-mcu>`_
- `FreeRTOS OSAL on the STM32H743ZIT <https://egit.irs.uni-stuttgart.de/fsfw/fsfw-example-stm32h7-freertos>`_
- `RTEMS OSAL on the STM32H743ZIT <https://egit.irs.uni-stuttgart.de/fsfw/fsfw-example-stm32h7-rtems>`_

View File

@@ -1,2 +0,0 @@
PUS Services
==============

View File

@@ -9,13 +9,12 @@ using gpioId_t = uint16_t;
namespace gpio {
enum Levels: uint8_t {
enum Levels {
LOW = 0,
HIGH = 1,
NONE = 99
HIGH = 1
};
enum Direction: uint8_t {
enum Direction {
IN = 0,
OUT = 1
};
@@ -25,18 +24,16 @@ enum GpioOperation {
WRITE
};
enum class GpioTypes {
enum GpioTypes {
NONE,
GPIO_REGULAR_BY_CHIP,
GPIO_REGULAR_BY_LABEL,
GPIO_REGULAR_BY_LINE_NAME,
CALLBACK
};
static constexpr gpioId_t NO_GPIO = -1;
using gpio_cb_t = void (*) (gpioId_t gpioId, gpio::GpioOperation gpioOp, gpio::Levels value,
void* args);
using gpio_cb_t = void (*) (gpioId_t gpioId, gpio::GpioOperation gpioOp, int value, void* args);
}
@@ -60,7 +57,7 @@ public:
GpioBase() = default;
GpioBase(gpio::GpioTypes gpioType, std::string consumer, gpio::Direction direction,
gpio::Levels initValue):
int initValue):
gpioType(gpioType), consumer(consumer),direction(direction), initValue(initValue) {}
virtual~ GpioBase() {};
@@ -69,21 +66,15 @@ public:
gpio::GpioTypes gpioType = gpio::GpioTypes::NONE;
std::string consumer;
gpio::Direction direction = gpio::Direction::IN;
gpio::Levels initValue = gpio::Levels::NONE;
int initValue = 0;
};
class GpiodRegularBase: public GpioBase {
public:
GpiodRegularBase(gpio::GpioTypes gpioType, std::string consumer, gpio::Direction direction,
gpio::Levels initValue, int lineNum):
GpioBase(gpioType, consumer, direction, initValue), lineNum(lineNum) {
int initValue, int 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;
struct gpiod_line* lineHandle = nullptr;
};
@@ -96,7 +87,7 @@ public:
}
GpiodRegularByChip(std::string chipname_, int lineNum_, std::string consumer_,
gpio::Direction direction_, gpio::Levels initValue_) :
gpio::Direction direction_, int initValue_) :
GpiodRegularBase(gpio::GpioTypes::GPIO_REGULAR_BY_CHIP,
consumer_, direction_, initValue_, lineNum_),
chipname(chipname_){
@@ -114,7 +105,7 @@ public:
class GpiodRegularByLabel: public GpiodRegularBase {
public:
GpiodRegularByLabel(std::string label_, int lineNum_, std::string consumer_,
gpio::Direction direction_, gpio::Levels initValue_) :
gpio::Direction direction_, int initValue_) :
GpiodRegularBase(gpio::GpioTypes::GPIO_REGULAR_BY_LABEL, consumer_,
direction_, initValue_, lineNum_),
label(label_) {
@@ -129,30 +120,9 @@ public:
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 {
public:
GpioCallback(std::string consumer, gpio::Direction direction_, gpio::Levels initValue_,
GpioCallback(std::string consumer, gpio::Direction direction_, int initValue_,
gpio::gpio_cb_t callback, void* callbackArgs):
GpioBase(gpio::GpioTypes::CALLBACK, consumer, direction_, initValue_),
callback(callback), callbackArgs(callbackArgs) {}

View File

@@ -1,15 +1,13 @@
#include "GyroL3GD20Handler.h"
#include "fsfw_hal/devicehandlers/GyroL3GD20Handler.h"
#include "fsfw/datapool/PoolReadGuard.h"
#include <cmath>
GyroHandlerL3GD20H::GyroHandlerL3GD20H(object_id_t objectId, object_id_t deviceCommunication,
CookieIF *comCookie, uint32_t transitionDelayMs):
CookieIF *comCookie, uint8_t switchId, uint32_t transitionDelayMs):
DeviceHandlerBase(objectId, deviceCommunication, comCookie),
transitionDelayMs(transitionDelayMs), dataset(this) {
switchId(switchId), transitionDelayMs(transitionDelayMs), dataset(this) {
#if FSFW_HAL_L3GD20_GYRO_DEBUG == 1
debugDivider = new PeriodicOperationDivider(3);
debugDivider = new PeriodicOperationDivider(5);
#endif
}
@@ -217,32 +215,11 @@ ReturnValue_t GyroHandlerL3GD20H::interpretDeviceReply(DeviceCommandId_t id,
PoolReadGuard readSet(&dataset);
if(readSet.getReadResult() == HasReturnvaluesIF::RETURN_OK) {
if(std::abs(angVelocX) < this->absLimitX) {
dataset.angVelocX = angVelocX;
dataset.angVelocX.setValid(true);
}
else {
dataset.angVelocX.setValid(false);
}
if(std::abs(angVelocY) < this->absLimitY) {
dataset.angVelocY = angVelocY;
dataset.angVelocY.setValid(true);
}
else {
dataset.angVelocY.setValid(false);
}
if(std::abs(angVelocZ) < this->absLimitZ) {
dataset.angVelocZ = angVelocZ;
dataset.angVelocZ.setValid(true);
}
else {
dataset.angVelocZ.setValid(false);
}
dataset.angVelocX = angVelocX;
dataset.angVelocY = angVelocY;
dataset.angVelocZ = angVelocZ;
dataset.temperature = temperature;
dataset.temperature.setValid(true);
dataset.setValidity(true, true);
}
break;
}
@@ -257,7 +234,7 @@ uint32_t GyroHandlerL3GD20H::getTransitionDelayMs(Mode_t from, Mode_t to) {
return this->transitionDelayMs;
}
void GyroHandlerL3GD20H::setToGoToNormalMode(bool enable) {
void GyroHandlerL3GD20H::setGoNormalModeAtStartup() {
this->goNormalModeImmediately = true;
}
@@ -279,9 +256,3 @@ void GyroHandlerL3GD20H::fillCommandAndReplyMap() {
void GyroHandlerL3GD20H::modeChanged() {
internalState = InternalState::NONE;
}
void GyroHandlerL3GD20H::setAbsoluteLimits(float limitX, float limitY, float limitZ) {
this->absLimitX = limitX;
this->absLimitY = limitY;
this->absLimitZ = limitZ;
}

View File

@@ -19,22 +19,13 @@
class GyroHandlerL3GD20H: public DeviceHandlerBase {
public:
GyroHandlerL3GD20H(object_id_t objectId, object_id_t deviceCommunication,
CookieIF* comCookie, uint32_t transitionDelayMs);
CookieIF* comCookie, uint8_t switchId, uint32_t transitionDelayMs = 10000);
virtual ~GyroHandlerL3GD20H();
/**
* Set the absolute limit for the values on the axis in degrees per second.
* The dataset values will be marked as invalid if that limit is exceeded
* @param xLimit
* @param yLimit
* @param zLimit
*/
void setAbsoluteLimits(float limitX, float limitY, float limitZ);
/**
* @brief Configure device handler to go to normal mode immediately
*/
void setToGoToNormalMode(bool enable);
void setGoNormalModeAtStartup();
protected:
/* DeviceHandlerBase overrides */
@@ -49,23 +40,20 @@ protected:
size_t commandDataLen) override;
ReturnValue_t scanForReply(const uint8_t *start, size_t len,
DeviceCommandId_t *foundId, size_t *foundLen) override;
virtual ReturnValue_t interpretDeviceReply(DeviceCommandId_t id,
ReturnValue_t interpretDeviceReply(DeviceCommandId_t id,
const uint8_t *packet) override;
void fillCommandAndReplyMap() override;
void modeChanged() override;
virtual uint32_t getTransitionDelayMs(Mode_t from, Mode_t to) override;
uint32_t getTransitionDelayMs(Mode_t from, Mode_t to) override;
ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
LocalDataPoolManager &poolManager) override;
private:
uint8_t switchId = 0;
uint32_t transitionDelayMs = 0;
GyroPrimaryDataset dataset;
float absLimitX = L3GD20H::RANGE_DPS_00;
float absLimitY = L3GD20H::RANGE_DPS_00;
float absLimitZ = L3GD20H::RANGE_DPS_00;
enum class InternalState {
NONE,
CONFIGURE,

View File

@@ -5,16 +5,14 @@
#include "fsfw/globalfunctions/PeriodicOperationDivider.h"
#endif
#include <cmath>
MgmLIS3MDLHandler::MgmLIS3MDLHandler(object_id_t objectId, object_id_t deviceCommunication,
CookieIF* comCookie, uint32_t transitionDelay):
CookieIF* comCookie, uint8_t switchId, uint32_t transitionDelay):
DeviceHandlerBase(objectId, deviceCommunication, comCookie),
dataset(this), transitionDelay(transitionDelay) {
#if FSFW_HAL_LIS3MDL_MGM_DEBUG == 1
debugDivider = new PeriodicOperationDivider(3);
debugDivider = new PeriodicOperationDivider(5);
#endif
// Set to default values right away
/* Set to default values right away. */
registers[0] = MGMLIS3MDL::CTRL_REG1_DEFAULT;
registers[1] = MGMLIS3MDL::CTRL_REG2_DEFAULT;
registers[2] = MGMLIS3MDL::CTRL_REG3_DEFAULT;
@@ -73,7 +71,7 @@ ReturnValue_t MgmLIS3MDLHandler::buildTransitionDeviceCommand(
switch (internalState) {
case(InternalState::STATE_NONE):
case(InternalState::STATE_NORMAL): {
return DeviceHandlerBase::NOTHING_TO_SEND;
return HasReturnvaluesIF::RETURN_OK;
}
case(InternalState::STATE_FIRST_CONTACT): {
*id = MGMLIS3MDL::IDENTIFY_DEVICE;
@@ -293,6 +291,7 @@ ReturnValue_t MgmLIS3MDLHandler::interpretDeviceReply(DeviceCommandId_t id,
#if FSFW_HAL_LIS3MDL_MGM_DEBUG == 1
if(debugDivider->checkAndIncrement()) {
/* Set terminal to utf-8 if there is an issue with micro printout. */
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "MGMHandlerLIS3: Magnetic field strength in"
" microtesla:" << std::endl;
@@ -309,29 +308,10 @@ ReturnValue_t MgmLIS3MDLHandler::interpretDeviceReply(DeviceCommandId_t id,
#endif /* OBSW_VERBOSE_LEVEL >= 1 */
PoolReadGuard readHelper(&dataset);
if(readHelper.getReadResult() == HasReturnvaluesIF::RETURN_OK) {
if(std::abs(mgmX) < absLimitX) {
dataset.fieldStrengthX = mgmX;
dataset.fieldStrengthX.setValid(true);
}
else {
dataset.fieldStrengthX.setValid(false);
}
if(std::abs(mgmY) < absLimitY) {
dataset.fieldStrengthY = mgmY;
dataset.fieldStrengthY.setValid(true);
}
else {
dataset.fieldStrengthY.setValid(false);
}
if(std::abs(mgmZ) < absLimitZ) {
dataset.fieldStrengthZ = mgmZ;
dataset.fieldStrengthZ.setValid(true);
}
else {
dataset.fieldStrengthZ.setValid(false);
}
dataset.fieldStrengthX = mgmX;
dataset.fieldStrengthY = mgmY;
dataset.fieldStrengthZ = mgmZ;
dataset.setValidity(true, true);
}
break;
}
@@ -341,6 +321,7 @@ ReturnValue_t MgmLIS3MDLHandler::interpretDeviceReply(DeviceCommandId_t id,
float tempValue = 25.0 + ((static_cast<float>(tempValueRaw)) / 8.0);
#if FSFW_HAL_LIS3MDL_MGM_DEBUG == 1
if(debugDivider->check()) {
/* Set terminal to utf-8 if there is an issue with micro printout. */
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "MGMHandlerLIS3: Temperature: " << tempValue << " C" <<
std::endl;
@@ -463,6 +444,16 @@ ReturnValue_t MgmLIS3MDLHandler::setOperatingMode(const uint8_t *commandData,
}
void MgmLIS3MDLHandler::fillCommandAndReplyMap() {
/*
* Regarding ArduinoBoard:
* Actually SPI answers directly, but as commanding ArduinoBoard the
* communication could be delayed
* SPI always has to be triggered, so there could be no periodic answer of
* the device, the device has to asked with a command, so periodic is zero.
*
* We dont read single registers, we just expect special
* reply from he Readall_MGM
*/
insertInCommandAndReplyMap(MGMLIS3MDL::READ_CONFIG_AND_DATA, 1, &dataset);
insertInCommandAndReplyMap(MGMLIS3MDL::READ_TEMPERATURE, 1);
insertInCommandAndReplyMap(MGMLIS3MDL::SETUP_MGM, 1);
@@ -484,7 +475,7 @@ ReturnValue_t MgmLIS3MDLHandler::prepareCtrlRegisterWrite() {
rawPacket = commandBuffer;
rawPacketLen = MGMLIS3MDL::NR_OF_CTRL_REGISTERS + 1;
// We dont have to check if this is working because we just did i
/* We dont have to check if this is working because we just did it */
return RETURN_OK;
}
@@ -512,9 +503,3 @@ ReturnValue_t MgmLIS3MDLHandler::initializeLocalDataPool(
new PoolEntry<float>({0.0}));
return HasReturnvaluesIF::RETURN_OK;
}
void MgmLIS3MDLHandler::setAbsoluteLimits(float xLimit, float yLimit, float zLimit) {
this->absLimitX = xLimit;
this->absLimitY = yLimit;
this->absLimitZ = zLimit;
}

View File

@@ -31,17 +31,9 @@ public:
static const Event CHANGE_OF_SETUP_PARAMETER = MAKE_EVENT(0, severity::LOW);
MgmLIS3MDLHandler(uint32_t objectId, object_id_t deviceCommunication, CookieIF* comCookie,
uint32_t transitionDelay);
uint8_t switchId, uint32_t transitionDelay = 10000);
virtual ~MgmLIS3MDLHandler();
/**
* Set the absolute limit for the values on the axis in microtesla. The dataset values will
* be marked as invalid if that limit is exceeded
* @param xLimit
* @param yLimit
* @param zLimit
*/
void setAbsoluteLimits(float xLimit, float yLimit, float zLimit);
void setToGoToNormalMode(bool enable);
protected:
@@ -50,7 +42,7 @@ protected:
void doShutDown() override;
void doStartUp() override;
void doTransition(Mode_t modeFrom, Submode_t subModeFrom) override;
virtual uint32_t getTransitionDelayMs(Mode_t from, Mode_t to) override;
uint32_t getTransitionDelayMs(Mode_t from, Mode_t to) override;
ReturnValue_t buildCommandFromCommand(
DeviceCommandId_t deviceCommand, const uint8_t *commandData,
size_t commandDataLen) override;
@@ -60,14 +52,7 @@ protected:
DeviceCommandId_t *id) override;
ReturnValue_t scanForReply(const uint8_t *start, size_t len,
DeviceCommandId_t *foundId, size_t *foundLen) override;
/**
* This implementation is tailored towards space applications and will flag values larger
* than 100 microtesla on X,Y and 150 microtesla on Z as invalid
* @param id
* @param packet
* @return
*/
virtual ReturnValue_t interpretDeviceReply(DeviceCommandId_t id,
ReturnValue_t interpretDeviceReply(DeviceCommandId_t id,
const uint8_t *packet) override;
void fillCommandAndReplyMap() override;
void modeChanged(void) override;
@@ -76,19 +61,15 @@ protected:
private:
MGMLIS3MDL::MgmPrimaryDataset dataset;
//Length a single command SPI answer
//Length a sindgle command SPI answer
static const uint8_t SINGLE_COMMAND_ANSWER_LEN = 2;
uint32_t transitionDelay;
// Single SPI command has 2 bytes, first for adress, second for content
//Single SPIcommand has 2 bytes, first for adress, second for content
size_t singleComandSize = 2;
// Has the size for all adresses of the lis3mdl + the continous write bit
//has the size for all adresses of the lis3mdl + the continous write bit
uint8_t commandBuffer[MGMLIS3MDL::NR_OF_DATA_AND_CFG_REGISTERS + 1];
float absLimitX = 100;
float absLimitY = 100;
float absLimitZ = 150;
/**
* We want to save the registers we set, so we dont have to read the
* registers when we want to change something.

View File

@@ -8,9 +8,10 @@
MgmRM3100Handler::MgmRM3100Handler(object_id_t objectId,
object_id_t deviceCommunication, CookieIF* comCookie, uint32_t transitionDelay):
object_id_t deviceCommunication, CookieIF* comCookie, uint8_t switchId,
uint32_t transitionDelay):
DeviceHandlerBase(objectId, deviceCommunication, comCookie),
primaryDataset(this), transitionDelay(transitionDelay) {
primaryDataset(this), switchId(switchId), transitionDelay(transitionDelay) {
#if FSFW_HAL_RM3100_MGM_DEBUG == 1
debugDivider = new PeriodicOperationDivider(3);
#endif
@@ -89,16 +90,9 @@ ReturnValue_t MgmRM3100Handler::buildTransitionDeviceCommand(
break;
}
default:
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1
// Might be a configuration error
sif::warning << "MgmRM3100Handler::buildTransitionDeviceCommand: "
"Unknown internal state" << std::endl;
#else
sif::printWarning("MgmRM3100Handler::buildTransitionDeviceCommand: "
"Unknown internal state\n");
#endif
#endif
sif::warning << "MgmRM3100Handler::buildTransitionDeviceCommand: Unknown internal state!" <<
std::endl;
return HasReturnvaluesIF::RETURN_OK;
}
@@ -186,7 +180,7 @@ ReturnValue_t MgmRM3100Handler::interpretDeviceReply(DeviceCommandId_t id, const
uint8_t cmmValue = packet[1];
// We clear the seventh bit in any case
// because this one is zero sometimes for some reason
bitutil::clear(&cmmValue, 6);
bitutil::bitClear(&cmmValue, 6);
if(cmmValue == cmmRegValue and internalState == InternalState::READ_CMM) {
commandExecuted = true;
}
@@ -328,7 +322,13 @@ ReturnValue_t MgmRM3100Handler::initializeLocalDataPool(
}
uint32_t MgmRM3100Handler::getTransitionDelayMs(Mode_t from, Mode_t to) {
return this->transitionDelay;
return 25000;
}
ReturnValue_t MgmRM3100Handler::getSwitches(const uint8_t **switches, uint8_t *numberOfSwitches) {
*switches = &switchId;
*numberOfSwitches = 1;
return HasReturnvaluesIF::RETURN_OK;
}
void MgmRM3100Handler::setToGoToNormalMode(bool enable) {
@@ -349,18 +349,12 @@ ReturnValue_t MgmRM3100Handler::handleDataReadout(const uint8_t *packet) {
#if FSFW_HAL_RM3100_MGM_DEBUG == 1
if(debugDivider->checkAndIncrement()) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "MgmRM3100Handler: Magnetic field strength in"
" microtesla:" << std::endl;
/* Set terminal to utf-8 if there is an issue with micro printout. */
sif::info << "X: " << fieldStrengthX << " uT" << std::endl;
sif::info << "Y: " << fieldStrengthY << " uT" << std::endl;
sif::info << "Z: " << fieldStrengthZ << " uT" << std::endl;
#else
sif::printInfo("MgmRM3100Handler: Magnetic field strength in microtesla:\n");
sif::printInfo("X: %f uT\n", fieldStrengthX);
sif::printInfo("Y: %f uT\n", fieldStrengthY);
sif::printInfo("Z: %f uT\n", fieldStrengthZ);
#endif
}
#endif

View File

@@ -2,6 +2,7 @@
#define MISSION_DEVICES_MGMRM3100HANDLER_H_
#include "fsfw/FSFW.h"
#include "devices/powerSwitcherList.h"
#include "devicedefinitions/MgmRM3100HandlerDefs.h"
#include "fsfw/devicehandlers/DeviceHandlerBase.h"
@@ -31,7 +32,7 @@ public:
SUBSYSTEM_ID::MGM_RM3100, 0x01, severity::INFO);
MgmRM3100Handler(object_id_t objectId, object_id_t deviceCommunication,
CookieIF* comCookie, uint32_t transitionDelay);
CookieIF* comCookie, uint8_t switchId, uint32_t transitionDelay = 10000);
virtual ~MgmRM3100Handler();
/**
@@ -47,16 +48,21 @@ protected:
DeviceCommandId_t *id) override;
void doStartUp() override;
void doShutDown() override;
ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t *id) override;
ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand,
const uint8_t *commandData, size_t commandDataLen) override;
ReturnValue_t buildNormalDeviceCommand(
DeviceCommandId_t *id) override;
ReturnValue_t buildCommandFromCommand(
DeviceCommandId_t deviceCommand, const uint8_t *commandData,
size_t commandDataLen) override;
ReturnValue_t scanForReply(const uint8_t *start, size_t len,
DeviceCommandId_t *foundId, size_t *foundLen) override;
ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) override;
ReturnValue_t interpretDeviceReply(DeviceCommandId_t id,
const uint8_t *packet) override;
ReturnValue_t getSwitches(const uint8_t **switches,
uint8_t *numberOfSwitches) override;
void fillCommandAndReplyMap() override;
void modeChanged(void) override;
virtual uint32_t getTransitionDelayMs(Mode_t from, Mode_t to) override;
uint32_t getTransitionDelayMs(Mode_t from, Mode_t to) override;
ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
LocalDataPoolManager &poolManager) override;
@@ -91,6 +97,7 @@ private:
float scaleFactorZ = 1.0 / RM3100::DEFAULT_GAIN;
bool goToNormalModeAtStartup = false;
uint8_t switchId;
uint32_t transitionDelay;
ReturnValue_t handleCycleCountConfigCommand(DeviceCommandId_t deviceCommand,

View File

@@ -154,7 +154,7 @@ enum MgmPoolIds: lp_id_t {
TEMPERATURE_CELCIUS
};
class MgmPrimaryDataset: public StaticLocalDataSet<4> {
class MgmPrimaryDataset: public StaticLocalDataSet<5> {
public:
MgmPrimaryDataset(HasLocalDataPoolIF* hkOwner):
StaticLocalDataSet(hkOwner, MGM_DATA_SET_ID) {}

View File

@@ -49,7 +49,7 @@ static constexpr uint8_t TMRC_DEFAULT_VALUE = TMRC_DEFAULT_37HZ_VALUE;
static constexpr uint8_t MEASUREMENT_REG_START = 0x24;
static constexpr uint8_t BIST_REGISTER = 0x33;
static constexpr uint8_t DATA_READY_VAL = 0b10000000;
static constexpr uint8_t DATA_READY_VAL = 0b1000'0000;
static constexpr uint8_t STATUS_REGISTER = 0x34;
static constexpr uint8_t REVID_REGISTER = 0x36;
@@ -108,7 +108,7 @@ enum MgmPoolIds: lp_id_t {
FIELD_STRENGTH_Z,
};
class Rm3100PrimaryDataset: public StaticLocalDataSet<3> {
class Rm3100PrimaryDataset: public StaticLocalDataSet<3 * sizeof(float)> {
public:
Rm3100PrimaryDataset(HasLocalDataPoolIF* hkOwner):
StaticLocalDataSet(hkOwner, MGM_DATASET_ID) {}

View File

@@ -1,9 +1,8 @@
#include "LinuxLibgpioIF.h"
#include "fsfw_hal/linux/gpio/LinuxLibgpioIF.h"
#include "fsfw_hal/common/gpio/gpioDefinitions.h"
#include "fsfw_hal/common/gpio/GpioCookie.h"
#include "fsfw/serviceinterface/ServiceInterface.h"
#include <fsfw/serviceinterface/ServiceInterface.h>
#include <utility>
#include <unistd.h>
@@ -67,14 +66,6 @@ ReturnValue_t LinuxLibgpioIF::configureGpios(GpioMap& mapToAdd) {
configureGpioByLabel(gpioConfig.first, *regularGpio);
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): {
auto gpioCallback = dynamic_cast<GpioCallback*>(gpioConfig.second);
if(gpioCallback->callback == nullptr) {
@@ -93,13 +84,13 @@ ReturnValue_t LinuxLibgpioIF::configureGpioByLabel(gpioId_t gpioId,
std::string& label = gpioByLabel.label;
struct gpiod_chip* chip = gpiod_chip_open_by_label(label.c_str());
if (chip == nullptr) {
sif::warning << "LinuxLibgpioIF::configureGpioByLabel: Failed to open gpio from gpio "
sif::warning << "LinuxLibgpioIF::configureRegularGpio: Failed to open gpio from gpio "
<< "group with label " << label << ". Gpio ID: " << gpioId << std::endl;
return RETURN_FAILED;
}
std::string failOutput = "label: " + label;
return configureRegularGpio(gpioId, chip, gpioByLabel, failOutput);
return configureRegularGpio(gpioId, gpioByLabel.gpioType, chip, gpioByLabel, failOutput);
}
ReturnValue_t LinuxLibgpioIF::configureGpioByChip(gpioId_t gpioId,
@@ -107,41 +98,16 @@ ReturnValue_t LinuxLibgpioIF::configureGpioByChip(gpioId_t gpioId,
std::string& chipname = gpioByChip.chipname;
struct gpiod_chip* chip = gpiod_chip_open_by_name(chipname.c_str());
if (chip == nullptr) {
sif::warning << "LinuxLibgpioIF::configureGpioByChip: Failed to open chip "
sif::warning << "LinuxLibgpioIF::configureRegularGpio: Failed to open chip "
<< chipname << ". Gpio ID: " << gpioId << std::endl;
return RETURN_FAILED;
}
std::string failOutput = "chipname: " + chipname;
return configureRegularGpio(gpioId, chip, gpioByChip, failOutput);
return configureRegularGpio(gpioId, gpioByChip.gpioType, chip, gpioByChip, failOutput);
}
ReturnValue_t LinuxLibgpioIF::configureGpioByLineName(gpioId_t gpioId,
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) {
ReturnValue_t LinuxLibgpioIF::configureRegularGpio(gpioId_t gpioId, gpio::GpioTypes gpioType,
struct gpiod_chip* chip, GpiodRegularBase& regularGpio, std::string failOutput) {
unsigned int lineNum;
gpio::Direction direction;
std::string consumer;
@@ -166,10 +132,22 @@ ReturnValue_t LinuxLibgpioIF::configureRegularGpio(gpioId_t gpioId, struct gpiod
case(gpio::OUT): {
result = gpiod_line_request_output(lineHandle, consumer.c_str(),
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;
}
case(gpio::IN): {
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;
}
default: {
@@ -178,18 +156,6 @@ ReturnValue_t LinuxLibgpioIF::configureRegularGpio(gpioId_t gpioId, struct gpiod
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
@@ -207,9 +173,8 @@ ReturnValue_t LinuxLibgpioIF::pullHigh(gpioId_t gpioId) {
}
auto gpioType = gpioMapIter->second->gpioType;
if (gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_CHIP
or gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_LABEL
or gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_LINE_NAME) {
if(gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_CHIP or
gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_LABEL) {
auto regularGpio = dynamic_cast<GpiodRegularBase*>(gpioMapIter->second);
if(regularGpio == nullptr) {
return GPIO_TYPE_FAILURE;
@@ -222,7 +187,7 @@ ReturnValue_t LinuxLibgpioIF::pullHigh(gpioId_t gpioId) {
return GPIO_INVALID_INSTANCE;
}
gpioCallback->callback(gpioMapIter->first, gpio::GpioOperation::WRITE,
gpio::Levels::HIGH, gpioCallback->callbackArgs);
1, gpioCallback->callbackArgs);
return RETURN_OK;
}
return GPIO_TYPE_FAILURE;
@@ -231,18 +196,13 @@ ReturnValue_t LinuxLibgpioIF::pullHigh(gpioId_t gpioId) {
ReturnValue_t LinuxLibgpioIF::pullLow(gpioId_t gpioId) {
gpioMapIter = gpioMap.find(gpioId);
if (gpioMapIter == gpioMap.end()) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
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;
}
auto& gpioType = gpioMapIter->second->gpioType;
if (gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_CHIP
or gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_LABEL
or gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_LINE_NAME) {
if(gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_CHIP or
gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_LABEL) {
auto regularGpio = dynamic_cast<GpiodRegularBase*>(gpioMapIter->second);
if(regularGpio == nullptr) {
return GPIO_TYPE_FAILURE;
@@ -255,7 +215,7 @@ ReturnValue_t LinuxLibgpioIF::pullLow(gpioId_t gpioId) {
return GPIO_INVALID_INSTANCE;
}
gpioCallback->callback(gpioMapIter->first, gpio::GpioOperation::WRITE,
gpio::Levels::LOW, gpioCallback->callbackArgs);
0, gpioCallback->callbackArgs);
return RETURN_OK;
}
return GPIO_TYPE_FAILURE;
@@ -265,13 +225,8 @@ ReturnValue_t LinuxLibgpioIF::driveGpio(gpioId_t gpioId,
GpiodRegularBase& regularGpio, gpio::Levels logicLevel) {
int result = gpiod_line_set_value(regularGpio.lineHandle, logicLevel);
if (result < 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LinuxLibgpioIF::driveGpio: Failed to pull GPIO with ID " << gpioId <<
" 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;
}
@@ -281,18 +236,12 @@ ReturnValue_t LinuxLibgpioIF::driveGpio(gpioId_t gpioId,
ReturnValue_t LinuxLibgpioIF::readGpio(gpioId_t gpioId, int* gpioState) {
gpioMapIter = gpioMap.find(gpioId);
if (gpioMapIter == gpioMap.end()){
#if FSFW_CPP_OSTREAM_ENABLED == 1
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;
}
auto gpioType = gpioMapIter->second->gpioType;
if (gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_CHIP
or gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_LABEL
or gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_LINE_NAME) {
if(gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_CHIP or
gpioType == gpio::GpioTypes::GPIO_REGULAR_BY_LABEL) {
auto regularGpio = dynamic_cast<GpiodRegularBase*>(gpioMapIter->second);
if(regularGpio == nullptr) {
return GPIO_TYPE_FAILURE;
@@ -300,14 +249,10 @@ ReturnValue_t LinuxLibgpioIF::readGpio(gpioId_t gpioId, int* gpioState) {
*gpioState = gpiod_line_get_value(regularGpio->lineHandle);
}
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;
}
@@ -317,14 +262,13 @@ ReturnValue_t LinuxLibgpioIF::checkForConflicts(GpioMap& mapToAdd){
for(auto& gpioConfig: mapToAdd) {
switch(gpioConfig.second->gpioType) {
case(gpio::GpioTypes::GPIO_REGULAR_BY_CHIP):
case(gpio::GpioTypes::GPIO_REGULAR_BY_LABEL):
case(gpio::GpioTypes::GPIO_REGULAR_BY_LINE_NAME): {
case(gpio::GpioTypes::GPIO_REGULAR_BY_LABEL): {
auto regularGpio = dynamic_cast<GpiodRegularBase*>(gpioConfig.second);
if(regularGpio == nullptr) {
return GPIO_TYPE_FAILURE;
}
// Check for conflicts and remove duplicates if necessary
result = checkForConflictsById(gpioConfig.first, gpioConfig.second->gpioType, mapToAdd);
/* Check for conflicts and remove duplicates if necessary */
result = checkForConflictsRegularGpio(gpioConfig.first, *regularGpio, mapToAdd);
if(result != HasReturnvaluesIF::RETURN_OK) {
status = result;
}
@@ -335,108 +279,66 @@ ReturnValue_t LinuxLibgpioIF::checkForConflicts(GpioMap& mapToAdd){
if(callbackGpio == nullptr) {
return GPIO_TYPE_FAILURE;
}
// Check for conflicts and remove duplicates if necessary
result = checkForConflictsById(gpioConfig.first,
gpioConfig.second->gpioType, mapToAdd);
/* Check for conflicts and remove duplicates if necessary */
result = checkForConflictsCallbackGpio(gpioConfig.first, callbackGpio, mapToAdd);
if(result != HasReturnvaluesIF::RETURN_OK) {
status = result;
}
break;
}
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;
}
ReturnValue_t LinuxLibgpioIF::checkForConflictsById(gpioId_t gpioIdToCheck,
gpio::GpioTypes expectedType, GpioMap& mapToAdd) {
// Cross check with private map
ReturnValue_t LinuxLibgpioIF::checkForConflictsRegularGpio(gpioId_t gpioIdToCheck,
GpiodRegularBase& gpioToCheck, GpioMap& mapToAdd) {
/* Cross check with private map */
gpioMapIter = gpioMap.find(gpioIdToCheck);
if(gpioMapIter != gpioMap.end()) {
auto& gpioType = gpioMapIter->second->gpioType;
bool eraseDuplicateDifferentType = false;
switch(expectedType) {
case(gpio::GpioTypes::NONE): {
break;
}
case(gpio::GpioTypes::GPIO_REGULAR_BY_CHIP):
case(gpio::GpioTypes::GPIO_REGULAR_BY_LABEL):
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
if(gpioType != gpio::GpioTypes::GPIO_REGULAR_BY_CHIP and
gpioType != gpio::GpioTypes::GPIO_REGULAR_BY_LABEL) {
sif::warning << "LinuxLibgpioIF::checkForConflicts: ID already exists for different "
"GPIO type" << gpioIdToCheck << ". Removing duplicate." << std::endl;
mapToAdd.erase(gpioIdToCheck);
return GPIO_DUPLICATE_DETECTED;
return HasReturnvaluesIF::RETURN_OK;
}
auto ownRegularGpio = dynamic_cast<GpiodRegularBase*>(gpioMapIter->second);
if(ownRegularGpio == nullptr) {
return GPIO_TYPE_FAILURE;
}
// Remove element from map to add because a entry for this GPIO already exists
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LinuxLibgpioIF::checkForConflictsRegularGpio: Duplicate GPIO "
"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
/* 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 GPIO_DUPLICATE_DETECTED;
}
return HasReturnvaluesIF::RETURN_OK;
}
void LinuxLibgpioIF::parseFindeLineResult(int result, std::string& lineName) {
switch (result) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
case LINE_NOT_EXISTS:
case LINE_ERROR: {
sif::warning << "LinuxLibgpioIF::parseFindeLineResult: Line with name " << lineName <<
" does not exist" << std::endl;
break;
}
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
}
ReturnValue_t LinuxLibgpioIF::checkForConflictsCallbackGpio(gpioId_t gpioIdToCheck,
GpioCallback *callbackGpio, GpioMap& mapToAdd) {
/* Cross check with private map */
gpioMapIter = gpioMap.find(gpioIdToCheck);
if(gpioMapIter != gpioMap.end()) {
if(gpioMapIter->second->gpioType != gpio::GpioTypes::CALLBACK) {
sif::warning << "LinuxLibgpioIF::checkForConflicts: ID already exists for different "
"GPIO type" << gpioIdToCheck << ". Removing duplicate." << std::endl;
mapToAdd.erase(gpioIdToCheck);
return HasReturnvaluesIF::RETURN_OK;
}
/* 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_
#define LINUX_GPIO_LINUXLIBGPIOIF_H_
#include "fsfw/returnvalues/FwClassIds.h"
#include "fsfw_hal/common/gpio/GpioIF.h"
#include "fsfw/objectmanager/SystemObject.h"
#include "../../common/gpio/GpioIF.h"
#include <returnvalues/classIds.h>
#include <fsfw/objectmanager/SystemObject.h>
class GpioCookie;
class GpiodRegularIF;
/**
* @brief This class implements the GpioIF for a linux based system.
* @details
* 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 2019.1.
* @brief This class implements the GpioIF for a linux based system. The
* implementation is based on the libgpiod lib which requires linux 4.8
* or higher.
* @note The Petalinux SDK from Xilinx supports libgpiod since Petalinux
* 2019.1.
*/
class LinuxLibgpioIF : public GpioIF, public SystemObject {
public:
@@ -28,8 +28,6 @@ public:
HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 3);
static constexpr ReturnValue_t GPIO_INVALID_INSTANCE =
HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 4);
static constexpr ReturnValue_t GPIO_DUPLICATE_DETECTED =
HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 5);
LinuxLibgpioIF(object_id_t objectId);
virtual ~LinuxLibgpioIF();
@@ -40,13 +38,7 @@ public:
ReturnValue_t readGpio(gpioId_t gpioId, int* gpioState) override;
private:
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
/* Holds the information and configuration of all used GPIOs */
GpioUnorderedMap gpioMap;
GpioUnorderedMapIter gpioMapIter;
@@ -61,10 +53,8 @@ private:
ReturnValue_t configureGpioByLabel(gpioId_t gpioId, GpiodRegularByLabel& gpioByLabel);
ReturnValue_t configureGpioByChip(gpioId_t gpioId, GpiodRegularByChip& gpioByChip);
ReturnValue_t configureGpioByLineName(gpioId_t gpioId,
GpiodRegularByLineName &gpioByLineName);
ReturnValue_t configureRegularGpio(gpioId_t gpioId, struct gpiod_chip* chip,
GpiodRegularBase& regularGpio, std::string failOutput);
ReturnValue_t configureRegularGpio(gpioId_t gpioId, gpio::GpioTypes gpioType,
struct gpiod_chip* chip, GpiodRegularBase& regularGpio, std::string failOutput);
/**
* @brief This function checks if GPIOs are already registered and whether
@@ -77,15 +67,16 @@ private:
*/
ReturnValue_t checkForConflicts(GpioMap& mapToAdd);
ReturnValue_t checkForConflictsById(gpioId_t gpiodId, gpio::GpioTypes type,
ReturnValue_t checkForConflictsRegularGpio(gpioId_t gpiodId, GpiodRegularBase& regularGpio,
GpioMap& mapToAdd);
ReturnValue_t checkForConflictsCallbackGpio(gpioId_t gpiodId, GpioCallback* regularGpio,
GpioMap& mapToAdd);
/**
* @brief Performs the initial configuration of all GPIOs specified in the GpioMap mapToAdd.
*/
ReturnValue_t configureGpios(GpioMap& mapToAdd);
void parseFindeLineResult(int result, std::string& lineName);
};
#endif /* LINUX_GPIO_LINUXLIBGPIOIF_H_ */

View File

@@ -12,7 +12,7 @@ ReturnValue_t gpio::createRpiGpioConfig(GpioCookie* cookie, gpioId_t gpioId, int
return HasReturnvaluesIF::RETURN_FAILED;
}
auto config = new GpiodRegularByChip();
GpiodRegular* config = new GpiodRegular();
/* Default chipname for Raspberry Pi. There is still gpiochip1 for expansion, but most users
will not need this */
config->chipname = "gpiochip0";

View File

@@ -141,8 +141,8 @@ ReturnValue_t SpiComIF::sendMessage(CookieIF *cookie, const uint8_t *sendData, s
if(sendLen > spiCookie->getMaxBufferSize()) {
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "SpiComIF::sendMessage: Too much data sent, send length " << sendLen <<
"larger than maximum buffer length " << spiCookie->getMaxBufferSize() << std::endl;
sif::warning << "SpiComIF::sendMessage: Too much data sent, send length" << sendLen <<
"larger than maximum buffer length" << spiCookie->getMaxBufferSize() << std::endl;
#else
sif::printWarning("SpiComIF::sendMessage: Too much data sent, send length %lu larger "
"than maximum buffer length %lu!\n", static_cast<unsigned long>(sendLen),
@@ -227,7 +227,7 @@ ReturnValue_t SpiComIF::performRegularSendOperation(SpiCookie *spiCookie, const
utility::handleIoctlError("SpiComIF::sendMessage: ioctl error.");
result = FULL_DUPLEX_TRANSFER_FAILED;
}
#if FSFW_HAL_SPI_WIRETAPPING == 1
#if FSFW_HAL_LINUX_SPI_WIRETAPPING == 1
performSpiWiretapping(spiCookie);
#endif /* FSFW_LINUX_SPI_WIRETAPPING == 1 */
}
@@ -398,11 +398,11 @@ GpioIF* SpiComIF::getGpioInterface() {
void SpiComIF::setSpiSpeedAndMode(int spiFd, spi::SpiModes mode, uint32_t speed) {
int retval = ioctl(spiFd, SPI_IOC_WR_MODE, reinterpret_cast<uint8_t*>(&mode));
if(retval != 0) {
utility::handleIoctlError("SpiComIF::setSpiSpeedAndMode: Setting SPI mode failed");
utility::handleIoctlError("SpiTestClass::performRm3100Test: Setting SPI mode failed!");
}
retval = ioctl(spiFd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if(retval != 0) {
utility::handleIoctlError("SpiComIF::setSpiSpeedAndMode: Setting SPI speed failed");
utility::handleIoctlError("SpiTestClass::performRm3100Test: Setting SPI speed failed!");
}
}

View File

@@ -1,25 +0,0 @@
#ifndef FSFW_HAL_STM32H7_DEFINITIONS_H_
#define FSFW_HAL_STM32H7_DEFINITIONS_H_
#include <utility>
#include "stm32h7xx.h"
namespace stm32h7 {
/**
* Typedef for STM32 GPIO pair where the first entry is the port used (e.g. GPIOA)
* and the second entry is the pin number
*/
struct GpioCfg {
GpioCfg(): port(nullptr), pin(0), altFnc(0) {};
GpioCfg(GPIO_TypeDef* port, uint16_t pin, uint8_t altFnc = 0):
port(port), pin(pin), altFnc(altFnc) {};
GPIO_TypeDef* port;
uint16_t pin;
uint8_t altFnc;
};
}
#endif /* #ifndef FSFW_HAL_STM32H7_DEFINITIONS_H_ */

View File

@@ -4,7 +4,7 @@
#include "fsfw_hal/stm32h7/spi/spiDefinitions.h"
#include "fsfw_hal/stm32h7/spi/spiCore.h"
#include "fsfw_hal/stm32h7/spi/spiInterrupts.h"
#include "fsfw_hal/stm32h7/spi/stm32h743zi.h"
#include "fsfw_hal/stm32h7/spi/stm32h743ziSpi.h"
#include "fsfw/tasks/TaskFactory.h"
#include "fsfw/serviceinterface/ServiceInterface.h"
@@ -33,20 +33,20 @@ GyroL3GD20H::GyroL3GD20H(SPI_HandleTypeDef *spiHandle, spi::TransferModes transf
mspCfg = new spi::MspDmaConfigStruct();
auto typedCfg = dynamic_cast<spi::MspDmaConfigStruct*>(mspCfg);
spi::setDmaHandles(txDmaHandle, rxDmaHandle);
stm32h7::h743zi::standardDmaCfg(*typedCfg, IrqPriorities::HIGHEST_FREERTOS,
spi::h743zi::standardDmaCfg(*typedCfg, IrqPriorities::HIGHEST_FREERTOS,
IrqPriorities::HIGHEST_FREERTOS, IrqPriorities::HIGHEST_FREERTOS);
spi::setSpiDmaMspFunctions(typedCfg);
}
else if(transferMode == spi::TransferModes::INTERRUPT) {
mspCfg = new spi::MspIrqConfigStruct();
auto typedCfg = dynamic_cast<spi::MspIrqConfigStruct*>(mspCfg);
stm32h7::h743zi::standardInterruptCfg(*typedCfg, IrqPriorities::HIGHEST_FREERTOS);
spi::h743zi::standardInterruptCfg(*typedCfg, IrqPriorities::HIGHEST_FREERTOS);
spi::setSpiIrqMspFunctions(typedCfg);
}
else if(transferMode == spi::TransferModes::POLLING) {
mspCfg = new spi::MspPollingConfigStruct();
auto typedCfg = dynamic_cast<spi::MspPollingConfigStruct*>(mspCfg);
stm32h7::h743zi::standardPollingCfg(*typedCfg);
spi::h743zi::standardPollingCfg(*typedCfg);
spi::setSpiPollingMspFunctions(typedCfg);
}

View File

@@ -5,5 +5,5 @@ target_sources(${LIB_FSFW_NAME} PRIVATE
mspInit.cpp
SpiCookie.cpp
SpiComIF.cpp
stm32h743zi.cpp
stm32h743ziSpi.cpp
)

View File

@@ -138,14 +138,12 @@ ReturnValue_t SpiComIF::initializeInterface(CookieIF *cookie) {
spi::setSpiDmaMspFunctions(typedCfg);
}
if(gpioPort != nullptr) {
gpio::initializeGpioClock(gpioPort);
GPIO_InitTypeDef chipSelect = {};
chipSelect.Pin = gpioPin;
chipSelect.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(gpioPort, &chipSelect);
HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_SET);
}
gpio::initializeGpioClock(gpioPort);
GPIO_InitTypeDef chipSelect = {};
chipSelect.Pin = gpioPin;
chipSelect.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(gpioPort, &chipSelect);
HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_SET);
if(HAL_SPI_Init(&spiHandle) != HAL_OK) {
sif::printWarning("SpiComIF::initialize: Error initializing SPI\n");
@@ -261,15 +259,10 @@ ReturnValue_t SpiComIF::handlePollingSendOperation(uint8_t* recvPtr, SPI_HandleT
return returnval;
}
spiCookie.setTransferState(spi::TransferStates::WAIT);
if(gpioPort != nullptr) {
HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_RESET);
}
HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_RESET);
auto result = HAL_SPI_TransmitReceive(&spiHandle, const_cast<uint8_t*>(sendData),
recvPtr, sendLen, defaultPollingTimeout);
if(gpioPort != nullptr) {
HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_SET);
}
HAL_GPIO_WritePin(gpioPort, gpioPin, GPIO_PIN_SET);
spiSemaphore->release();
switch(result) {
case(HAL_OK): {
@@ -399,10 +392,8 @@ ReturnValue_t SpiComIF::genericIrqSendSetup(uint8_t *recvPtr, SPI_HandleTypeDef&
// The SPI handle is passed to the default SPI callback as a void argument. This callback
// is different from the user callbacks specified above!
spi::assignSpiUserArgs(spiCookie.getSpiIdx(), reinterpret_cast<void*>(&spiHandle));
if(spiCookie.getChipSelectGpioPort() != nullptr) {
HAL_GPIO_WritePin(spiCookie.getChipSelectGpioPort(), spiCookie.getChipSelectGpioPin(),
GPIO_PIN_RESET);
}
HAL_GPIO_WritePin(spiCookie.getChipSelectGpioPort(), spiCookie.getChipSelectGpioPin(),
GPIO_PIN_RESET);
return HasReturnvaluesIF::RETURN_OK;
}
@@ -435,12 +426,9 @@ void SpiComIF::genericIrqHandler(void *irqArgsVoid, spi::TransferStates targetSt
spiCookie->setTransferState(targetState);
if(spiCookie->getChipSelectGpioPort() != nullptr) {
// Pull CS pin high again
HAL_GPIO_WritePin(spiCookie->getChipSelectGpioPort(), spiCookie->getChipSelectGpioPin(),
GPIO_PIN_SET);
}
// Pull CS pin high again
HAL_GPIO_WritePin(spiCookie->getChipSelectGpioPort(), spiCookie->getChipSelectGpioPin(),
GPIO_PIN_SET);
#if defined FSFW_OSAL_FREERTOS
// Release the task semaphore

View File

@@ -60,6 +60,7 @@ public:
void addDmaHandles(DMA_HandleTypeDef* txHandle, DMA_HandleTypeDef* rxHandle);
ReturnValue_t initialize() override;
protected:
// DeviceCommunicationIF overrides
virtual ReturnValue_t initializeInterface(CookieIF * cookie) override;
@@ -71,7 +72,7 @@ public:
virtual ReturnValue_t readReceivedMessage(CookieIF *cookie,
uint8_t **buffer, size_t *size) override;
protected:
private:
struct SpiInstance {
SpiInstance(size_t maxRecvSize): replyBuffer(std::vector<uint8_t>(maxRecvSize)) {}

View File

@@ -3,10 +3,10 @@
SpiCookie::SpiCookie(address_t deviceAddress, spi::SpiBus spiIdx, spi::TransferModes transferMode,
spi::MspCfgBase* mspCfg, uint32_t spiSpeed, spi::SpiModes spiMode,
size_t maxRecvSize, stm32h7::GpioCfg csGpio):
uint16_t chipSelectGpioPin, GPIO_TypeDef* chipSelectGpioPort, size_t maxRecvSize):
deviceAddress(deviceAddress), spiIdx(spiIdx), spiSpeed(spiSpeed), spiMode(spiMode),
transferMode(transferMode), csGpio(csGpio),
mspCfg(mspCfg), maxRecvSize(maxRecvSize) {
transferMode(transferMode), chipSelectGpioPin(chipSelectGpioPin),
chipSelectGpioPort(chipSelectGpioPort), mspCfg(mspCfg), maxRecvSize(maxRecvSize) {
spiHandle.Init.DataSize = SPI_DATASIZE_8BIT;
spiHandle.Init.FirstBit = SPI_FIRSTBIT_MSB;
spiHandle.Init.TIMode = SPI_TIMODE_DISABLE;
@@ -24,11 +24,11 @@ SpiCookie::SpiCookie(address_t deviceAddress, spi::SpiBus spiIdx, spi::TransferM
}
uint16_t SpiCookie::getChipSelectGpioPin() const {
return csGpio.pin;
return chipSelectGpioPin;
}
GPIO_TypeDef* SpiCookie::getChipSelectGpioPort() {
return csGpio.port;
return chipSelectGpioPort;
}
address_t SpiCookie::getDeviceAddress() const {

View File

@@ -3,14 +3,11 @@
#include "spiDefinitions.h"
#include "mspInit.h"
#include "../definitions.h"
#include "fsfw/devicehandlers/CookieIF.h"
#include "stm32h743xx.h"
#include <utility>
/**
* @brief SPI cookie implementation for the STM32H7 device family
* @details
@@ -21,7 +18,6 @@
class SpiCookie: public CookieIF {
friend class SpiComIF;
public:
/**
* Allows construction of a SPI cookie for a connected SPI device
* @param deviceAddress
@@ -36,11 +32,10 @@ public:
* definitions supplied in the MCU header file! (e.g. GPIO_PIN_X)
* @param chipSelectGpioPort GPIO port (e.g. GPIOA)
* @param maxRecvSize Maximum expected receive size. Chose as small as possible.
* @param csGpio Optional CS GPIO definition.
*/
SpiCookie(address_t deviceAddress, spi::SpiBus spiIdx, spi::TransferModes transferMode,
spi::MspCfgBase* mspCfg, uint32_t spiSpeed, spi::SpiModes spiMode,
size_t maxRecvSize, stm32h7::GpioCfg csGpio = stm32h7::GpioCfg(nullptr, 0, 0));
uint16_t chipSelectGpioPin, GPIO_TypeDef* chipSelectGpioPort, size_t maxRecvSize);
uint16_t getChipSelectGpioPin() const;
GPIO_TypeDef* getChipSelectGpioPort();
@@ -60,8 +55,8 @@ private:
spi::SpiModes spiMode;
spi::TransferModes transferMode;
volatile spi::TransferStates transferState = spi::TransferStates::IDLE;
stm32h7::GpioCfg csGpio;
uint16_t chipSelectGpioPin;
GPIO_TypeDef* chipSelectGpioPort;
// The MSP configuration is cached here. Be careful when using this, it is automatically
// deleted by the SPI communication interface if it is not required anymore!
spi::MspCfgBase* mspCfg = nullptr;

View File

@@ -118,40 +118,40 @@ void spi::halMspInitPolling(SPI_HandleTypeDef* hspi, MspCfgBase* cfgBase) {
GPIO_InitTypeDef GPIO_InitStruct = {};
/*##-1- Enable peripherals and GPIO Clocks #################################*/
/* Enable GPIO TX/RX clock */
cfg->setupCb();
cfg->setupMacroWrapper();
/*##-2- Configure peripheral GPIO ##########################################*/
/* SPI SCK GPIO pin configuration */
GPIO_InitStruct.Pin = cfg->sck.pin;
GPIO_InitStruct.Pin = cfg->sckPin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = cfg->sck.altFnc;
HAL_GPIO_Init(cfg->sck.port, &GPIO_InitStruct);
GPIO_InitStruct.Alternate = cfg->sckAlternateFunction;
HAL_GPIO_Init(cfg->sckPort, &GPIO_InitStruct);
/* SPI MISO GPIO pin configuration */
GPIO_InitStruct.Pin = cfg->miso.pin;
GPIO_InitStruct.Alternate = cfg->miso.altFnc;
HAL_GPIO_Init(cfg->miso.port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = cfg->misoPin;
GPIO_InitStruct.Alternate = cfg->misoAlternateFunction;
HAL_GPIO_Init(cfg->misoPort, &GPIO_InitStruct);
/* SPI MOSI GPIO pin configuration */
GPIO_InitStruct.Pin = cfg->mosi.pin;
GPIO_InitStruct.Alternate = cfg->mosi.altFnc;
HAL_GPIO_Init(cfg->mosi.port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = cfg->mosiPin;
GPIO_InitStruct.Alternate = cfg->mosiAlternateFunction;
HAL_GPIO_Init(cfg->mosiPort, &GPIO_InitStruct);
}
void spi::halMspDeinitPolling(SPI_HandleTypeDef* hspi, MspCfgBase* cfgBase) {
auto cfg = reinterpret_cast<MspPollingConfigStruct*>(cfgBase);
// Reset peripherals
cfg->cleanupCb();
cfg->cleanUpMacroWrapper();
// Disable peripherals and GPIO Clocks
/* Configure SPI SCK as alternate function */
HAL_GPIO_DeInit(cfg->sck.port, cfg->sck.pin);
HAL_GPIO_DeInit(cfg->sckPort, cfg->sckPin);
/* Configure SPI MISO as alternate function */
HAL_GPIO_DeInit(cfg->miso.port, cfg->miso.pin);
HAL_GPIO_DeInit(cfg->misoPort, cfg->misoPin);
/* Configure SPI MOSI as alternate function */
HAL_GPIO_DeInit(cfg->mosi.port, cfg->mosi.pin);
HAL_GPIO_DeInit(cfg->mosiPort, cfg->mosiPin);
}
void spi::halMspInitInterrupt(SPI_HandleTypeDef* hspi, MspCfgBase* cfgBase) {

View File

@@ -2,7 +2,6 @@
#define FSFW_HAL_STM32H7_SPI_MSPINIT_H_
#include "spiDefinitions.h"
#include "../definitions.h"
#include "../dma.h"
#include "stm32h7xx_hal_spi.h"
@@ -13,8 +12,6 @@
extern "C" {
#endif
using mspCb = void (*) (void);
/**
* @brief This file provides MSP implementation for DMA, IRQ and Polling mode for the
* SPI peripheral. This configuration is required for the SPI communication to work.
@@ -22,37 +19,27 @@ using mspCb = void (*) (void);
namespace spi {
struct MspCfgBase {
MspCfgBase();
MspCfgBase(stm32h7::GpioCfg sck, stm32h7::GpioCfg mosi, stm32h7::GpioCfg miso,
mspCb cleanupCb = nullptr, mspCb setupCb = nullptr):
sck(sck), mosi(mosi), miso(miso), cleanupCb(cleanupCb),
setupCb(setupCb) {}
virtual ~MspCfgBase() = default;
stm32h7::GpioCfg sck;
stm32h7::GpioCfg mosi;
stm32h7::GpioCfg miso;
void (* cleanUpMacroWrapper) (void) = nullptr;
void (* setupMacroWrapper) (void) = nullptr;
mspCb cleanupCb = nullptr;
mspCb setupCb = nullptr;
GPIO_TypeDef* sckPort = nullptr;
uint32_t sckPin = 0;
uint8_t sckAlternateFunction = 0;
GPIO_TypeDef* mosiPort = nullptr;
uint32_t mosiPin = 0;
uint8_t mosiAlternateFunction = 0;
GPIO_TypeDef* misoPort = nullptr;
uint32_t misoPin = 0;
uint8_t misoAlternateFunction = 0;
};
struct MspPollingConfigStruct: public MspCfgBase {
MspPollingConfigStruct(): MspCfgBase() {};
MspPollingConfigStruct(stm32h7::GpioCfg sck, stm32h7::GpioCfg mosi, stm32h7::GpioCfg miso,
mspCb cleanupCb = nullptr, mspCb setupCb = nullptr):
MspCfgBase(sck, mosi, miso, cleanupCb, setupCb) {}
};
struct MspPollingConfigStruct: public MspCfgBase {};
/* A valid instance of this struct must be passed to the MSP initialization function as a void*
argument */
struct MspIrqConfigStruct: public MspPollingConfigStruct {
MspIrqConfigStruct(): MspPollingConfigStruct() {};
MspIrqConfigStruct(stm32h7::GpioCfg sck, stm32h7::GpioCfg mosi, stm32h7::GpioCfg miso,
mspCb cleanupCb = nullptr, mspCb setupCb = nullptr):
MspPollingConfigStruct(sck, mosi, miso, cleanupCb, setupCb) {}
SpiBus spiBus = SpiBus::SPI_1;
user_handler_t spiIrqHandler = nullptr;
user_args_t spiUserArgs = nullptr;
@@ -66,16 +53,11 @@ struct MspIrqConfigStruct: public MspPollingConfigStruct {
/* A valid instance of this struct must be passed to the MSP initialization function as a void*
argument */
struct MspDmaConfigStruct: public MspIrqConfigStruct {
MspDmaConfigStruct(): MspIrqConfigStruct() {};
MspDmaConfigStruct(stm32h7::GpioCfg sck, stm32h7::GpioCfg mosi, stm32h7::GpioCfg miso,
mspCb cleanupCb = nullptr, mspCb setupCb = nullptr):
MspIrqConfigStruct(sck, mosi, miso, cleanupCb, setupCb) {}
void (* dmaClkEnableWrapper) (void) = nullptr;
dma::DMAIndexes txDmaIndex = dma::DMAIndexes::DMA_1;
dma::DMAIndexes rxDmaIndex = dma::DMAIndexes::DMA_1;
dma::DMAStreams txDmaStream = dma::DMAStreams::STREAM_0;
dma::DMAStreams rxDmaStream = dma::DMAStreams::STREAM_0;
dma::DMAIndexes txDmaIndex;
dma::DMAIndexes rxDmaIndex;
dma::DMAStreams txDmaStream;
dma::DMAStreams rxDmaStream;
IRQn_Type txDmaIrqNumber = DMA1_Stream0_IRQn;
IRQn_Type rxDmaIrqNumber = DMA1_Stream1_IRQn;
// Priorities for NVIC

View File

@@ -1,4 +1,4 @@
#include "fsfw_hal/stm32h7/spi/stm32h743zi.h"
#include "fsfw_hal/stm32h7/spi/stm32h743ziSpi.h"
#include "fsfw_hal/stm32h7/spi/spiCore.h"
#include "fsfw_hal/stm32h7/spi/spiInterrupts.h"
@@ -22,27 +22,27 @@ void spiDmaClockEnableWrapper() {
__HAL_RCC_DMA2_CLK_ENABLE();
}
void stm32h7::h743zi::standardPollingCfg(spi::MspPollingConfigStruct& cfg) {
cfg.setupCb = &spiSetupWrapper;
cfg.cleanupCb = &spiCleanUpWrapper;
cfg.sck.port = GPIOA;
cfg.sck.pin = GPIO_PIN_5;
cfg.miso.port = GPIOA;
cfg.miso.pin = GPIO_PIN_6;
cfg.mosi.port = GPIOA;
cfg.mosi.pin = GPIO_PIN_7;
cfg.sck.altFnc = GPIO_AF5_SPI1;
cfg.mosi.altFnc = GPIO_AF5_SPI1;
cfg.miso.altFnc = GPIO_AF5_SPI1;
void spi::h743zi::standardPollingCfg(MspPollingConfigStruct& cfg) {
cfg.setupMacroWrapper = &spiSetupWrapper;
cfg.cleanUpMacroWrapper = &spiCleanUpWrapper;
cfg.sckPort = GPIOA;
cfg.sckPin = GPIO_PIN_5;
cfg.misoPort = GPIOA;
cfg.misoPin = GPIO_PIN_6;
cfg.mosiPort = GPIOA;
cfg.mosiPin = GPIO_PIN_7;
cfg.sckAlternateFunction = GPIO_AF5_SPI1;
cfg.mosiAlternateFunction = GPIO_AF5_SPI1;
cfg.misoAlternateFunction = GPIO_AF5_SPI1;
}
void stm32h7::h743zi::standardInterruptCfg(spi::MspIrqConfigStruct& cfg, IrqPriorities spiIrqPrio,
void spi::h743zi::standardInterruptCfg(MspIrqConfigStruct& cfg, IrqPriorities spiIrqPrio,
IrqPriorities spiSubprio) {
// High, but works on FreeRTOS as well (priorities range from 0 to 15)
cfg.preEmptPriority = spiIrqPrio;
cfg.subpriority = spiSubprio;
cfg.spiIrqNumber = SPI1_IRQn;
cfg.spiBus = spi::SpiBus::SPI_1;
cfg.spiBus = SpiBus::SPI_1;
user_handler_t spiUserHandler = nullptr;
user_args_t spiUserArgs = nullptr;
getSpiUserHandler(spi::SpiBus::SPI_1, &spiUserHandler, &spiUserArgs);
@@ -55,7 +55,7 @@ void stm32h7::h743zi::standardInterruptCfg(spi::MspIrqConfigStruct& cfg, IrqPrio
standardPollingCfg(cfg);
}
void stm32h7::h743zi::standardDmaCfg(spi::MspDmaConfigStruct& cfg, IrqPriorities spiIrqPrio,
void spi::h743zi::standardDmaCfg(MspDmaConfigStruct& cfg, IrqPriorities spiIrqPrio,
IrqPriorities txIrqPrio, IrqPriorities rxIrqPrio, IrqPriorities spiSubprio,
IrqPriorities txSubprio, IrqPriorities rxSubprio) {
cfg.dmaClkEnableWrapper = &spiDmaClockEnableWrapper;

View File

@@ -3,20 +3,21 @@
#include "mspInit.h"
namespace stm32h7 {
namespace spi {
namespace h743zi {
void standardPollingCfg(spi::MspPollingConfigStruct& cfg);
void standardInterruptCfg(spi::MspIrqConfigStruct& cfg, IrqPriorities spiIrqPrio,
void standardPollingCfg(MspPollingConfigStruct& cfg);
void standardInterruptCfg(MspIrqConfigStruct& cfg, IrqPriorities spiIrqPrio,
IrqPriorities spiSubprio = HIGHEST);
void standardDmaCfg(spi::MspDmaConfigStruct& cfg, IrqPriorities spiIrqPrio,
void standardDmaCfg(MspDmaConfigStruct& cfg, IrqPriorities spiIrqPrio,
IrqPriorities txIrqPrio, IrqPriorities rxIrqPrio,
IrqPriorities spiSubprio = HIGHEST, IrqPriorities txSubPrio = HIGHEST,
IrqPriorities rxSubprio = HIGHEST);
}
}
}
}
#endif /* FSFW_HAL_STM32H7_SPI_STM32H743ZISPI_H_ */

View File

@@ -1,49 +1,23 @@
if(DEFINED TARGET_NAME)
target_include_directories(${TARGET_NAME} PRIVATE
target_include_directories(${TARGET_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
)
)
target_sources(${TARGET_NAME} PRIVATE
ipc/missionMessageTypes.cpp
pollingsequence/PollingSequenceFactory.cpp
objects/FsfwFactory.cpp
)
# If a special translation file for object IDs exists, compile it.
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/objects/translateObjects.cpp")
target_sources(${TARGET_NAME} PRIVATE
ipc/missionMessageTypes.cpp
pollingsequence/PollingSequenceFactory.cpp
objects/FsfwFactory.cpp
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(${TARGET_NAME} PRIVATE
events/translateEvents.cpp
)
# If a special translation file for object IDs exists, compile it.
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/objects/translateObjects.cpp")
target_sources(${TARGET_NAME} 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(${TARGET_NAME} PRIVATE
events/translateEvents.cpp
)
endif()
else()
target_include_directories(${LIB_FSFW_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
)
target_sources(${LIB_FSFW_NAME} PRIVATE
ipc/missionMessageTypes.cpp
pollingsequence/PollingSequenceFactory.cpp
objects/FsfwFactory.cpp
)
# If a special translation file for object IDs exists, compile it.
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/objects/translateObjects.cpp")
target_sources(${LIB_FSFW_NAME} 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(${LIB_FSFW_NAME} PRIVATE
events/translateEvents.cpp
)
endif()
endif()

View File

@@ -7,7 +7,7 @@
#include <fsfw/tmtcpacket/pus/tm/TmPacketStored.h>
#include <fsfw/tmtcservices/CommandingServiceBase.h>
#include <fsfw/tmtcservices/PusServiceBase.h>
#include <fsfw/internalerror/InternalErrorReporter.h>
#include <fsfw/internalError/InternalErrorReporter.h>
#include <cstdint>
@@ -48,6 +48,6 @@ void Factory::setStaticFrameworkObjectIds() {
DeviceHandlerFailureIsolation::powerConfirmationId = objects::NO_OBJECT;
TmPacketBase::timeStamperId = objects::NO_OBJECT;
TmPacketStored::timeStamperId = objects::NO_OBJECT;
}

View File

@@ -1,8 +0,0 @@
#!/bin/bash
if [[ ! -f README.md ]]; then
cd ..
fi
find ./src -iname *.h -o -iname *.cpp -o -iname *.c | xargs clang-format --style=file -i
find ./hal -iname *.h -o -iname *.cpp -o -iname *.c | xargs clang-format --style=file -i
find ./tests -iname *.h -o -iname *.cpp -o -iname *.c | xargs clang-format --style=file -i

View File

@@ -1,188 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*
"""Small portable helper script to generate test or doc configuration for the
flight software framework
"""
import os
import argparse
import webbrowser
import shutil
import sys
import time
from typing import List
UNITTEST_FOLDER_NAME = 'build-tests'
DOCS_FOLDER_NAME = 'build-docs'
def main():
parser = argparse.ArgumentParser(description="FSFW helper script")
choices = ('docs', 'tests')
parser.add_argument(
'type', metavar='type', choices=choices,
help=f'Target type. Choices: {choices}'
)
parser.add_argument(
'-a', '--all', action='store_true',
help='Create, build and open specified type'
)
parser.add_argument(
'-c', '--create', action='store_true',
help='Create docs or test build configuration'
)
parser.add_argument(
'-b', '--build', action='store_true',
help='Build the specified type'
)
parser.add_argument(
'-o', '--open', action='store_true',
help='Open test or documentation data in webbrowser'
)
args = parser.parse_args()
if args.all:
args.build = True
args.create = True
args.open = True
elif not args.build and not args.create and not args.open:
print(
'Please select at least one operation to perform. '
'Use helper.py -h for more information'
)
sys.exit(1)
# This script can be called from root and from script folder
if not os.path.isfile('README.md'):
os.chdir('..')
build_dir_list = []
if not args.create:
build_dir_list = build_build_dir_list()
if args.type == 'tests':
handle_tests_type(args, build_dir_list)
elif args.type == 'docs':
handle_docs_type(args, build_dir_list)
else:
print('Invalid or unknown type')
sys.exit(1)
def handle_docs_type(args, build_dir_list: list):
if args.create:
if os.path.exists(DOCS_FOLDER_NAME):
shutil.rmtree(DOCS_FOLDER_NAME)
create_docs_build_cfg()
build_directory = DOCS_FOLDER_NAME
elif len(build_dir_list) == 0:
print('No valid CMake docs build directory found. Trying to set up docs build system')
shutil.rmtree(DOCS_FOLDER_NAME)
create_docs_build_cfg()
build_directory = DOCS_FOLDER_NAME
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)
os.chdir(build_directory)
if args.build:
os.system('cmake --build . -j')
if args.open:
if not os.path.isfile('docs/sphinx/index.html'):
# try again..
os.system('cmake --build . -j')
if not os.path.isfile('docs/sphinx/index.html'):
print(
"No Sphinx documentation file detected. "
"Try to build it first with the -b argument"
)
sys.exit(1)
webbrowser.open('docs/sphinx/index.html')
def handle_tests_type(args, build_dir_list: list):
if args.create:
if os.path.exists(UNITTEST_FOLDER_NAME):
shutil.rmtree(UNITTEST_FOLDER_NAME)
create_tests_build_cfg()
build_directory = UNITTEST_FOLDER_NAME
elif len(build_dir_list) == 0:
print(
'No valid CMake tests build directory found. '
'Trying to set up test build system'
)
create_tests_build_cfg()
build_directory = UNITTEST_FOLDER_NAME
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)
os.chdir(build_directory)
if args.build:
perform_lcov_operation(build_directory, False)
if args.open:
if not os.path.isdir('fsfw-tests_coverage'):
print("No Unittest folder detected. Try to build them first with the -b argument")
sys.exit(1)
webbrowser.open('fsfw-tests_coverage/index.html')
def create_tests_build_cfg():
os.mkdir(UNITTEST_FOLDER_NAME)
os.chdir(UNITTEST_FOLDER_NAME)
os.system('cmake -DFSFW_OSAL=host -DFSFW_BUILD_UNITTESTS=ON ..')
os.chdir('..')
def create_docs_build_cfg():
os.mkdir(DOCS_FOLDER_NAME)
os.chdir(DOCS_FOLDER_NAME)
os.system('cmake -DFSFW_OSAL=host -DFSFW_BUILD_DOCS=ON ..')
os.chdir('..')
def build_build_dir_list() -> list:
build_dir_list = []
for directory in os.listdir("."):
if os.path.isdir(directory):
os.chdir(directory)
build_dir_list = check_for_cmake_build_dir(build_dir_list)
os.chdir("..")
return build_dir_list
def check_for_cmake_build_dir(build_dir_list: list) -> list:
if os.path.isfile("CMakeCache.txt"):
build_dir_list.append(os.getcwd())
return build_dir_list
def perform_lcov_operation(directory: str, chdir: bool):
if chdir:
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: ")
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

@@ -16,3 +16,5 @@ target_include_directories(${LIB_FSFW_NAME} PRIVATE
target_include_directories(${LIB_FSFW_NAME} INTERFACE
${CMAKE_CURRENT_BINARY_DIR}
)
configure_file(fsfw/FSFW.h.in fsfw/FSFW.h)

View File

@@ -16,37 +16,13 @@
#cmakedefine FSFW_ADD_MONITORING
#cmakedefine FSFW_ADD_SGP4_PROPAGATOR
// FSFW core defines
#ifndef FSFW_CPP_OSTREAM_ENABLED
#define FSFW_CPP_OSTREAM_ENABLED 1
#endif /* FSFW_CPP_OSTREAM_ENABLED */
#ifndef FSFW_VERBOSE_LEVEL
#define FSFW_VERBOSE_LEVEL 1
#endif /* FSFW_VERBOSE_LEVEL */
#ifndef FSFW_USE_REALTIME_FOR_LINUX
#define FSFW_USE_REALTIME_FOR_LINUX 0
#endif /* FSFW_USE_REALTIME_FOR_LINUX */
#ifndef FSFW_NO_C99_IO
#define FSFW_NO_C99_IO 0
#endif /* FSFW_NO_C99_IO */
#ifndef FSFW_USE_PUS_C_TELEMETRY
#define FSFW_USE_PUS_C_TELEMETRY 1
#endif /* FSFW_USE_PUS_C_TELEMETRY */
#ifndef FSFW_USE_PUS_C_TELECOMMANDS
#define FSFW_USE_PUS_C_TELECOMMANDS 1
#ifndef FSFW_TCP_RECV_WIRETAPPING_ENABLED
#define FSFW_TCP_RECV_WIRETAPPING_ENABLED 0
#endif
// FSFW HAL defines
// Can be used for low-level debugging of the SPI bus
#ifndef FSFW_HAL_SPI_WIRETAPPING
#define FSFW_HAL_SPI_WIRETAPPING 0
/* Can be used for low-level debugging of the SPI bus */
#ifndef FSFW_HAL_LINUX_SPI_WIRETAPPING
#define FSFW_HAL_LINUX_SPI_WIRETAPPING 0
#endif
#ifndef FSFW_HAL_L3GD20_GYRO_DEBUG
@@ -58,7 +34,7 @@
#endif /* FSFW_HAL_RM3100_MGM_DEBUG */
#ifndef FSFW_HAL_LIS3MDL_MGM_DEBUG
#define FSFW_HAL_LIS3MDL_MGM_DEBUG 0
#define FSFW_HAL_LIS3MDL_MGM_DEBUG 0
#endif /* FSFW_HAL_LIS3MDL_MGM_DEBUG */
#endif /* FSFW_FSFW_H_ */

10
src/fsfw/FSFWVersion.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef FSFW_VERSION_H_
#define FSFW_VERSION_H_
const char* const FSFW_VERSION_NAME = "ASTP";
#define FSFW_VERSION 1
#define FSFW_SUBVERSION 2
#define FSFW_REVISION 0
#endif /* FSFW_VERSION_H_ */

View File

@@ -1,9 +0,0 @@
#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

@@ -110,7 +110,7 @@ ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(uint8_t **buffer
for (uint16_t count = 0; count < fillCount; count++) {
if(registeredVariables[count]->isValid()) {
/* Set bit at correct position */
bitutil::set(validityPtr + validBufferIndex, validBufferIndexBit);
bitutil::bitSet(validityPtr + validBufferIndex, validBufferIndexBit);
}
if(validBufferIndexBit == 7) {
validBufferIndex ++;
@@ -156,8 +156,8 @@ ReturnValue_t LocalPoolDataSetBase::deSerializeWithValidityBuffer(
uint8_t validBufferIndexBit = 0;
for (uint16_t count = 0; count < fillCount; count++) {
// set validity buffer here.
bool nextVarValid = false;
bitutil::get(*buffer + validBufferIndex, validBufferIndexBit, nextVarValid);
bool nextVarValid = bitutil::bitGet(*buffer +
validBufferIndex, validBufferIndexBit);
registeredVariables[count]->setValid(nextVarValid);
if(validBufferIndexBit == 7) {

View File

@@ -85,9 +85,10 @@ public:
* Called by DHB in the GET_WRITE doGetWrite().
* Get send confirmation that the data in sendMessage() was sent successfully.
* @param cookie
* @return - @c RETURN_OK if data was sent successfull
* - Everything else triggers falure event with
* returnvalue as parameter 1
* @return
* - @c RETURN_OK if data was sent successfully but a reply is expected
* - NO_REPLY_EXPECTED if data was sent successfully and no reply is expected
* - Everything else to indicate failure
*/
virtual ReturnValue_t getSendSuccess(CookieIF *cookie) = 0;

View File

@@ -120,7 +120,8 @@ public:
static const ReturnValue_t WRONG_MODE_FOR_COMMAND = MAKE_RETURN_CODE(0xA5);
static const ReturnValue_t TIMEOUT = MAKE_RETURN_CODE(0xA6);
static const ReturnValue_t BUSY = MAKE_RETURN_CODE(0xA7);
static const ReturnValue_t NO_REPLY_EXPECTED = MAKE_RETURN_CODE(0xA8); //!< Used to indicate that this is a command-only command.
//!< Used to indicate that this is a command-only command.
static const ReturnValue_t NO_REPLY_EXPECTED = MAKE_RETURN_CODE(0xA8);
static const ReturnValue_t NON_OP_TEMPERATURE = MAKE_RETURN_CODE(0xA9);
static const ReturnValue_t COMMAND_NOT_IMPLEMENTED = MAKE_RETURN_CODE(0xAA);

View File

@@ -29,8 +29,6 @@ enum: uint8_t {
PUS_SERVICE_9 = 89,
PUS_SERVICE_17 = 97,
PUS_SERVICE_23 = 103,
MGM_LIS3MDL = 106,
MGM_RM3100 = 107,
FW_SUBSYSTEM_ID_RANGE
};

View File

@@ -165,9 +165,11 @@ ReturnValue_t DleEncoder::decodeStreamEscaped(const uint8_t *sourceStream, size_
if (sourceStream[encodedIndex++] != STX_CHAR) {
return DECODING_ERROR;
}
while ((encodedIndex < sourceStreamLen) and (decodedIndex < maxDestStreamlen)) {
switch(sourceStream[encodedIndex]) {
case(DLE_CHAR): {
while ((encodedIndex < sourceStreamLen)
and (decodedIndex < maxDestStreamlen)
and (sourceStream[encodedIndex] != ETX_CHAR)
and (sourceStream[encodedIndex] != STX_CHAR)) {
if (sourceStream[encodedIndex] == DLE_CHAR) {
if(encodedIndex + 1 >= sourceStreamLen) {
//reached the end of the sourceStream
*readLen = sourceStreamLen;
@@ -195,33 +197,29 @@ ReturnValue_t DleEncoder::decodeStreamEscaped(const uint8_t *sourceStream, size_
}
}
++encodedIndex;
break;
}
case(STX_CHAR): {
*readLen = encodedIndex;
return DECODING_ERROR;
}
case(ETX_CHAR): {
*readLen = ++encodedIndex;
*decodedLen = decodedIndex;
return RETURN_OK;
}
default: {
else {
destStream[decodedIndex] = sourceStream[encodedIndex];
break;
}
}
++encodedIndex;
++decodedIndex;
}
if(decodedIndex == maxDestStreamlen) {
//so far we did not find anything wrong here, so let user try again
*readLen = 0;
return STREAM_TOO_SHORT;
} else {
*readLen = encodedIndex;
return DECODING_ERROR;
if (sourceStream[encodedIndex] != ETX_CHAR) {
if(decodedIndex == maxDestStreamlen) {
//so far we did not find anything wrong here, so let user try again
*readLen = 0;
return STREAM_TOO_SHORT;
}
else {
*readLen = ++encodedIndex;
return DECODING_ERROR;
}
}
else {
*readLen = ++encodedIndex;
*decodedLen = decodedIndex;
return RETURN_OK;
}
}

View File

@@ -3,17 +3,18 @@
PeriodicOperationDivider::PeriodicOperationDivider(uint32_t divider,
bool resetAutomatically): resetAutomatically(resetAutomatically),
divider(divider) {
counter(divider), divider(divider) {
}
bool PeriodicOperationDivider::checkAndIncrement() {
bool opNecessary = check();
if(opNecessary and resetAutomatically) {
resetCounter();
}
else {
counter++;
if(opNecessary) {
if(resetAutomatically) {
counter = 1;
}
return opNecessary;
}
counter++;
return opNecessary;
}
@@ -24,8 +25,10 @@ bool PeriodicOperationDivider::check() {
return false;
}
void PeriodicOperationDivider::resetCounter() {
counter = 1;
counter = 0;
}
void PeriodicOperationDivider::setDivider(uint32_t newDivider) {

View File

@@ -16,15 +16,16 @@ public:
/**
* Initialize with the desired divider and specify whether the internal
* counter will be reset automatically.
* @param divider Value of 0 or 1 will cause #check and #checkAndIncrement to always return
* true
* @param divider
* @param resetAutomatically
*/
PeriodicOperationDivider(uint32_t divider, bool resetAutomatically = true);
/**
* Check whether operation is necessary. If an operation is necessary and the class has been
* configured to be reset automatically, the counter will be reset to 1 automatically
* Check whether operation is necessary.
* If an operation is necessary and the class has been
* configured to be reset automatically, the counter will be reset.
*
* @return
* -@c true if the counter is larger or equal to the divider
@@ -33,7 +34,8 @@ public:
bool checkAndIncrement();
/**
* Checks whether an operation is necessary. This function will not increment the counter.
* Checks whether an operation is necessary.
* This function will not increment the counter!
* @return
* -@c true if the counter is larger or equal to the divider
* -@c false otherwise
@@ -41,7 +43,7 @@ public:
bool check();
/**
* Can be used to reset the counter to 1 manually
* Can be used to reset the counter to 0 manually.
*/
void resetCounter();
uint32_t getCounter() const;
@@ -52,10 +54,9 @@ public:
*/
void setDivider(uint32_t newDivider);
uint32_t getDivider() const;
private:
bool resetAutomatically = true;
uint32_t counter = 1;
uint32_t counter = 0;
uint32_t divider = 0;
};

View File

@@ -45,18 +45,18 @@ void arrayprinter::printHex(const uint8_t *data, size_t size,
std::cout << "\r" << std::endl;
}
std::cout << "hex [" << std::setfill('0') << std::hex;
std::cout << "[" << std::hex;
for(size_t i = 0; i < size; i++) {
std::cout << std::setw(2) << static_cast<int>(data[i]);
std::cout << "0x" << static_cast<int>(data[i]);
if(i < size - 1) {
std::cout << ",";
std::cout << " , ";
if(i > 0 and (i + 1) % maxCharPerLine == 0) {
std::cout << std::endl;
}
}
}
std::cout << std::dec << std::setfill(' ');
std::cout << std::dec;
std::cout << "]" << std::endl;
#else
// General format: 0x01, 0x02, 0x03 so it is number of chars times 6
@@ -69,16 +69,16 @@ void arrayprinter::printHex(const uint8_t *data, size_t size,
break;
}
currentPos += snprintf(printBuffer + currentPos, 6, "%02x", data[i]);
currentPos += snprintf(printBuffer + currentPos, 6, "0x%02x", data[i]);
if(i < size - 1) {
currentPos += sprintf(printBuffer + currentPos, ",");
currentPos += sprintf(printBuffer + currentPos, ", ");
if(i > 0 and (i + 1) % maxCharPerLine == 0) {
currentPos += sprintf(printBuffer + currentPos, "\n");
}
}
}
#if FSFW_DISABLE_PRINTOUT == 0
printf("hex [%s]\n", printBuffer);
printf("[%s]\n", printBuffer);
#endif /* FSFW_DISABLE_PRINTOUT == 0 */
#endif
}
@@ -90,11 +90,11 @@ void arrayprinter::printDec(const uint8_t *data, size_t size,
std::cout << "\r" << std::endl;
}
std::cout << "dec [" << std::dec;
std::cout << "[" << std::dec;
for(size_t i = 0; i < size; i++) {
std::cout << static_cast<int>(data[i]);
if(i < size - 1){
std::cout << ",";
std::cout << " , ";
if(i > 0 and (i + 1) % maxCharPerLine == 0) {
std::cout << std::endl;
}
@@ -114,14 +114,14 @@ void arrayprinter::printDec(const uint8_t *data, size_t size,
currentPos += snprintf(printBuffer + currentPos, 3, "%d", data[i]);
if(i < size - 1) {
currentPos += sprintf(printBuffer + currentPos, ",");
currentPos += sprintf(printBuffer + currentPos, ", ");
if(i > 0 and (i + 1) % maxCharPerLine == 0) {
currentPos += sprintf(printBuffer + currentPos, "\n");
}
}
}
#if FSFW_DISABLE_PRINTOUT == 0
printf("dec [%s]\n", printBuffer);
printf("[%s]\n", printBuffer);
#endif /* FSFW_DISABLE_PRINTOUT == 0 */
#endif
}

View File

@@ -1,6 +1,6 @@
#include "fsfw/globalfunctions/bitutility.h"
void bitutil::set(uint8_t *byte, uint8_t position) {
void bitutil::bitSet(uint8_t *byte, uint8_t position) {
if(position > 7) {
return;
}
@@ -8,7 +8,7 @@ void bitutil::set(uint8_t *byte, uint8_t position) {
*byte |= 1 << shiftNumber;
}
void bitutil::toggle(uint8_t *byte, uint8_t position) {
void bitutil::bitToggle(uint8_t *byte, uint8_t position) {
if(position > 7) {
return;
}
@@ -16,7 +16,7 @@ void bitutil::toggle(uint8_t *byte, uint8_t position) {
*byte ^= 1 << shiftNumber;
}
void bitutil::clear(uint8_t *byte, uint8_t position) {
void bitutil::bitClear(uint8_t *byte, uint8_t position) {
if(position > 7) {
return;
}
@@ -24,11 +24,10 @@ void bitutil::clear(uint8_t *byte, uint8_t position) {
*byte &= ~(1 << shiftNumber);
}
bool bitutil::get(const uint8_t *byte, uint8_t position, bool& bit) {
bool bitutil::bitGet(const uint8_t *byte, uint8_t position) {
if(position > 7) {
return false;
}
uint8_t shiftNumber = position + (7 - 2 * position);
bit = *byte & (1 << shiftNumber);
return true;
return *byte & (1 << shiftNumber);
}

View File

@@ -5,36 +5,13 @@
namespace bitutil {
// Helper functions for manipulating the individual bits of a byte.
// Position refers to n-th bit of a byte, going from 0 (most significant bit) to
// 7 (least significant bit)
/**
* @brief Set the bit in a given byte
* @param byte
* @param position
*/
void set(uint8_t* byte, uint8_t position);
/**
* @brief Toggle the bit in a given byte
* @param byte
* @param position
*/
void toggle(uint8_t* byte, uint8_t position);
/**
* @brief Clear the bit in a given byte
* @param byte
* @param position
*/
void clear(uint8_t* byte, uint8_t position);
/**
* @brief Get the bit in a given byte
* @param byte
* @param position
* @param If the input is valid, this will be set to true if the bit is set and false otherwise.
* @return False if position is invalid, True otherwise
*/
bool get(const uint8_t* byte, uint8_t position, bool& bit);
/* Helper functions for manipulating the individual bits of a byte.
Position refers to n-th bit of a byte, going from 0 (most significant bit) to
7 (least significant bit) */
void bitSet(uint8_t* byte, uint8_t position);
void bitToggle(uint8_t* byte, uint8_t position);
void bitClear(uint8_t* byte, uint8_t position);
bool bitGet(const uint8_t* byte, uint8_t position);
}

View File

@@ -2,9 +2,8 @@
#define TIMEVALOPERATIONS_H_
#include <stdint.h>
#include <fsfw/platform.h>
#ifdef PLATFORM_WIN
#ifdef WIN32
#include <winsock2.h>
#else
#include <sys/time.h>

View File

@@ -1,13 +0,0 @@
#ifndef FSFW_SRC_FSFW_MEMORY_FILESYSTEMARGS_H_
#define FSFW_SRC_FSFW_MEMORY_FILESYSTEMARGS_H_
/**
* Empty base interface which can be implemented by to pass arguments via the HasFileSystemIF.
* Users can then dynamic_cast the base pointer to the require child pointer.
*/
class FileSystemArgsIF {
public:
virtual~ FileSystemArgsIF() {};
};
#endif /* FSFW_SRC_FSFW_MEMORY_FILESYSTEMARGS_H_ */

View File

@@ -1,10 +1,9 @@
#ifndef FSFW_MEMORY_HASFILESYSTEMIF_H_
#define FSFW_MEMORY_HASFILESYSTEMIF_H_
#include "FileSystemArgsIF.h"
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
#include "fsfw/returnvalues/FwClassIds.h"
#include "fsfw/ipc/messageQueueDefinitions.h"
#include "../returnvalues/HasReturnvaluesIF.h"
#include "../returnvalues/FwClassIds.h"
#include "../ipc/messageQueueDefinitions.h"
#include <cstddef>
@@ -60,7 +59,7 @@ public:
*/
virtual ReturnValue_t appendToFile(const char* repositoryPath,
const char* filename, const uint8_t* data, size_t size,
uint16_t packetNumber, FileSystemArgsIF* args = nullptr) = 0;
uint16_t packetNumber, void* args = nullptr) = 0;
/**
* @brief Generic function to create a new file.
@@ -73,7 +72,7 @@ public:
*/
virtual ReturnValue_t createFile(const char* repositoryPath,
const char* filename, const uint8_t* data = nullptr,
size_t size = 0, FileSystemArgsIF* args = nullptr) = 0;
size_t size = 0, void* args = nullptr) = 0;
/**
* @brief Generic function to delete a file.
@@ -83,29 +82,23 @@ public:
* @return
*/
virtual ReturnValue_t removeFile(const char* repositoryPath,
const char* filename, FileSystemArgsIF* args = nullptr) = 0;
const char* filename, void* args = nullptr) = 0;
/**
* @brief Generic function to create a directory
* @param repositoryPath
* @param Equivalent to the -p flag in Unix systems. If some required parent directories
* do not exist, create them as well
* @param args Any other arguments which an implementation might require
* @return
*/
virtual ReturnValue_t createDirectory(const char* repositoryPath, const char* dirname,
bool createParentDirs, FileSystemArgsIF* args = nullptr) = 0;
virtual ReturnValue_t createDirectory(const char* repositoryPath, void* args = nullptr) = 0;
/**
* @brief Generic function to remove a directory
* @param repositoryPath
* @param args Any other arguments which an implementation might require
*/
virtual ReturnValue_t removeDirectory(const char* repositoryPath, const char* dirname,
bool deleteRecurively = false, FileSystemArgsIF* args = nullptr) = 0;
virtual ReturnValue_t renameFile(const char* repositoryPath, const char* oldFilename,
const char* newFilename, FileSystemArgsIF* args = nullptr) = 0;
virtual ReturnValue_t removeDirectory(const char* repositoryPath,
bool deleteRecurively = false, void* args = nullptr) = 0;
};

View File

@@ -5,13 +5,11 @@
#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/ipc/MessageQueueSenderIF.h"
#include "fsfw/ipc/MutexGuard.h"
#include "fsfw/objectmanager/ObjectManager.h"
#include "fsfw/serviceinterface/ServiceInterface.h"
#include "fsfw/tmtcservices/TmTcMessage.h"
@@ -22,14 +20,19 @@
#include <netdb.h>
#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;
TcpTmTcServer::TcpTmTcServer(object_id_t objectId, object_id_t tmtcTcpBridge,
size_t receptionBufferSize, size_t ringBufferSize, std::string customTcpServerPort,
ReceptionModes receptionMode):
SystemObject(objectId), tmtcBridgeId(tmtcTcpBridge), receptionMode(receptionMode),
tcpConfig(customTcpServerPort), receptionBuffer(receptionBufferSize),
ringBuffer(ringBufferSize, true) {
size_t receptionBufferSize, std::string customTcpServerPort):
SystemObject(objectId), tmtcBridgeId(tmtcTcpBridge),
tcpPort(customTcpServerPort), receptionBuffer(receptionBufferSize) {
if(tcpPort == "") {
tcpPort = DEFAULT_SERVER_PORT;
}
}
ReturnValue_t TcpTmTcServer::initialize() {
@@ -40,17 +43,6 @@ ReturnValue_t TcpTmTcServer::initialize() {
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);
if (tcStore == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
@@ -73,7 +65,7 @@ ReturnValue_t TcpTmTcServer::initialize() {
hints.ai_flags = AI_PASSIVE;
// Listen to all addresses (0.0.0.0) by using AI_PASSIVE in the hint flags
retval = getaddrinfo(nullptr, tcpConfig.tcpPort.c_str(), &hints, &addrResult);
retval = getaddrinfo(nullptr, tcpPort.c_str(), &hints, &addrResult);
if (retval != 0) {
handleError(Protocol::TCP, ErrorSources::GETADDRINFO_CALL);
return HasReturnvaluesIF::RETURN_FAILED;
@@ -115,7 +107,7 @@ ReturnValue_t TcpTmTcServer::performOperation(uint8_t opCode) {
// Listen for connection requests permanently for lifetime of program
while(true) {
retval = listen(listenerTcpSocket, tcpConfig.tcpBacklog);
retval = listen(listenerTcpSocket, tcpBacklog);
if(retval == SOCKET_ERROR) {
handleError(Protocol::TCP, ErrorSources::LISTEN_CALL, 500);
continue;
@@ -133,12 +125,11 @@ ReturnValue_t TcpTmTcServer::performOperation(uint8_t opCode) {
handleServerOperation(connSocket);
// Done, shut down connection and go back to listening for client requests
retval = shutdown(connSocket, SHUT_BOTH);
retval = shutdown(connSocket, SHUT_SEND);
if(retval != 0) {
handleError(Protocol::TCP, ErrorSources::SHUTDOWN_CALL);
}
closeSocket(connSocket);
connSocket = 0;
}
return HasReturnvaluesIF::RETURN_OK;
}
@@ -155,101 +146,51 @@ ReturnValue_t TcpTmTcServer::initializeAfterTaskCreation() {
return HasReturnvaluesIF::RETURN_OK;
}
void TcpTmTcServer::handleServerOperation(socket_t& connSocket) {
#if defined PLATFORM_WIN
setSocketNonBlocking(connSocket);
#endif
while (true) {
int retval = recv(
connSocket,
void TcpTmTcServer::handleServerOperation(socket_t connSocket) {
int retval = 0;
do {
// Read all telecommands sent by the client
retval = recv(connSocket,
reinterpret_cast<char*>(receptionBuffer.data()),
receptionBuffer.capacity(),
tcpConfig.tcpFlags
);
if(retval == 0) {
size_t availableReadData = ringBuffer.getAvailableReadData();
if(availableReadData > lastRingBufferSize) {
handleTcRingBufferData(availableReadData);
}
return;
tcpFlags);
if (retval > 0) {
handleTcReception(retval);
}
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) {
// Client has finished sending telecommands, send telemetry now
handleTmSending(connSocket);
}
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 {
tcpip::handleError(tcpip::Protocol::TCP, tcpip::ErrorSources::RECV_CALL, 300);
}
else {
// Should not happen
tcpip::handleError(tcpip::Protocol::TCP, tcpip::ErrorSources::RECV_CALL);
}
}
} while(retval > 0);
}
ReturnValue_t TcpTmTcServer::handleTcReception(uint8_t* spacePacket, size_t packetSize) {
if(wiretappingEnabled) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "Received TC:" << std::endl;
#else
sif::printInfo("Received TC:\n");
ReturnValue_t TcpTmTcServer::handleTcReception(size_t bytesRecvd) {
#if FSFW_TCP_RECV_WIRETAPPING_ENABLED == 1
arrayprinter::print(receptionBuffer.data(), bytesRead);
#endif
arrayprinter::print(spacePacket, packetSize);
}
if(spacePacket == nullptr or packetSize == 0) {
return HasReturnvaluesIF::RETURN_FAILED;
}
store_address_t storeId;
ReturnValue_t result = tcStore->addData(&storeId, spacePacket, packetSize);
ReturnValue_t result = tcStore->addData(&storeId, receptionBuffer.data(), bytesRecvd);
if (result != HasReturnvaluesIF::RETURN_OK) {
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "TcpTmTcServer::handleServerOperation: Data storage with packet size" <<
packetSize << " failed" << std::endl;
#else
sif::printWarning("TcpTmTcServer::handleServerOperation: Data storage with packet size %d "
"failed\n", packetSize);
sif::warning<< "TcpTmTcServer::handleServerOperation: Data storage failed." << std::endl;
sif::warning << "Packet size: " << bytesRecvd << std::endl;
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
return result;
}
TmTcMessage message(storeId);
result = MessageQueueSenderIF::sendMessage(targetTcDestination, &message);
result = MessageQueueSenderIF::sendMessage(targetTcDestination, &message);
if (result != HasReturnvaluesIF::RETURN_OK) {
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "TcpTmTcServer::handleServerOperation: "
sif::warning << "UdpTcPollingTask::handleSuccessfullTcRead: "
" 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_VERBOSE_LEVEL >= 1 */
tcStore->deleteData(storeId);
@@ -257,26 +198,21 @@ ReturnValue_t TcpTmTcServer::handleTcReception(uint8_t* spacePacket, size_t pack
return result;
}
void TcpTmTcServer::setTcpBacklog(uint8_t tcpBacklog) {
this->tcpBacklog = tcpBacklog;
}
std::string TcpTmTcServer::getTcpPort() const {
return tcpConfig.tcpPort;
return tcpPort;
}
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) {
ReturnValue_t TcpTmTcServer::handleTmSending(socket_t connSocket) {
// Access to the FIFO is mutex protected because it is filled by the bridge
MutexGuard(tmtcBridge->mutex, tmtcBridge->timeoutType, tmtcBridge->mutexTimeoutMs);
store_address_t storeId;
while((not tmtcBridge->tmFifo->empty()) and
(tmtcBridge->packetSentCounter < tmtcBridge->sentPacketsPerCycle)) {
// Send can fail, so only peek from the FIFO
tmtcBridge->tmFifo->peek(&storeId);
tmtcBridge->tmFifo->retrieve(&storeId);
// Using the store accessor will take care of deleting TM from the store automatically
ConstStorageAccessor storeAccessor(storeId);
@@ -284,134 +220,13 @@ ReturnValue_t TcpTmTcServer::handleTmSending(socket_t connSocket, bool& tmSent)
if(result != HasReturnvaluesIF::RETURN_OK) {
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,
reinterpret_cast<const char*>(storeAccessor.data()),
storeAccessor.size(),
tcpConfig.tcpTmFlags);
if(retval == static_cast<int>(storeAccessor.size())) {
// 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;
tcpTmFlags);
if(retval != static_cast<int>(storeAccessor.size())) {
tcpip::handleError(tcpip::Protocol::TCP, tcpip::ErrorSources::SEND_CALL);
}
}
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,7 +6,6 @@
#include "fsfw/platform.h"
#include "fsfw/osal/common/tcpipHelpers.h"
#include "fsfw/ipc/messageQueueDefinitions.h"
#include "fsfw/container/SimpleRingBuffer.h"
#include "fsfw/ipc/MessageQueueIF.h"
#include "fsfw/objectmanager/frameworkObjects.h"
#include "fsfw/objectmanager/SystemObject.h"
@@ -21,7 +20,6 @@
#include <vector>
class TcpTmTcBridge;
class SpacePacketParser;
/**
* @brief TCP server implementation
@@ -44,38 +42,9 @@ class TcpTmTcServer:
public TcpIpBase,
public ExecutableObjectIF {
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 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
@@ -86,21 +55,11 @@ public:
* @param customTcpServerPort The user can specify another port than the default (7301) here.
*/
TcpTmTcServer(object_id_t objectId, object_id_t tmtcTcpBridge,
size_t receptionBufferSize = RING_BUFFER_SIZE,
size_t ringBufferSize = RING_BUFFER_SIZE,
std::string customTcpServerPort = DEFAULT_SERVER_PORT,
ReceptionModes receptionMode = ReceptionModes::SPACE_PACKETS);
size_t receptionBufferSize = ETHERNET_MTU_SIZE + 1,
std::string customTcpServerPort = "");
virtual~ TcpTmTcServer();
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);
void setTcpBacklog(uint8_t tcpBacklog);
ReturnValue_t initialize() override;
ReturnValue_t performOperation(uint8_t opCode) override;
@@ -112,33 +71,25 @@ protected:
StorageManagerIF* tcStore = nullptr;
StorageManagerIF* tmStore = nullptr;
private:
static constexpr ReturnValue_t CONN_BROKEN = HasReturnvaluesIF::makeReturnCode(1, 0);
//! TMTC bridge is cached.
object_id_t tmtcBridgeId = objects::NO_OBJECT;
TcpTmTcBridge* tmtcBridge = nullptr;
bool wiretappingEnabled = false;
ReceptionModes receptionMode;
TcpConfig tcpConfig;
struct sockaddr tcpAddress;
std::string tcpPort;
int tcpFlags = 0;
socket_t listenerTcpSocket = 0;
struct sockaddr tcpAddress;
MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE;
int tcpAddrLen = sizeof(tcpAddress);
int tcpBacklog = 3;
std::vector<uint8_t> receptionBuffer;
SimpleRingBuffer ringBuffer;
std::vector<uint16_t> validPacketIds;
SpacePacketParser* spacePacketParser = nullptr;
uint8_t lastRingBufferSize = 0;
int tcpSockOpt = 0;
int tcpTmFlags = 0;
virtual void handleServerOperation(socket_t& connSocket);
ReturnValue_t handleTcReception(uint8_t* spacePacket, size_t packetSize);
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
void handleServerOperation(socket_t connSocket);
ReturnValue_t handleTcReception(size_t bytesRecvd);
ReturnValue_t handleTmSending(socket_t connSocket);
};
#endif /* FSFW_OSAL_COMMON_TCP_TMTC_SERVER_H_ */

View File

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

View File

@@ -13,8 +13,10 @@ target_sources(${LIB_FSFW_NAME}
QueueFactory.cpp
SemaphoreFactory.cpp
TaskFactory.cpp
Timer.cpp
tcpipHelpers.cpp
unixUtility.cpp
CommandExecutor.cpp
)
find_package(Threads REQUIRED)

View File

@@ -0,0 +1,185 @@
#include "CommandExecutor.h"
#include "fsfw/serviceinterface.h"
#include "fsfw/container/SimpleRingBuffer.h"
#include "fsfw/container/DynamicFIFO.h"
#include <unistd.h>
#include <cstring>
CommandExecutor::CommandExecutor(const size_t maxSize):
readVec(maxSize) {
waiter.events = POLLIN;
}
ReturnValue_t CommandExecutor::load(std::string command, bool blocking, bool printOutput) {
if(state == States::PENDING) {
return COMMAND_PENDING;
}
currentCmd = command;
this->blocking = blocking;
this->printOutput = printOutput;
if(state == States::IDLE) {
state = States::COMMAND_LOADED;
}
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t CommandExecutor::execute() {
if(state == States::IDLE) {
return NO_COMMAND_LOADED_OR_PENDING;
}
else if(state == States::PENDING) {
return COMMAND_PENDING;
}
currentCmdFile = popen(currentCmd.c_str(), "r");
if(currentCmdFile == nullptr) {
lastError = errno;
return HasReturnvaluesIF::RETURN_FAILED;
}
if(blocking) {
return executeBlocking();
}
else {
currentFd = fileno(currentCmdFile);
waiter.fd = currentFd;
}
state = States::PENDING;
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t CommandExecutor::close() {
if(state == States::PENDING) {
// Attempt to close process, irrespective of if it is running or not
if(currentCmdFile != nullptr) {
pclose(currentCmdFile);
}
}
return HasReturnvaluesIF::RETURN_OK;
}
void CommandExecutor::printLastError(std::string funcName) const {
if(lastError != 0) {
sif::error << funcName << " pclose failed with code " <<
lastError << ": " << strerror(lastError) << std::endl;
}
}
void CommandExecutor::setRingBuffer(SimpleRingBuffer *ringBuffer,
DynamicFIFO<uint16_t>* sizesFifo) {
this->ringBuffer = ringBuffer;
this->sizesFifo = sizesFifo;
}
ReturnValue_t CommandExecutor::check(bool& bytesRead) {
if(blocking) {
return HasReturnvaluesIF::RETURN_OK;
}
switch(state) {
case(States::IDLE):
case(States::COMMAND_LOADED): {
return NO_COMMAND_LOADED_OR_PENDING;
}
case(States::PENDING): {
break;
}
}
int result = poll(&waiter, 1, 0);
switch(result) {
case(0): {
return HasReturnvaluesIF::RETURN_OK;
break;
}
case(1): {
if (waiter.revents & POLLIN) {
ssize_t readBytes = read(currentFd, readVec.data(), readVec.size());
if(readBytes == 0) {
// Should not happen
sif::warning << "CommandExecutor::check: "
"No bytes read after poll event.." << std::endl;
break;
}
else if(readBytes > 0) {
bytesRead = true;
if(printOutput) {
// It is assumed the command output is line terminated
sif::info << currentCmd << " | " << readVec.data();
}
if(ringBuffer != nullptr) {
ringBuffer->writeData(reinterpret_cast<const uint8_t*>(
readVec.data()), readBytes);
}
if(sizesFifo != nullptr) {
if(not sizesFifo->full()) {
sizesFifo->insert(readBytes);
}
}
return BYTES_READ;
}
else {
// Should also not happen
sif::warning << "CommandExecutor::check: Error " << errno << ": " <<
strerror(errno) << std::endl;
}
}
else if(waiter.revents & POLLERR) {
sif::warning << "CommandExecuter::check: Poll error" << std::endl;
return COMMAND_ERROR;
}
else if(waiter.revents & POLLHUP) {
int result = pclose(currentCmdFile);
if(result != 0) {
lastError = result;
return HasReturnvaluesIF::RETURN_FAILED;
}
state = States::IDLE;
currentCmdFile = nullptr;
currentFd = 0;
return EXECUTION_FINISHED;
}
break;
}
}
return HasReturnvaluesIF::RETURN_OK;
}
void CommandExecutor::reset() {
CommandExecutor::close();
currentCmdFile = nullptr;
currentFd = 0;
state = States::IDLE;
}
int CommandExecutor::getLastError() const {
return this->lastError;
}
CommandExecutor::States CommandExecutor::getCurrentState() const {
return state;
}
ReturnValue_t CommandExecutor::executeBlocking() {
while(fgets(readVec.data(), readVec.size(), currentCmdFile) != nullptr) {
std::string output(readVec.data());
if(printOutput) {
sif::info << currentCmd << " | " << output;
}
if(ringBuffer != nullptr) {
ringBuffer->writeData(reinterpret_cast<const uint8_t*>(output.data()), output.size());
}
if(sizesFifo != nullptr) {
if(not sizesFifo->full()) {
sizesFifo->insert(output.size());
}
}
}
int result = pclose(currentCmdFile);
if(result != 0) {
lastError = result;
return HasReturnvaluesIF::RETURN_FAILED;
}
return HasReturnvaluesIF::RETURN_OK;
}

View File

@@ -0,0 +1,134 @@
#ifndef FSFW_SRC_FSFW_OSAL_LINUX_COMMANDEXECUTOR_H_
#define FSFW_SRC_FSFW_OSAL_LINUX_COMMANDEXECUTOR_H_
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
#include "fsfw/returnvalues/FwClassIds.h"
#include <poll.h>
#include <string>
#include <vector>
class SimpleRingBuffer;
template <typename T> class DynamicFIFO;
/**
* @brief Helper class to execute shell commands in blocking and non-blocking mode
* @details
* This class is able to execute processes by using the Linux popen call. It also has the
* capability of writing the read output of a process into a provided ring buffer.
*
* The executor works by first loading the command which should be executed and specifying
* whether it should be executed blocking or non-blocking. After that, execution can be started
* with the execute command. In blocking mode, the execute command will block until the command
* has finished
*/
class CommandExecutor {
public:
enum class States {
IDLE,
COMMAND_LOADED,
PENDING
};
static constexpr uint8_t CLASS_ID = CLASS_ID::LINUX_OSAL;
//! [EXPORT] : [COMMENT] Execution of the current command has finished
static constexpr ReturnValue_t EXECUTION_FINISHED =
HasReturnvaluesIF::makeReturnCode(CLASS_ID, 0);
//! [EXPORT] : [COMMENT] Command is pending. This will also be returned if the user tries
//! to load another command but a command is still pending
static constexpr ReturnValue_t COMMAND_PENDING =
HasReturnvaluesIF::makeReturnCode(CLASS_ID, 1);
//! [EXPORT] : [COMMENT] Some bytes have been read from the executing process
static constexpr ReturnValue_t BYTES_READ =
HasReturnvaluesIF::makeReturnCode(CLASS_ID, 2);
//! [EXPORT] : [COMMENT] Command execution failed
static constexpr ReturnValue_t COMMAND_ERROR =
HasReturnvaluesIF::makeReturnCode(CLASS_ID, 3);
//! [EXPORT] : [COMMENT]
static constexpr ReturnValue_t NO_COMMAND_LOADED_OR_PENDING =
HasReturnvaluesIF::makeReturnCode(CLASS_ID, 4);
static constexpr ReturnValue_t PCLOSE_CALL_ERROR =
HasReturnvaluesIF::makeReturnCode(CLASS_ID, 6);
/**
* Constructor. Is initialized with maximum size of internal buffer to read data from the
* executed process.
* @param maxSize
*/
CommandExecutor(const size_t maxSize);
/**
* Load a new command which should be executed
* @param command
* @param blocking
* @param printOutput
* @return
*/
ReturnValue_t load(std::string command, bool blocking, bool printOutput = true);
/**
* Execute the loaded command.
* @return
* - In blocking mode, it will return RETURN_FAILED if
* the result of the system call was not 0. The error value can be accessed using
* getLastError
* - In non-blocking mode, this call will start
* the execution and then return RETURN_OK
*/
ReturnValue_t execute();
/**
* Only used in non-blocking mode. Checks the currently running command.
* @param bytesRead Will be set to the number of bytes read, if bytes have been read
* @return
* - BYTES_READ if bytes have been read from the executing process. It is recommended to call
* check again after this
* - RETURN_OK execution is pending, but no bytes have been read from the executing process
* - RETURN_FAILED if execution has failed, error value can be accessed using getLastError
* - EXECUTION_FINISHED if the process was executed successfully
* - COMMAND_ERROR internal poll error
*/
ReturnValue_t check(bool& bytesRead);
/**
* Abort the current command. Should normally not be necessary, check can be used to find
* out whether command execution was successful
* @return RETURN_OK
*/
ReturnValue_t close();
States getCurrentState() const;
int getLastError() const;
void printLastError(std::string funcName) const;
/**
* Assign a ring buffer and a FIFO which will be filled by the executor with the output
* read from the started process
* @param ringBuffer
* @param sizesFifo
*/
void setRingBuffer(SimpleRingBuffer* ringBuffer, DynamicFIFO<uint16_t>* sizesFifo);
/**
* Reset the executor. This calls close internally and then reset the state machine so new
* commands can be loaded and executed
*/
void reset();
private:
std::string currentCmd;
bool blocking = true;
FILE* currentCmdFile = nullptr;
int currentFd = 0;
bool printOutput = true;
std::vector<char> readVec;
struct pollfd waiter {};
SimpleRingBuffer* ringBuffer = nullptr;
DynamicFIFO<uint16_t>* sizesFifo = nullptr;
States state = States::IDLE;
int lastError = 0;
ReturnValue_t executeBlocking();
};
#endif /* FSFW_SRC_FSFW_OSAL_LINUX_COMMANDEXECUTOR_H_ */

View File

@@ -285,10 +285,10 @@ ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo,
utility::printUnixErrorGeneric(CLASS_NAME, "sendMessageFromMessageQueue", "EBADF");
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "mq_send to: " << sendTo << " sent from "
<< sentFrom << "failed" << std::endl;
sif::warning << "mq_send to " << sendTo << " sent from "
<< sentFrom << " failed" << std::endl;
#else
sif::printWarning("mq_send to: %d sent from %d failed\n", sendTo, sentFrom);
sif::printWarning("mq_send to %d sent from %d failed\n", sendTo, sentFrom);
#endif
return DESTINATION_INVALID;
}

View File

@@ -0,0 +1,57 @@
#include "fsfw/osal/linux/Timer.h"
#include "fsfw/serviceinterface/ServiceInterfaceStream.h"
#include <errno.h>
Timer::Timer() {
sigevent sigEvent;
sigEvent.sigev_notify = SIGEV_NONE;
sigEvent.sigev_signo = 0;
sigEvent.sigev_value.sival_ptr = &timerId;
int status = timer_create(CLOCK_MONOTONIC, &sigEvent, &timerId);
if(status!=0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "Timer creation failed with: " << status <<
" errno: " << errno << std::endl;
#endif
}
}
Timer::~Timer() {
timer_delete(timerId);
}
int Timer::setTimer(uint32_t intervalMs) {
itimerspec timer;
timer.it_value.tv_sec = intervalMs / 1000;
timer.it_value.tv_nsec = (intervalMs * 1000000) % (1000000000);
timer.it_interval.tv_sec = 0;
timer.it_interval.tv_nsec = 0;
set = true;
return timer_settime(timerId, 0, &timer, NULL);
}
int Timer::getTimer(uint32_t* remainingTimeMs){
itimerspec timer;
timer.it_value.tv_sec = 0;
timer.it_value.tv_nsec = 0;
timer.it_interval.tv_sec = 0;
timer.it_interval.tv_nsec = 0;
int status = timer_gettime(timerId, &timer);
*remainingTimeMs = timer.it_value.tv_sec * 1000 + timer.it_value.tv_nsec / 1000000;
return status;
}
bool Timer::isSet() const {
return this->set;
}
void Timer::resetTimer() {
if(not this->set) {
set = false;
}
setTimer(0);
}

View File

@@ -0,0 +1,49 @@
#ifndef FRAMEWORK_OSAL_LINUX_TIMER_H_
#define FRAMEWORK_OSAL_LINUX_TIMER_H_
#include <signal.h>
#include <time.h>
#include <stdint.h>
/**
* This class is a helper for the creation of a Clock Monotonic timer which does not trigger a signal
*/
class Timer {
public:
/**
* Creates the Timer sets the timerId Member
*/
Timer();
/**
* Deletes the timer
*
* Careful! According to POSIX documentation:
* The treatment of any pending signal generated by the deleted timer is unspecified.
*/
virtual ~Timer();
/**
* Set the timer given in timerId to the given interval
*
* @param intervalMs Interval in ms to be set
* @return 0 on Success 1 else
*/
int setTimer(uint32_t intervalMs);
/**
* Get the remaining time of the timer
*
* @param remainingTimeMs Pointer to integer value which is used to return the remaining time
* @return 0 on Success 1 else (see timer_getime documentation of posix function)
*/
int getTimer(uint32_t* remainingTimeMs);
bool isSet() const;
void resetTimer();
private:
bool set = true;
timer_t timerId;
};
#endif /* FRAMEWORK_OSAL_LINUX_TIMER_H_ */

View File

@@ -77,10 +77,6 @@ enum: uint8_t {
HAL_UART, //HURT
HAL_I2C, //HI2C
HAL_GPIO, //HGIO
FIXED_SLOT_TASK_IF, //FTIF
MGM_LIS3MDL, //MGMLIS3
MGM_RM3100, //MGMRM3100
SPACE_PACKET_PARSER, //SPPA
FW_CLASS_ID_COUNT // [EXPORT] : [END]
};

Some files were not shown because too many files have changed in this diff Show More