diff --git a/.clang-format b/.clang-format
new file mode 100644
index 00000000..ec53b72b
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,7 @@
+---
+BasedOnStyle: Google
+IndentWidth: 2
+---
+Language: Cpp
+ColumnLimit: 100
+---
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e78e8929..bb3d48b8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,6 +4,9 @@ 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
)
@@ -12,6 +15,7 @@ if(FSFW_GENERATE_SECTIONS)
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()
@@ -36,7 +40,9 @@ 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)
@@ -59,7 +65,6 @@ if(FSFW_BUILD_UNITTESTS)
set(FSFW_CONFIG_PATH tests/src/fsfw_tests/unit/testcfg)
configure_file(tests/src/fsfw_tests/unit/testcfg/FSFWConfig.h.in FSFWConfig.h)
configure_file(tests/src/fsfw_tests/unit/testcfg/TestsConfig.h.in tests/TestsConfig.h)
- configure_file(tests/src/fsfw_tests/unit/testcfg/OBSWConfig.h.in OBSWConfig.h)
project(${FSFW_TEST_TGT} CXX C)
add_executable(${FSFW_TEST_TGT})
@@ -147,7 +152,7 @@ else()
set(OS_FSFW "host")
endif()
-if(FSFW_BUILD_UNITTESTS)
+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()
@@ -163,6 +168,9 @@ 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)
@@ -234,9 +242,11 @@ endif()
# The project CMakeLists file has to set the FSFW_CONFIG_PATH and add it.
# If this is not given, we include the default configuration and emit a warning.
if(NOT FSFW_CONFIG_PATH)
- message(WARNING "Flight Software Framework configuration path not set!")
set(DEF_CONF_PATH misc/defaultcfg/fsfwconfig)
- message(WARNING "Setting default configuration from ${DEF_CONF_PATH} ..")
+ 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})
endif()
diff --git a/README.md b/README.md
index 312bc077..73339ff6 100644
--- a/README.md
+++ b/README.md
@@ -42,7 +42,7 @@ There are some functions like `printChar` which are different depending on the t
and need to be implemented by the mission developer.
A template configuration folder was provided and can be copied into the project root to have
-a starting point. The [configuration section](doc/README-config.md#top) provides more specific
+a starting point. The [configuration section](docs/README-config.md#top) provides more specific
information about the possible options.
## Adding the library
@@ -107,16 +107,22 @@ 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.
+
## Index
-[1. High-level overview](doc/README-highlevel.md#top)
-[2. Core components](doc/README-core.md#top)
-[3. Configuration](doc/README-config.md#top)
-[4. OSAL overview](doc/README-osal.md#top)
-[5. PUS services](doc/README-pus.md#top)
-[6. Device Handler overview](doc/README-devicehandlers.md#top)
-[7. Controller overview](doc/README-controllers.md#top)
-[8. Local Data Pools](doc/README-localpools.md#top)
+[1. High-level overview](docs/README-highlevel.md#top)
+[2. Core components](docs/README-core.md#top)
+[3. Configuration](docs/README-config.md#top)
+[4. OSAL overview](docs/README-osal.md#top)
+[5. PUS services](docs/README-pus.md#top)
+[6. Device Handler overview](docs/README-devicehandlers.md#top)
+[7. Controller overview](docs/README-controllers.md#top)
+[8. Local Data Pools](docs/README-localpools.md#top)
diff --git a/cmake/FindSphinx.cmake b/cmake/FindSphinx.cmake
new file mode 100644
index 00000000..4a5b0700
--- /dev/null
+++ b/cmake/FindSphinx.cmake
@@ -0,0 +1,13 @@
+# 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
+)
\ No newline at end of file
diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt
new file mode 100644
index 00000000..fa5790db
--- /dev/null
+++ b/docs/CMakeLists.txt
@@ -0,0 +1,66 @@
+# 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})
diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in
new file mode 100644
index 00000000..3d01d126
--- /dev/null
+++ b/docs/Doxyfile.in
@@ -0,0 +1,7 @@
+INPUT = "@DOXYGEN_INPUT_DIR@"
+
+RECURSIVE = YES
+
+OUTPUT_DIRECTORY = "@DOXYGEN_OUTPUT_DIR@"
+
+GENERATE_XML = YES
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 00000000..d4bb2cbb
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# 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)
diff --git a/doc/README-config.md b/docs/README-config.md
similarity index 100%
rename from doc/README-config.md
rename to docs/README-config.md
diff --git a/doc/README-controllers.md b/docs/README-controllers.md
similarity index 100%
rename from doc/README-controllers.md
rename to docs/README-controllers.md
diff --git a/doc/README-core.md b/docs/README-core.md
similarity index 100%
rename from doc/README-core.md
rename to docs/README-core.md
diff --git a/doc/README-devicehandlers.md b/docs/README-devicehandlers.md
similarity index 100%
rename from doc/README-devicehandlers.md
rename to docs/README-devicehandlers.md
diff --git a/doc/README-highlevel.md b/docs/README-highlevel.md
similarity index 100%
rename from doc/README-highlevel.md
rename to docs/README-highlevel.md
diff --git a/doc/README-localpools.md b/docs/README-localpools.md
similarity index 98%
rename from doc/README-localpools.md
rename to docs/README-localpools.md
index 96ae2d0a..2ee75189 100644
--- a/doc/README-localpools.md
+++ b/docs/README-localpools.md
@@ -31,7 +31,9 @@ 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
+
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
diff --git a/doc/README-osal.md b/docs/README-osal.md
similarity index 100%
rename from doc/README-osal.md
rename to docs/README-osal.md
diff --git a/doc/README-pus.md b/docs/README-pus.md
similarity index 100%
rename from doc/README-pus.md
rename to docs/README-pus.md
diff --git a/docs/api.rst b/docs/api.rst
new file mode 100644
index 00000000..d2ee6c69
--- /dev/null
+++ b/docs/api.rst
@@ -0,0 +1,16 @@
+API
+====
+
+.. toctree::
+ :maxdepth: 4
+
+ api/objectmanager
+ api/task
+ api/ipc
+ api/returnvalue
+ api/event
+ api/modes
+ api/health
+ api/action
+ api/devicehandler
+ api/controller
diff --git a/docs/api/action.rst b/docs/api/action.rst
new file mode 100644
index 00000000..31825b89
--- /dev/null
+++ b/docs/api/action.rst
@@ -0,0 +1,15 @@
+Action Module API
+=================
+
+``ActionHelper``
+-----------------
+
+.. doxygenclass:: ActionHelper
+ :members:
+
+``HasActionsIF``
+-----------------
+
+.. doxygenclass:: HasActionsIF
+ :members:
+ :protected-members:
diff --git a/docs/api/controller.rst b/docs/api/controller.rst
new file mode 100644
index 00000000..27136be6
--- /dev/null
+++ b/docs/api/controller.rst
@@ -0,0 +1,16 @@
+Controller API
+=================
+
+``ControllerBase``
+-------------------------
+
+.. doxygenclass:: ControllerBase
+ :members:
+ :protected-members:
+
+``ExtendedControllerBase``
+-----------------------------
+
+.. doxygenclass:: ExtendedControllerBase
+ :members:
+ :protected-members:
diff --git a/docs/api/devicehandler.rst b/docs/api/devicehandler.rst
new file mode 100644
index 00000000..f709b640
--- /dev/null
+++ b/docs/api/devicehandler.rst
@@ -0,0 +1,16 @@
+Device Handler Base API
+=========================
+
+``DeviceHandlerBase``
+-----------------------
+
+.. doxygenclass:: DeviceHandlerBase
+ :members:
+ :protected-members:
+
+``DeviceHandlerIF``
+-----------------------
+
+.. doxygenclass:: DeviceHandlerIF
+ :members:
+ :protected-members:
diff --git a/docs/api/event.rst b/docs/api/event.rst
new file mode 100644
index 00000000..7553c963
--- /dev/null
+++ b/docs/api/event.rst
@@ -0,0 +1,6 @@
+.. _eventapi:
+
+Event API
+============
+
+.. doxygenfile:: Event.h
diff --git a/docs/api/health.rst b/docs/api/health.rst
new file mode 100644
index 00000000..b1d4c1b2
--- /dev/null
+++ b/docs/api/health.rst
@@ -0,0 +1,9 @@
+Health API
+===========
+
+``HasHealthIF``
+------------------
+
+.. doxygenclass:: HasHealthIF
+ :members:
+ :protected-members:
diff --git a/docs/api/ipc.rst b/docs/api/ipc.rst
new file mode 100644
index 00000000..17a91f00
--- /dev/null
+++ b/docs/api/ipc.rst
@@ -0,0 +1,9 @@
+IPC Module API
+=================
+
+``MessageQueueIF``
+-------------------
+
+.. doxygenclass:: MessageQueueIF
+ :members:
+ :protected-members:
diff --git a/docs/api/modes.rst b/docs/api/modes.rst
new file mode 100644
index 00000000..7b6b0dca
--- /dev/null
+++ b/docs/api/modes.rst
@@ -0,0 +1,10 @@
+Modes API
+=========
+
+
+``HasModesIF``
+---------------
+
+.. doxygenclass:: HasModesIF
+ :members:
+ :protected-members:
diff --git a/docs/api/objectmanager.rst b/docs/api/objectmanager.rst
new file mode 100644
index 00000000..e90deb57
--- /dev/null
+++ b/docs/api/objectmanager.rst
@@ -0,0 +1,30 @@
+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:
diff --git a/docs/api/returnvalue.rst b/docs/api/returnvalue.rst
new file mode 100644
index 00000000..b0d43916
--- /dev/null
+++ b/docs/api/returnvalue.rst
@@ -0,0 +1,10 @@
+.. _retvalapi:
+
+Returnvalue API
+==================
+
+.. doxygenfile:: HasReturnvaluesIF.h
+
+.. _fwclassids:
+
+.. doxygenfile:: FwClassIds.h
diff --git a/docs/api/task.rst b/docs/api/task.rst
new file mode 100644
index 00000000..b218dac1
--- /dev/null
+++ b/docs/api/task.rst
@@ -0,0 +1,8 @@
+Task API
+=========
+
+``ExecutableObjectIF``
+-----------------------
+
+.. doxygenclass:: ExecutableObjectIF
+ :members:
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 00000000..62b17192
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,56 @@
+# 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 = []
\ No newline at end of file
diff --git a/docs/config.rst b/docs/config.rst
new file mode 100644
index 00000000..ed317aed
--- /dev/null
+++ b/docs/config.rst
@@ -0,0 +1,41 @@
+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 `_.
+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 `_
+or the `FSFW example `_
+
+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;
+ }
diff --git a/docs/controllers.rst b/docs/controllers.rst
new file mode 100644
index 00000000..28f57393
--- /dev/null
+++ b/docs/controllers.rst
@@ -0,0 +1,2 @@
+Controllers
+=============
diff --git a/docs/core.rst b/docs/core.rst
new file mode 100644
index 00000000..ef6f6165
--- /dev/null
+++ b/docs/core.rst
@@ -0,0 +1,70 @@
+.. _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 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
+
diff --git a/docs/devicehandlers.rst b/docs/devicehandlers.rst
new file mode 100644
index 00000000..58c2df78
--- /dev/null
+++ b/docs/devicehandlers.rst
@@ -0,0 +1,3 @@
+Device Handlers
+==================
+
diff --git a/doc/doxy/.gitignore b/docs/doxy/.gitignore
similarity index 100%
rename from doc/doxy/.gitignore
rename to docs/doxy/.gitignore
diff --git a/doc/doxy/OPUS.doxyfile b/docs/doxy/OPUS.doxyfile
similarity index 100%
rename from doc/doxy/OPUS.doxyfile
rename to docs/doxy/OPUS.doxyfile
diff --git a/docs/getting_started.rst b/docs/getting_started.rst
new file mode 100644
index 00000000..069e98cd
--- /dev/null
+++ b/docs/getting_started.rst
@@ -0,0 +1,115 @@
+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 `_.
+
+4. Link against the FSFW library
+
+ .. code-block:: cmake
+
+ target_link_libraries( PRIVATE fsfw)
+
+
+5. It should now be possible use the FSFW as a static library from the user code.
+
+Building the unittests
+-------------------------
+
+The FSFW also has unittests which use the `Catch2 library`_.
+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 `_. 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
diff --git a/docs/highlevel.rst b/docs/highlevel.rst
new file mode 100644
index 00000000..08f44777
--- /dev/null
+++ b/docs/highlevel.rst
@@ -0,0 +1,149 @@
+.. _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 ` can be used. The ``CLASS_ID`` is a unique ID for that type of object.
+See the :ref:`FSFW Class IDs file `. 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 ` 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 `:
+
+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 ` 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 `_.
+
+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 `_
+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.
diff --git a/doc/images/PoolArchitecture.png b/docs/images/PoolArchitecture.png
similarity index 100%
rename from doc/images/PoolArchitecture.png
rename to docs/images/PoolArchitecture.png
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 00000000..b37c0904
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,69 @@
+.. 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`
diff --git a/docs/localpools.rst b/docs/localpools.rst
new file mode 100644
index 00000000..d2afd0a0
--- /dev/null
+++ b/docs/localpools.rst
@@ -0,0 +1,181 @@
+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 angVelocityX = lp_var_t(sid.objectId,
+ gyrodefs::ANGULAR_VELOCITY_X, this);
+ lp_var_t angVelocityY = lp_var_t(sid.objectId,
+ gyrodefs::ANGULAR_VELOCITY_Y, this);
+ lp_var_t angVelocityZ = lp_var_t(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({0.0}));
+ localDataPoolMap.emplace(gyrodefs::ANGULAR_VELOCITY_Y,
+ new PoolEntry({0.0}));
+ localDataPoolMap.emplace(gyrodefs::ANGULAR_VELOCITY_Z,
+ new PoolEntry({0.0}));
+ localDataPoolMap.emplace(gyrodefs::GENERAL_CONFIG_REG42,
+ new PoolEntry({0}));
+ localDataPoolMap.emplace(gyrodefs::RANGE_CONFIG_REG43,
+ new PoolEntry({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
+
+
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 00000000..922152e9
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,35 @@
+@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
diff --git a/docs/osal.rst b/docs/osal.rst
new file mode 100644
index 00000000..7ac66e47
--- /dev/null
+++ b/docs/osal.rst
@@ -0,0 +1,63 @@
+.. _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 `_
+- `Linux OSAL for MCUs `_
+- `FreeRTOS OSAL on the STM32H743ZIT `_
+- `RTEMS OSAL on the STM32H743ZIT `_
diff --git a/docs/pus.rst b/docs/pus.rst
new file mode 100644
index 00000000..8dbb2104
--- /dev/null
+++ b/docs/pus.rst
@@ -0,0 +1,2 @@
+PUS Services
+==============
diff --git a/hal/src/fsfw_hal/common/gpio/gpioDefinitions.h b/hal/src/fsfw_hal/common/gpio/gpioDefinitions.h
index c6f21195..e5446c82 100644
--- a/hal/src/fsfw_hal/common/gpio/gpioDefinitions.h
+++ b/hal/src/fsfw_hal/common/gpio/gpioDefinitions.h
@@ -16,8 +16,8 @@ enum Levels: uint8_t {
};
enum Direction: uint8_t {
- IN = 0,
- OUT = 1
+ DIR_IN = 0,
+ DIR_OUT = 1
};
enum GpioOperation {
@@ -30,7 +30,7 @@ enum class GpioTypes {
GPIO_REGULAR_BY_CHIP,
GPIO_REGULAR_BY_LABEL,
GPIO_REGULAR_BY_LINE_NAME,
- CALLBACK
+ TYPE_CALLBACK
};
static constexpr gpioId_t NO_GPIO = -1;
@@ -68,7 +68,7 @@ public:
// Can be used to cast GpioBase to a concrete child implementation
gpio::GpioTypes gpioType = gpio::GpioTypes::NONE;
std::string consumer;
- gpio::Direction direction = gpio::Direction::IN;
+ gpio::Direction direction = gpio::Direction::DIR_IN;
gpio::Levels initValue = gpio::Levels::NONE;
};
@@ -92,7 +92,7 @@ class GpiodRegularByChip: public GpiodRegularBase {
public:
GpiodRegularByChip() :
GpiodRegularBase(gpio::GpioTypes::GPIO_REGULAR_BY_CHIP,
- std::string(), gpio::Direction::IN, gpio::LOW, 0) {
+ std::string(), gpio::Direction::DIR_IN, gpio::LOW, 0) {
}
GpiodRegularByChip(std::string chipname_, int lineNum_, std::string consumer_,
@@ -104,7 +104,7 @@ public:
GpiodRegularByChip(std::string chipname_, int lineNum_, std::string consumer_) :
GpiodRegularBase(gpio::GpioTypes::GPIO_REGULAR_BY_CHIP, consumer_,
- gpio::Direction::IN, gpio::LOW, lineNum_),
+ gpio::Direction::DIR_IN, gpio::LOW, lineNum_),
chipname(chipname_) {
}
@@ -122,7 +122,7 @@ public:
GpiodRegularByLabel(std::string label_, int lineNum_, std::string consumer_) :
GpiodRegularBase(gpio::GpioTypes::GPIO_REGULAR_BY_LABEL, consumer_,
- gpio::Direction::IN, gpio::LOW, lineNum_),
+ gpio::Direction::DIR_IN, gpio::LOW, lineNum_),
label(label_) {
}
@@ -144,7 +144,7 @@ public:
GpiodRegularByLineName(std::string lineName_, std::string consumer_) :
GpiodRegularBase(gpio::GpioTypes::GPIO_REGULAR_BY_LINE_NAME, consumer_,
- gpio::Direction::IN, gpio::LOW), lineName(lineName_) {
+ gpio::Direction::DIR_IN, gpio::LOW), lineName(lineName_) {
}
std::string lineName;
@@ -154,7 +154,7 @@ class GpioCallback: public GpioBase {
public:
GpioCallback(std::string consumer, gpio::Direction direction_, gpio::Levels initValue_,
gpio::gpio_cb_t callback, void* callbackArgs):
- GpioBase(gpio::GpioTypes::CALLBACK, consumer, direction_, initValue_),
+ GpioBase(gpio::GpioTypes::TYPE_CALLBACK, consumer, direction_, initValue_),
callback(callback), callbackArgs(callbackArgs) {}
gpio::gpio_cb_t callback = nullptr;
diff --git a/hal/src/fsfw_hal/linux/gpio/LinuxLibgpioIF.cpp b/hal/src/fsfw_hal/linux/gpio/LinuxLibgpioIF.cpp
index 020ba964..e2e4e46d 100644
--- a/hal/src/fsfw_hal/linux/gpio/LinuxLibgpioIF.cpp
+++ b/hal/src/fsfw_hal/linux/gpio/LinuxLibgpioIF.cpp
@@ -75,7 +75,7 @@ ReturnValue_t LinuxLibgpioIF::configureGpios(GpioMap& mapToAdd) {
configureGpioByLineName(gpioConfig.first, *regularGpio);
break;
}
- case(gpio::GpioTypes::CALLBACK): {
+ case(gpio::GpioTypes::TYPE_CALLBACK): {
auto gpioCallback = dynamic_cast(gpioConfig.second);
if(gpioCallback->callback == nullptr) {
return GPIO_INVALID_INSTANCE;
@@ -163,12 +163,12 @@ ReturnValue_t LinuxLibgpioIF::configureRegularGpio(gpioId_t gpioId, struct gpiod
consumer = regularGpio.consumer;
/* Configure direction and add a description to the GPIO */
switch (direction) {
- case(gpio::OUT): {
+ case(gpio::DIR_OUT): {
result = gpiod_line_request_output(lineHandle, consumer.c_str(),
regularGpio.initValue);
break;
}
- case(gpio::IN): {
+ case(gpio::DIR_IN): {
result = gpiod_line_request_input(lineHandle, consumer.c_str());
break;
}
@@ -330,7 +330,7 @@ ReturnValue_t LinuxLibgpioIF::checkForConflicts(GpioMap& mapToAdd){
}
break;
}
- case(gpio::GpioTypes::CALLBACK): {
+ case(gpio::GpioTypes::TYPE_CALLBACK): {
auto callbackGpio = dynamic_cast(gpioConfig.second);
if(callbackGpio == nullptr) {
return GPIO_TYPE_FAILURE;
@@ -371,13 +371,13 @@ ReturnValue_t LinuxLibgpioIF::checkForConflictsById(gpioId_t gpioIdToCheck,
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) {
+ if(gpioType == gpio::GpioTypes::NONE or gpioType == gpio::GpioTypes::TYPE_CALLBACK) {
eraseDuplicateDifferentType = true;
}
break;
}
- case(gpio::GpioTypes::CALLBACK): {
- if(gpioType != gpio::GpioTypes::CALLBACK) {
+ case(gpio::GpioTypes::TYPE_CALLBACK): {
+ if(gpioType != gpio::GpioTypes::TYPE_CALLBACK) {
eraseDuplicateDifferentType = true;
}
}
diff --git a/misc/defaultcfg/fsfwconfig/CMakeLists.txt b/misc/defaultcfg/fsfwconfig/CMakeLists.txt
index 178fc273..3b2064ba 100644
--- a/misc/defaultcfg/fsfwconfig/CMakeLists.txt
+++ b/misc/defaultcfg/fsfwconfig/CMakeLists.txt
@@ -1,23 +1,49 @@
-target_include_directories(${TARGET_NAME} PRIVATE
+if(DEFINED TARGET_NAME)
+ 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
- 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
+ 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
+ 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()
diff --git a/misc/defaultcfg/fsfwconfig/objects/FsfwFactory.cpp b/misc/defaultcfg/fsfwconfig/objects/FsfwFactory.cpp
index 08ad41ec..5aef4980 100644
--- a/misc/defaultcfg/fsfwconfig/objects/FsfwFactory.cpp
+++ b/misc/defaultcfg/fsfwconfig/objects/FsfwFactory.cpp
@@ -7,7 +7,7 @@
#include
#include
#include
-#include
+#include
#include
@@ -48,6 +48,6 @@ void Factory::setStaticFrameworkObjectIds() {
DeviceHandlerFailureIsolation::powerConfirmationId = objects::NO_OBJECT;
- TmPacketStored::timeStamperId = objects::NO_OBJECT;
+ TmPacketBase::timeStamperId = objects::NO_OBJECT;
}
diff --git a/scripts/apply-clang-format.sh b/scripts/apply-clang-format.sh
new file mode 100755
index 00000000..27202324
--- /dev/null
+++ b/scripts/apply-clang-format.sh
@@ -0,0 +1,8 @@
+#!/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
diff --git a/scripts/coverage.py b/scripts/coverage.py
deleted file mode 100755
index b5bd7745..00000000
--- a/scripts/coverage.py
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*
-"""Small portable helper script to generate LCOV HTML coverage data"""
-import os
-import platform
-import sys
-import time
-import argparse
-import webbrowser
-from typing import List
-
-
-"""Copy this helper script into your project folder. It will try to determine a CMake build folder
-and then attempt to build your project with coverage information.
-
-See Unittest documentation at https://egit.irs.uni-stuttgart.de/fsfw/fsfw for more
-information how to set up the build folder.
-"""
-def main():
-
- parser = argparse.ArgumentParser(description="Processing arguments for LCOV helper script.")
- parser.add_argument(
- '-o', '--open', action='store_true', help='Open coverage data in webbrowser'
- )
- args = parser.parse_args()
-
- build_dir_list = []
- if not os.path.isfile('README.md'):
- os.chdir('..')
- for directory in os.listdir("."):
- if os.path.isdir(directory):
- os.chdir(directory)
- check_for_cmake_build_dir(build_dir_list)
- os.chdir("..")
-
- if len(build_dir_list) == 0:
- print("No valid CMake build directory found. Trying to set up hosted build")
- build_directory = 'build-Debug-Host'
- os.mkdir(build_directory)
- os.chdir(build_directory)
- os.system('cmake -DFSFW_OSAL=host -DFSFW_BUILD_UNITTESTS=ON ..')
- os.chdir('..')
- elif len(build_dir_list) == 1:
- build_directory = build_dir_list[0]
- else:
- print("Multiple build directories found!")
- build_directory = determine_build_dir(build_dir_list)
- perform_lcov_operation(build_directory)
- if os.path.isdir('fsfw-tests_coverage') and args.open:
- webbrowser.open('fsfw-tests_coverage/index.html')
-
-
-def check_for_cmake_build_dir(build_dir_dict: list):
- if os.path.isfile("CMakeCache.txt"):
- build_dir_dict.append(os.getcwd())
-
-
-def perform_lcov_operation(directory):
- os.chdir(directory)
- os.system("cmake --build . -- fsfw-tests_coverage -j")
-
-
-def determine_build_dir(build_dir_list: List[str]):
- build_directory = ""
- for idx, directory in enumerate(build_dir_list):
- print(f"{idx + 1}: {directory}")
- while True:
- idx = input("Pick the directory to perform LCOV HTML generation by index: ")
- if not idx.isdigit():
- print("Invalid input!")
- continue
-
- idx = int(idx)
- if idx > len(build_dir_list) or idx < 1:
- print("Invalid input!")
- continue
- build_directory = build_dir_list[idx - 1]
- break
- return build_directory
-
-
-if __name__ == "__main__":
- main()
diff --git a/scripts/gen-unittest.sh b/scripts/gen-unittest.sh
deleted file mode 100755
index 9ca8c399..00000000
--- a/scripts/gen-unittest.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-mkdir build-Unittest && cd build-Unittest
-cmake -DFSFW_BUILD_UNITTESTS=ON -DFSFW_OSAL=host -DCMAKE_BUILD_TYPE=Debug ..
diff --git a/scripts/helper.py b/scripts/helper.py
new file mode 100755
index 00000000..0bb430ee
--- /dev/null
+++ b/scripts/helper.py
@@ -0,0 +1,188 @@
+#!/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()
diff --git a/src/fsfw/globalfunctions/timevalOperations.h b/src/fsfw/globalfunctions/timevalOperations.h
index db68f330..41f4e52f 100644
--- a/src/fsfw/globalfunctions/timevalOperations.h
+++ b/src/fsfw/globalfunctions/timevalOperations.h
@@ -2,8 +2,9 @@
#define TIMEVALOPERATIONS_H_
#include
+#include
-#ifdef WIN32
+#ifdef PLATFORM_WIN
#include
#else
#include
diff --git a/src/fsfw/serialize/SerializeAdapter.h b/src/fsfw/serialize/SerializeAdapter.h
index e6cd247e..2831472c 100644
--- a/src/fsfw/serialize/SerializeAdapter.h
+++ b/src/fsfw/serialize/SerializeAdapter.h
@@ -7,7 +7,7 @@
#include
#include
- /**
+/**
* @brief These adapters provides an interface to use the SerializeIF functions
* with arbitrary template objects to facilitate and simplify the
* serialization of classes with different multiple different data types
@@ -20,174 +20,250 @@
*/
class SerializeAdapter {
public:
- /***
- * This function can be used to serialize a trivial copy-able type or a
- * child of SerializeIF.
- * The right template to be called is determined in the function itself.
- * For objects of non trivial copy-able type this function is almost never
- * called by the user directly. Instead helpers for specific types like
- * SerialArrayListAdapter or SerialLinkedListAdapter is the right choice here.
- *
- * @param[in] object Object to serialize, the used type is deduced from this pointer
- * @param[in/out] buffer Buffer to serialize into. Will be moved by the function.
- * @param[in/out] size Size of current written buffer. Will be incremented by the function.
- * @param[in] maxSize Max size of Buffer
- * @param[in] streamEndianness Endianness of serialized element as in according to SerializeIF::Endianness
- * @return
- * - @c BUFFER_TOO_SHORT The given buffer in is too short
- * - @c RETURN_FAILED Generic Error
- * - @c RETURN_OK Successful serialization
- */
- template
- static ReturnValue_t serialize(const T *object, uint8_t **buffer,
- size_t *size, size_t maxSize,
- SerializeIF::Endianness streamEndianness) {
- InternalSerializeAdapter::value> adapter;
- return adapter.serialize(object, buffer, size, maxSize,
- streamEndianness);
- }
- /**
- * Function to return the serialized size of the object in the pointer.
- * May be a trivially copy-able object or a Child of SerializeIF
- *
- * @param object Pointer to Object
- * @return Serialized size of object
- */
- template
- static size_t getSerializedSize(const T *object){
- InternalSerializeAdapter::value> adapter;
- return adapter.getSerializedSize(object);
- }
- /**
- * @brief
- * Deserializes a object from a given buffer of given size.
- * Object Must be trivially copy-able or a child of SerializeIF.
- *
- * @details
- * Buffer will be moved to the current read location. Size will be decreased by the function.
- *
- * @param[in/out] buffer Buffer to deSerialize from. Will be moved by the function.
- * @param[in/out] size Remaining size of the buffer to read from. Will be decreased by function.
- * @param[in] streamEndianness Endianness as in according to SerializeIF::Endianness
- * @return
- * - @c STREAM_TOO_SHORT The input stream is too short to deSerialize the object
- * - @c TOO_MANY_ELEMENTS The buffer has more inputs than expected
- * - @c RETURN_FAILED Generic Error
- * - @c RETURN_OK Successful deserialization
- */
- template
- static ReturnValue_t deSerialize(T *object, const uint8_t **buffer,
- size_t *size, SerializeIF::Endianness streamEndianness) {
- InternalSerializeAdapter::value> adapter;
- return adapter.deSerialize(object, buffer, size, streamEndianness);
- }
+ /***
+ * @brief Serialize a trivial copy-able type or a child of SerializeIF.
+ * @details
+ * The right template to be called is determined in the function itself.
+ * For objects of non trivial copy-able type this function is almost never
+ * called by the user directly. Instead helpers for specific types like
+ * SerialArrayListAdapter or SerialLinkedListAdapter are the right choice here.
+ *
+ * @param[in] object: Object to serialize, the used type is deduced from this pointer
+ * @param[in/out] buffer: Pointer to the buffer to serialize into. Buffer position will be
+ * incremented by the function.
+ * @param[in/out] size: Pointer to size of current written buffer.
+ * SIze will be incremented by the function.
+ * @param[in] maxSize: Max size of Buffer
+ * @param[in] streamEndianness: Endianness of serialized element as in according to
+ * SerializeIF::Endianness
+ * @return
+ * - @c BUFFER_TOO_SHORT The given buffer in is too short
+ * - @c RETURN_FAILED Generic Error
+ * - @c RETURN_OK Successful serialization
+ */
+ template
+ static ReturnValue_t serialize(const T *object, uint8_t **buffer,
+ size_t *size, size_t maxSize,
+ SerializeIF::Endianness streamEndianness) {
+ InternalSerializeAdapter::value> adapter;
+ return adapter.serialize(object, buffer, size, maxSize,
+ streamEndianness);
+ }
+
+ /***
+ * This function can be used to serialize a trivial copy-able type or a child of SerializeIF.
+ * The right template to be called is determined in the function itself.
+ * For objects of non trivial copy-able type this function is almost never
+ * called by the user directly. Instead helpers for specific types like
+ * SerialArrayListAdapter or SerialLinkedListAdapter are the right choice here.
+ *
+ * @param[in] object: Object to serialize, the used type is deduced from this pointer
+ * @param[in/out] buffer: Buffer to serialize into.
+ * @param[out] serSize: Serialized size
+ * @param[in] maxSize: Max size of buffer
+ * @param[in] streamEndianness: Endianness of serialized element as in according to
+ * SerializeIF::Endianness
+ * @return
+ * - @c BUFFER_TOO_SHORT The given buffer in is too short
+ * - @c RETURN_FAILED Generic Error
+ * - @c RETURN_OK Successful serialization
+ */
+ template
+ static ReturnValue_t serialize(const T *object, uint8_t* const buffer, size_t* serSize,
+ size_t maxSize, SerializeIF::Endianness streamEndianness) {
+ if(object == nullptr or buffer == nullptr) {
+ return HasReturnvaluesIF::RETURN_FAILED;
+ }
+ InternalSerializeAdapter::value> adapter;
+ uint8_t** tempPtr = const_cast(&buffer);
+ size_t tmpSize = 0;
+ ReturnValue_t result = adapter.serialize(object, tempPtr, &tmpSize, maxSize,
+ streamEndianness);
+ if(serSize != nullptr) {
+ *serSize = tmpSize;
+ }
+ return result;
+ }
+
+ /**
+ * @brief Function to return the serialized size of the object in the pointer.
+ * @details
+ * May be a trivially copy-able object or a child of SerializeIF.
+ *
+ * @param object Pointer to Object
+ * @return Serialized size of object
+ */
+ template
+ static size_t getSerializedSize(const T *object){
+ InternalSerializeAdapter::value> adapter;
+ return adapter.getSerializedSize(object);
+ }
+
+ /**
+ * @brief Deserializes a object from a given buffer of given size.
+ *
+ * @details
+ * Object Must be trivially copy-able or a child of SerializeIF.
+ * Buffer will be moved to the current read location. Size will be decreased by the function.
+ *
+ * @param[in] object: Pointer to object to deserialize
+ * @param[in/out] buffer: Pointer to the buffer to deSerialize from. Buffer position will be
+ * incremented by the function
+ * @param[in/out] size: Pointer to remaining size of the buffer to read from.
+ * Will be decreased by function.
+ * @param[in] streamEndianness: Endianness as in according to SerializeIF::Endianness
+ * @return
+ * - @c STREAM_TOO_SHORT The input stream is too short to deSerialize the object
+ * - @c TOO_MANY_ELEMENTS The buffer has more inputs than expected
+ * - @c RETURN_FAILED Generic Error
+ * - @c RETURN_OK Successful deserialization
+ */
+ template
+ static ReturnValue_t deSerialize(T *object, const uint8_t **buffer,
+ size_t *size, SerializeIF::Endianness streamEndianness) {
+ InternalSerializeAdapter::value> adapter;
+ return adapter.deSerialize(object, buffer, size, streamEndianness);
+ }
+
+ /**
+ * @brief Deserializes a object from a given buffer of given size.
+ *
+ * @details
+ * Object Must be trivially copy-able or a child of SerializeIF.
+ *
+ * @param[in] object: Pointer to object to deserialize
+ * @param[in] buffer: Buffer to deSerialize from
+ * @param[out] deserSize: Deserialized length
+ * @param[in] streamEndianness: Endianness as in according to SerializeIF::Endianness
+ * @return
+ * - @c STREAM_TOO_SHORT The input stream is too short to deSerialize the object
+ * - @c TOO_MANY_ELEMENTS The buffer has more inputs than expected
+ * - @c RETURN_FAILED Generic Error
+ * - @c RETURN_OK Successful deserialization
+ */
+ template
+ static ReturnValue_t deSerialize(T *object, const uint8_t* buffer,
+ size_t* deserSize, SerializeIF::Endianness streamEndianness) {
+ if(object == nullptr or buffer == nullptr) {
+ return HasReturnvaluesIF::RETURN_FAILED;
+ }
+ InternalSerializeAdapter::value> adapter;
+ const uint8_t** tempPtr = &buffer;
+ size_t maxVal = -1;
+ ReturnValue_t result = adapter.deSerialize(object, tempPtr, &maxVal, streamEndianness);
+ if(deserSize != nullptr) {
+ *deserSize = -1 - maxVal;
+ }
+ return result;
+ }
+
private:
- /**
- * Internal template to deduce the right function calls at compile time
- */
- template class InternalSerializeAdapter;
+ /**
+ * Internal template to deduce the right function calls at compile time
+ */
+ template class InternalSerializeAdapter;
- /**
- * Template to be used if T is not a child of SerializeIF
- *
- * @tparam T T must be trivially_copyable
- */
- template
- class InternalSerializeAdapter {
- static_assert (std::is_trivially_copyable::value,
- "If a type needs to be serialized it must be a child of "
- "SerializeIF or trivially copy-able");
- public:
- static ReturnValue_t serialize(const T *object, uint8_t **buffer,
- size_t *size, size_t max_size,
- SerializeIF::Endianness streamEndianness) {
- size_t ignoredSize = 0;
- if (size == nullptr) {
- size = &ignoredSize;
- }
- // Check remaining size is large enough and check integer
- // overflow of *size
- size_t newSize = sizeof(T) + *size;
- if ((newSize <= max_size) and (newSize > *size)) {
- T tmp;
- switch (streamEndianness) {
- case SerializeIF::Endianness::BIG:
- tmp = EndianConverter::convertBigEndian(*object);
- break;
- case SerializeIF::Endianness::LITTLE:
- tmp = EndianConverter::convertLittleEndian(*object);
- break;
- default:
- case SerializeIF::Endianness::MACHINE:
- tmp = *object;
- break;
- }
- std::memcpy(*buffer, &tmp, sizeof(T));
- *size += sizeof(T);
- (*buffer) += sizeof(T);
- return HasReturnvaluesIF::RETURN_OK;
- } else {
- return SerializeIF::BUFFER_TOO_SHORT;
- }
- }
+ /**
+ * Template to be used if T is not a child of SerializeIF
+ *
+ * @tparam T T must be trivially_copyable
+ */
+ template
+ class InternalSerializeAdapter {
+ static_assert (std::is_trivially_copyable::value,
+ "If a type needs to be serialized it must be a child of "
+ "SerializeIF or trivially copy-able");
+ public:
+ static ReturnValue_t serialize(const T *object, uint8_t **buffer,
+ size_t *size, size_t max_size,
+ SerializeIF::Endianness streamEndianness) {
+ size_t ignoredSize = 0;
+ if (size == nullptr) {
+ size = &ignoredSize;
+ }
+ // Check remaining size is large enough and check integer
+ // overflow of *size
+ size_t newSize = sizeof(T) + *size;
+ if ((newSize <= max_size) and (newSize > *size)) {
+ T tmp;
+ switch (streamEndianness) {
+ case SerializeIF::Endianness::BIG:
+ tmp = EndianConverter::convertBigEndian(*object);
+ break;
+ case SerializeIF::Endianness::LITTLE:
+ tmp = EndianConverter::convertLittleEndian(*object);
+ break;
+ default:
+ case SerializeIF::Endianness::MACHINE:
+ tmp = *object;
+ break;
+ }
+ std::memcpy(*buffer, &tmp, sizeof(T));
+ *size += sizeof(T);
+ (*buffer) += sizeof(T);
+ return HasReturnvaluesIF::RETURN_OK;
+ } else {
+ return SerializeIF::BUFFER_TOO_SHORT;
+ }
+ }
- ReturnValue_t deSerialize(T *object, const uint8_t **buffer,
- size_t *size, SerializeIF::Endianness streamEndianness) {
- T tmp;
- if (*size >= sizeof(T)) {
- *size -= sizeof(T);
- std::memcpy(&tmp, *buffer, sizeof(T));
- switch (streamEndianness) {
- case SerializeIF::Endianness::BIG:
- *object = EndianConverter::convertBigEndian(tmp);
- break;
- case SerializeIF::Endianness::LITTLE:
- *object = EndianConverter::convertLittleEndian(tmp);
- break;
- default:
- case SerializeIF::Endianness::MACHINE:
- *object = tmp;
- break;
- }
+ ReturnValue_t deSerialize(T *object, const uint8_t **buffer,
+ size_t *size, SerializeIF::Endianness streamEndianness) {
+ T tmp;
+ if (*size >= sizeof(T)) {
+ *size -= sizeof(T);
+ std::memcpy(&tmp, *buffer, sizeof(T));
+ switch (streamEndianness) {
+ case SerializeIF::Endianness::BIG:
+ *object = EndianConverter::convertBigEndian(tmp);
+ break;
+ case SerializeIF::Endianness::LITTLE:
+ *object = EndianConverter::convertLittleEndian(tmp);
+ break;
+ default:
+ case SerializeIF::Endianness::MACHINE:
+ *object = tmp;
+ break;
+ }
- *buffer += sizeof(T);
- return HasReturnvaluesIF::RETURN_OK;
- } else {
- return SerializeIF::STREAM_TOO_SHORT;
- }
- }
+ *buffer += sizeof(T);
+ return HasReturnvaluesIF::RETURN_OK;
+ } else {
+ return SerializeIF::STREAM_TOO_SHORT;
+ }
+ }
- uint32_t getSerializedSize(const T *object) {
- return sizeof(T);
- }
- };
+ uint32_t getSerializedSize(const T *object) {
+ return sizeof(T);
+ }
+ };
- /**
- * Template for objects that inherit from SerializeIF
- *
- * @tparam T A child of SerializeIF
- */
- template
- class InternalSerializeAdapter {
- public:
- ReturnValue_t serialize(const T *object, uint8_t **buffer, size_t *size,
- size_t max_size,
- SerializeIF::Endianness streamEndianness) const {
- size_t ignoredSize = 0;
- if (size == nullptr) {
- size = &ignoredSize;
- }
- return object->serialize(buffer, size, max_size, streamEndianness);
- }
- size_t getSerializedSize(const T *object) const {
- return object->getSerializedSize();
- }
+ /**
+ * Template for objects that inherit from SerializeIF
+ *
+ * @tparam T A child of SerializeIF
+ */
+ template
+ class InternalSerializeAdapter {
+ public:
+ ReturnValue_t serialize(const T *object, uint8_t **buffer, size_t *size,
+ size_t max_size,
+ SerializeIF::Endianness streamEndianness) const {
+ size_t ignoredSize = 0;
+ if (size == nullptr) {
+ size = &ignoredSize;
+ }
+ return object->serialize(buffer, size, max_size, streamEndianness);
+ }
+ size_t getSerializedSize(const T *object) const {
+ return object->getSerializedSize();
+ }
- ReturnValue_t deSerialize(T *object, const uint8_t **buffer,
- size_t *size, SerializeIF::Endianness streamEndianness) {
- return object->deSerialize(buffer, size, streamEndianness);
- }
- };
+ ReturnValue_t deSerialize(T *object, const uint8_t **buffer,
+ size_t *size, SerializeIF::Endianness streamEndianness) {
+ return object->deSerialize(buffer, size, streamEndianness);
+ }
+ };
};
#endif /* _FSFW_SERIALIZE_SERIALIZEADAPTER_H_ */
diff --git a/src/fsfw/tmtcservices/TmTcBridge.cpp b/src/fsfw/tmtcservices/TmTcBridge.cpp
index 7346cfe2..be45b698 100644
--- a/src/fsfw/tmtcservices/TmTcBridge.cpp
+++ b/src/fsfw/tmtcservices/TmTcBridge.cpp
@@ -183,13 +183,16 @@ ReturnValue_t TmTcBridge::storeDownlinkData(TmTcMessage *message) {
}
if(tmFifo->full()) {
-#if FSFW_CPP_OSTREAM_ENABLED == 1
- sif::warning << "TmTcBridge::storeDownlinkData: TM downlink max. number "
- "of stored packet IDs reached!" << std::endl;
-#else
- sif::printWarning("TmTcBridge::storeDownlinkData: TM downlink max. number "
- "of stored packet IDs reached!\n");
-#endif
+ if(warningLatch) {
+ #if FSFW_CPP_OSTREAM_ENABLED == 1
+ sif::warning << "TmTcBridge::storeDownlinkData: TM downlink max. number "
+ "of stored packet IDs reached!" << std::endl;
+ #else
+ sif::printWarning("TmTcBridge::storeDownlinkData: TM downlink max. number "
+ "of stored packet IDs reached!\n");
+ #endif
+ warningLatch = true;
+ }
if(overwriteOld) {
tmFifo->retrieve(&storeId);
tmStore->deleteData(storeId);
diff --git a/src/fsfw/tmtcservices/TmTcBridge.h b/src/fsfw/tmtcservices/TmTcBridge.h
index 4980caff..490d57fa 100644
--- a/src/fsfw/tmtcservices/TmTcBridge.h
+++ b/src/fsfw/tmtcservices/TmTcBridge.h
@@ -75,6 +75,7 @@ public:
virtual uint16_t getIdentifier() override;
virtual MessageQueueId_t getRequestQueue() override;
+ bool warningLatch = true;
protected:
//! Cached for initialize function.
object_id_t tmStoreId = objects::NO_OBJECT;
diff --git a/tests/src/fsfw_tests/integration/task/CMakeLists.txt b/tests/src/fsfw_tests/integration/task/CMakeLists.txt
index 0402d093..4cd481bf 100644
--- a/tests/src/fsfw_tests/integration/task/CMakeLists.txt
+++ b/tests/src/fsfw_tests/integration/task/CMakeLists.txt
@@ -1,3 +1,3 @@
-target_sources(${TARGET_NAME} PRIVATE
+target_sources(${LIB_FSFW_NAME} PRIVATE
TestTask.cpp
)
\ No newline at end of file
diff --git a/tests/src/fsfw_tests/unit/serialize/TestSerialization.cpp b/tests/src/fsfw_tests/unit/serialize/TestSerialization.cpp
index 64deae3b..f883fe78 100644
--- a/tests/src/fsfw_tests/unit/serialize/TestSerialization.cpp
+++ b/tests/src/fsfw_tests/unit/serialize/TestSerialization.cpp
@@ -3,128 +3,213 @@
#include
#include
+#include
#include
-static bool test_value_bool = true;
-static uint8_t tv_uint8 {5};
-static uint16_t tv_uint16 {283};
-static uint32_t tv_uint32 {929221};
-static uint64_t tv_uint64 {2929329429};
+static bool testBool = true;
+static uint8_t tvUint8 {5};
+static uint16_t tvUint16 {283};
+static uint32_t tvUint32 {929221};
+static uint64_t tvUint64 {2929329429};
-static int8_t tv_int8 {-16};
-static int16_t tv_int16 {-829};
-static int32_t tv_int32 {-2312};
+static int8_t tvInt8 {-16};
+static int16_t tvInt16 {-829};
+static int32_t tvInt32 {-2312};
-static float tv_float {8.2149214};
-static float tv_sfloat = {-922.2321321};
-static double tv_double {9.2132142141e8};
-static double tv_sdouble {-2.2421e19};
+static float tvFloat {8.2149214};
+static float tvSfloat = {-922.2321321};
+static double tvDouble {9.2132142141e8};
+static double tvSdouble {-2.2421e19};
-static std::array test_array;
+static std::array TEST_ARRAY;
-TEST_CASE( "Serialization size tests", "[TestSerialization]") {
+TEST_CASE( "Serialization size tests", "[SerSizeTest]") {
//REQUIRE(unitTestClass.test_autoserialization() == 0);
- REQUIRE(SerializeAdapter::getSerializedSize(&test_value_bool) ==
- sizeof(test_value_bool));
- REQUIRE(SerializeAdapter::getSerializedSize(&tv_uint8) ==
- sizeof(tv_uint8));
- REQUIRE(SerializeAdapter::getSerializedSize(&tv_uint16) ==
- sizeof(tv_uint16));
- REQUIRE(SerializeAdapter::getSerializedSize(&tv_uint32 ) ==
- sizeof(tv_uint32));
- REQUIRE(SerializeAdapter::getSerializedSize(&tv_uint64) ==
- sizeof(tv_uint64));
- REQUIRE(SerializeAdapter::getSerializedSize(&tv_int8) ==
- sizeof(tv_int8));
- REQUIRE(SerializeAdapter::getSerializedSize(&tv_int16) ==
- sizeof(tv_int16));
- REQUIRE(SerializeAdapter::getSerializedSize(&tv_int32) ==
- sizeof(tv_int32));
- REQUIRE(SerializeAdapter::getSerializedSize(&tv_float) ==
- sizeof(tv_float));
- REQUIRE(SerializeAdapter::getSerializedSize(&tv_sfloat) ==
- sizeof(tv_sfloat ));
- REQUIRE(SerializeAdapter::getSerializedSize(&tv_double) ==
- sizeof(tv_double));
- REQUIRE(SerializeAdapter::getSerializedSize(&tv_sdouble) ==
- sizeof(tv_sdouble));
+ REQUIRE(SerializeAdapter::getSerializedSize(&testBool) ==
+ sizeof(testBool));
+ REQUIRE(SerializeAdapter::getSerializedSize(&tvUint8) ==
+ sizeof(tvUint8));
+ REQUIRE(SerializeAdapter::getSerializedSize(&tvUint16) ==
+ sizeof(tvUint16));
+ REQUIRE(SerializeAdapter::getSerializedSize(&tvUint32 ) ==
+ sizeof(tvUint32));
+ REQUIRE(SerializeAdapter::getSerializedSize(&tvUint64) ==
+ sizeof(tvUint64));
+ REQUIRE(SerializeAdapter::getSerializedSize(&tvInt8) ==
+ sizeof(tvInt8));
+ REQUIRE(SerializeAdapter::getSerializedSize(&tvInt16) ==
+ sizeof(tvInt16));
+ REQUIRE(SerializeAdapter::getSerializedSize(&tvInt32) ==
+ sizeof(tvInt32));
+ REQUIRE(SerializeAdapter::getSerializedSize(&tvFloat) ==
+ sizeof(tvFloat));
+ REQUIRE(SerializeAdapter::getSerializedSize(&tvSfloat) ==
+ sizeof(tvSfloat ));
+ REQUIRE(SerializeAdapter::getSerializedSize(&tvDouble) ==
+ sizeof(tvDouble));
+ REQUIRE(SerializeAdapter::getSerializedSize(&tvSdouble) ==
+ sizeof(tvSdouble));
}
+TEST_CASE("Auto Serialize Adapter", "[SerAdapter]") {
+ size_t serializedSize = 0;
+ uint8_t * pArray = TEST_ARRAY.data();
-TEST_CASE("Auto Serialize Adapter testing", "[single-file]") {
- size_t serialized_size = 0;
- uint8_t * p_array = test_array.data();
+ SECTION("SerDe") {
+ size_t deserSize = 0;
+ SerializeAdapter::serialize(&testBool, TEST_ARRAY.data(), &deserSize, TEST_ARRAY.size(),
+ SerializeIF::Endianness::MACHINE);
+ REQUIRE(deserSize == 1);
+ REQUIRE(TEST_ARRAY[0] == true);
+ bool readBack = false;
+ SerializeAdapter::deSerialize(&readBack, TEST_ARRAY.data(), &deserSize,
+ SerializeIF::Endianness::MACHINE);
+ REQUIRE(deserSize == 1);
+ REQUIRE(readBack == true);
+ SerializeAdapter::serialize(&tvUint8, TEST_ARRAY.data(), &deserSize, TEST_ARRAY.size(),
+ SerializeIF::Endianness::MACHINE);
+ REQUIRE(deserSize == 1);
+ REQUIRE(TEST_ARRAY[0] == 5);
+ uint8_t readBackUint8 = 0;
+ uint8_t* const testPtr = TEST_ARRAY.data();
+ uint8_t* const shouldStayConst = testPtr;
+ SerializeAdapter::deSerialize(&readBackUint8, testPtr, &deserSize,
+ SerializeIF::Endianness::MACHINE);
+ REQUIRE(testPtr == shouldStayConst);
+ REQUIRE(deserSize == 1);
+ REQUIRE(readBackUint8 == 5);
+ SerializeAdapter::serialize(&tvUint16, TEST_ARRAY.data(), &deserSize, TEST_ARRAY.size(),
+ SerializeIF::Endianness::MACHINE);
+ REQUIRE(deserSize == 2);
+ deserSize = 0;
+ uint16_t readBackUint16 = 0;
+ SerializeAdapter::deSerialize(&readBackUint16, TEST_ARRAY.data(), &deserSize,
+ SerializeIF::Endianness::MACHINE);
+ REQUIRE(deserSize == 2);
+ REQUIRE(readBackUint16 == 283);
- SECTION("Serializing...") {
- SerializeAdapter::serialize(&test_value_bool, &p_array,
- &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
- SerializeAdapter::serialize(&tv_uint8, &p_array,
- &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
- SerializeAdapter::serialize(&tv_uint16, &p_array,
- &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
- SerializeAdapter::serialize(&tv_uint32, &p_array,
- &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
- SerializeAdapter::serialize(&tv_int8, &p_array,
- &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
- SerializeAdapter::serialize(&tv_int16, &p_array,
- &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
- SerializeAdapter::serialize(&tv_int32, &p_array,
- &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
- SerializeAdapter::serialize(&tv_uint64, &p_array,
- &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
- SerializeAdapter::serialize(&tv_float, &p_array,
- &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
- SerializeAdapter::serialize(&tv_double, &p_array,
- &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
- SerializeAdapter::serialize(&tv_sfloat, &p_array,
- &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
- SerializeAdapter::serialize(&tv_sdouble, &p_array,
- &serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
- REQUIRE (serialized_size == 47);
+ SerializeAdapter::serialize(&tvUint32, TEST_ARRAY.data(), &deserSize, TEST_ARRAY.size(),
+ SerializeIF::Endianness::MACHINE);
+ REQUIRE(deserSize == 4);
+ uint32_t readBackUint32 = 0;
+ deserSize = 0;
+ SerializeAdapter::deSerialize(&readBackUint32, TEST_ARRAY.data(), &deserSize,
+ SerializeIF::Endianness::MACHINE);
+ REQUIRE(deserSize == 4);
+ REQUIRE(readBackUint32 == 929221);
+
+ SerializeAdapter::serialize(&tvInt16, TEST_ARRAY.data(), &deserSize, TEST_ARRAY.size(),
+ SerializeIF::Endianness::MACHINE);
+ REQUIRE(deserSize == 2);
+ int16_t readBackInt16 = 0;
+ SerializeAdapter::deSerialize(&readBackInt16, TEST_ARRAY.data(), &deserSize,
+ SerializeIF::Endianness::MACHINE);
+ REQUIRE(readBackInt16 == -829);
+ REQUIRE(deserSize == 2);
+
+ SerializeAdapter::serialize(&tvFloat, TEST_ARRAY.data(), &deserSize, TEST_ARRAY.size(),
+ SerializeIF::Endianness::MACHINE);
+ float readBackFloat = 0.0;
+ SerializeAdapter::deSerialize(&readBackFloat, TEST_ARRAY.data(), &deserSize,
+ SerializeIF::Endianness::MACHINE);
+ REQUIRE(readBackFloat == Catch::Approx(8.214921));
+
+ SerializeAdapter::serialize(&tvSdouble, TEST_ARRAY.data(), &deserSize, TEST_ARRAY.size(),
+ SerializeIF::Endianness::MACHINE);
+ double readBackSignedDouble = 0.0;
+ SerializeAdapter::deSerialize(&readBackSignedDouble, TEST_ARRAY.data(), &deserSize,
+ SerializeIF::Endianness::MACHINE);
+ REQUIRE(readBackSignedDouble == Catch::Approx(-2.2421e19));
+
+ uint8_t testBuf[4] = {1, 2, 3, 4};
+ SerialBufferAdapter bufferAdapter(testBuf, sizeof(testBuf));
+ SerializeAdapter::serialize(&bufferAdapter, TEST_ARRAY.data(), &deserSize,
+ TEST_ARRAY.size(), SerializeIF::Endianness::MACHINE);
+ REQUIRE(deserSize == 4);
+ for(uint8_t idx = 0; idx < 4; idx++) {
+ REQUIRE(TEST_ARRAY[idx] == idx + 1);
+ }
+ deserSize = 0;
+ testBuf[0] = 0;
+ testBuf[1] = 12;
+ SerializeAdapter::deSerialize(&bufferAdapter, TEST_ARRAY.data(), &deserSize,
+ SerializeIF::Endianness::MACHINE);
+ REQUIRE(deserSize == 4);
+ for(uint8_t idx = 0; idx < 4; idx++) {
+ REQUIRE(testBuf[idx] == idx + 1);
+ }
+ }
+
+ SECTION("Serialize incrementing") {
+ SerializeAdapter::serialize(&testBool, &pArray, &serializedSize,
+ TEST_ARRAY.size(), SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::serialize(&tvUint8, &pArray, &serializedSize,
+ TEST_ARRAY.size(), SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::serialize(&tvUint16, &pArray, &serializedSize,
+ TEST_ARRAY.size(), SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::serialize(&tvUint32, &pArray, &serializedSize,
+ TEST_ARRAY.size(), SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::serialize(&tvInt8, &pArray, &serializedSize,
+ TEST_ARRAY.size(), SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::serialize(&tvInt16, &pArray, &serializedSize,
+ TEST_ARRAY.size(), SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::serialize(&tvInt32, &pArray, &serializedSize,
+ TEST_ARRAY.size(), SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::serialize(&tvUint64, &pArray, &serializedSize,
+ TEST_ARRAY.size(), SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::serialize(&tvFloat, &pArray, &serializedSize,
+ TEST_ARRAY.size(), SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::serialize(&tvDouble, &pArray, &serializedSize, TEST_ARRAY.size(),
+ SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::serialize(&tvSfloat, &pArray, &serializedSize, TEST_ARRAY.size(),
+ SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::serialize(&tvSdouble, &pArray, &serializedSize, TEST_ARRAY.size(),
+ SerializeIF::Endianness::MACHINE);
+ REQUIRE (serializedSize == 47);
}
- SECTION("Deserializing") {
- p_array = test_array.data();
- size_t remaining_size = serialized_size;
- SerializeAdapter::deSerialize(&test_value_bool,
- const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
- SerializeAdapter::deSerialize(&tv_uint8,
- const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
- SerializeAdapter::deSerialize(&tv_uint16,
- const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
- SerializeAdapter::deSerialize(&tv_uint32,
- const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
- SerializeAdapter::deSerialize(&tv_int8,
- const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
- SerializeAdapter::deSerialize(&tv_int16,
- const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
- SerializeAdapter::deSerialize(&tv_int32,
- const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
- SerializeAdapter::deSerialize(&tv_uint64,
- const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
- SerializeAdapter::deSerialize(&tv_float,
- const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
- SerializeAdapter::deSerialize(&tv_double,
- const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
- SerializeAdapter::deSerialize(&tv_sfloat,
- const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
- SerializeAdapter::deSerialize(&tv_sdouble,
- const_cast(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
+ SECTION("Deserialize decrementing") {
+ pArray = TEST_ARRAY.data();
+ size_t remaining_size = serializedSize;
+ SerializeAdapter::deSerialize(&testBool, const_cast(&pArray),
+ &remaining_size, SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::deSerialize(&tvUint8, const_cast(&pArray),
+ &remaining_size, SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::deSerialize(&tvUint16, const_cast(&pArray),
+ &remaining_size, SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::deSerialize(&tvUint32, const_cast(&pArray),
+ &remaining_size, SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::deSerialize(&tvInt8, const_cast(&pArray),
+ &remaining_size, SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::deSerialize(&tvInt16, const_cast(&pArray),
+ &remaining_size, SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::deSerialize(&tvInt32, const_cast(&pArray),
+ &remaining_size, SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::deSerialize(&tvUint64,
+ const_cast(&pArray), &remaining_size, SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::deSerialize(&tvFloat,
+ const_cast(&pArray), &remaining_size, SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::deSerialize(&tvDouble,
+ const_cast(&pArray), &remaining_size, SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::deSerialize(&tvSfloat,
+ const_cast(&pArray), &remaining_size, SerializeIF::Endianness::MACHINE);
+ SerializeAdapter::deSerialize(&tvSdouble,
+ const_cast(&pArray), &remaining_size, SerializeIF::Endianness::MACHINE);
- REQUIRE(test_value_bool == true);
- REQUIRE(tv_uint8 == 5);
- REQUIRE(tv_uint16 == 283);
- REQUIRE(tv_uint32 == 929221);
- REQUIRE(tv_uint64 == 2929329429);
- REQUIRE(tv_int8 == -16);
- REQUIRE(tv_int16 == -829);
- REQUIRE(tv_int32 == -2312);
+ REQUIRE(testBool == true);
+ REQUIRE(tvUint8 == 5);
+ REQUIRE(tvUint16 == 283);
+ REQUIRE(tvUint32 == 929221);
+ REQUIRE(tvUint64 == 2929329429);
+ REQUIRE(tvInt8 == -16);
+ REQUIRE(tvInt16 == -829);
+ REQUIRE(tvInt32 == -2312);
- REQUIRE(tv_float == Catch::Approx(8.214921));
- REQUIRE(tv_double == Catch::Approx(9.2132142141e8));
- REQUIRE(tv_sfloat == Catch::Approx(-922.2321321));
- REQUIRE(tv_sdouble == Catch::Approx(-2.2421e19));
+ REQUIRE(tvFloat == Catch::Approx(8.214921));
+ REQUIRE(tvDouble == Catch::Approx(9.2132142141e8));
+ REQUIRE(tvSfloat == Catch::Approx(-922.2321321));
+ REQUIRE(tvSdouble == Catch::Approx(-2.2421e19));
}
}