Compare commits
1 Commits
mohr-patch
...
mohr/relse
Author | SHA1 | Date | |
---|---|---|---|
b8d2fa530b |
94
CHANGELOG.md
94
CHANGELOG.md
@ -12,8 +12,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## Fixes
|
||||
|
||||
- Mode Service: Add allowed subservice
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/739
|
||||
- `CService200ModeManagement`: Various bugfixes which lead to now execution complete being generated
|
||||
on mode announcements, duplicate mode reply generated on announce commands, and the mode read
|
||||
subservice not working properly.
|
||||
@ -49,36 +47,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- `TcpTmTcServer.cpp`: The server was actually not able to handle
|
||||
CCSDS packets which were clumped together. This has been fixed now.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/673
|
||||
- `CServiceHealthCommanding`: Add announce all health info implementation
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/725
|
||||
- various fixes related to linux Unittests and memory leaks
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/715
|
||||
- small fix to allow teardown handling
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/713
|
||||
- fix compiler warning for fixed array list copy ctor
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/704
|
||||
- missing include
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/703
|
||||
- defaultconfig did not build anymore
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/702
|
||||
- hotfix
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/699
|
||||
- small fix for helper
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/698
|
||||
- missing retval conv
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/697
|
||||
- DHB Countdown Bug
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/693
|
||||
- doc corrections
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/687
|
||||
- better error printout
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/686
|
||||
- include correction
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/683
|
||||
- better warning for missing include paths
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/676
|
||||
- Service 11 regression
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/670
|
||||
|
||||
## Added
|
||||
|
||||
@ -95,32 +63,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/709
|
||||
- Add new `UnsignedByteField` class
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/660
|
||||
- publish documentation for development and master branch
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/681
|
||||
- Add Linux HAL options
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/663
|
||||
- Expand SerializeIF
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/656
|
||||
- PUS Service 11: Additional Safety Check
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/666
|
||||
- improvements for auto-formatter script
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/665
|
||||
- provide a weak print char impl
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/674
|
||||
|
||||
## Removed
|
||||
|
||||
- now that doc server is up, remove markdown files
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/688
|
||||
- remove bsp specific code
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/679
|
||||
|
||||
## Changes
|
||||
|
||||
- `CService201HealthCommanding` renamed to `CServiceHealthCommanding`,
|
||||
service ID customizable now. `CServiceHealthCommanding` expects configuration struct
|
||||
`HealthServiceCfg` now
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/725
|
||||
- `AcceptsTelemetryIF`: `getReportReceptionQueue` is const now
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/712
|
||||
- Moved some container returnvalues to dedicated header and namespace
|
||||
@ -154,41 +99,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- `DeviceHandlerBase`: New signature of `handleDeviceTm` which expects
|
||||
a `const SerializeIF&` and additional helper variant which expects `const uint8_t*`
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/671
|
||||
- Move some generic `StorageManagerIF` implementations from `LocalPool` to
|
||||
interface itself so it can be re-used more easily. Also add new
|
||||
abstract function `bool hasDataAtId(store_address_t storeId) const`.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/685
|
||||
- Improvements for `AcceptsTelemetryIF` and `AcceptsTelecommandsIF`:
|
||||
- Make functions `const` where it makes sense
|
||||
- Add `const char* getName const` abstract function
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/684
|
||||
- Generic TMTC Bridge Update
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/734
|
||||
- comment tweak to event parser can read everything
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/732
|
||||
- CMakeLists file updates
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/731
|
||||
- improve srv20 error messages
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/723
|
||||
- I2C Linux: remove duplicate printout
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/718
|
||||
- printout handling improvements
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/717
|
||||
- vec getter, reset for content
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/716
|
||||
- updates for source sequence counter
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/714
|
||||
- SP reader getPacketData is const now
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/708
|
||||
- refactoring of serial drivers for linux
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/705
|
||||
- Local Pool Update Remove Add Data Ignore Fault Argument
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/701
|
||||
- Switch to new documentation server
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/694
|
||||
- Windows Tweaks
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/691
|
||||
- Refactor Local Pool API
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/667
|
||||
- group MGM data in local pool vectors
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/664
|
||||
|
||||
- Move some generic `StorageManagerIF` implementations from `LocalPool` to
|
||||
interface itself so it can be re-used more easily. Also add new
|
||||
abstract function `bool hasDataAtId(store_address_t storeId) const`.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/685
|
||||
|
||||
## CFDP
|
||||
|
||||
|
@ -13,7 +13,7 @@ list(APPEND CMAKE_MODULE_PATH
|
||||
# Version file handling #
|
||||
# ##############################################################################
|
||||
|
||||
set(FSFW_VERSION_IF_GIT_FAILS 6)
|
||||
set(FSFW_VERSION_IF_GIT_FAILS 5)
|
||||
set(FSFW_SUBVERSION_IF_GIT_FAILS 0)
|
||||
set(FSFW_REVISION_IF_GIT_FAILS 0)
|
||||
|
||||
|
209
README.md
209
README.md
@ -1 +1,208 @@
|
||||
-
|
||||

|
||||
|
||||
# Flight Software Framework (FSFW)
|
||||
|
||||
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.
|
||||
|
||||
## Getting started
|
||||
|
||||
The [Hosted FSFW example](https://egit.irs.uni-stuttgart.de/fsfw/fsfw-example-hosted) provides a
|
||||
good starting point and a demo to see the FSFW capabilities.
|
||||
It is recommended to get started by building and playing around with the demo application.
|
||||
There are also other examples provided for all OSALs using the popular embedded platforms
|
||||
Raspberry Pi, Beagle Bone Black and STM32H7.
|
||||
|
||||
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.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
The Embedded Template Library (etl) is a dependency of the FSFW which is automatically
|
||||
installed and provided by the build system unless the correction version was installed.
|
||||
The current recommended version can be found inside the fsfw `CMakeLists.txt` file or by using
|
||||
`ccmake` and looking up the `FSFW_ETL_LIB_MAJOR_VERSION` variable.
|
||||
|
||||
You can install the ETL library like this. On Linux, it might be necessary to add `sudo` before
|
||||
the install call:
|
||||
|
||||
```cpp
|
||||
git clone https://github.com/ETLCPP/etl
|
||||
cd etl
|
||||
git checkout <currentRecommendedVersion>
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
cmake --install .
|
||||
```
|
||||
|
||||
It is recommended to install `20.27.2` or newer for the package version handling of
|
||||
ETL to work.
|
||||
|
||||
## Adding the library
|
||||
|
||||
The following steps show how to add and use FSFW components. It is still recommended to
|
||||
try out the example mentioned above to get started, but the following steps show how to
|
||||
add and link against the FSFW library in general.
|
||||
|
||||
1. Add this repository as a submodule
|
||||
|
||||
```sh
|
||||
git submodule add https://egit.irs.uni-stuttgart.de/fsfw/fsfw.git fsfw
|
||||
```
|
||||
|
||||
2. Add the following directive inside the uppermost `CMakeLists.txt` file of your project
|
||||
|
||||
```cmake
|
||||
add_subdirectory(fsfw)
|
||||
```
|
||||
|
||||
3. Make sure to provide a configuration folder and supply the path to that folder with
|
||||
the `FSFW_CONFIG_PATH` CMake variable from the uppermost `CMakeLists.txt` file.
|
||||
It is also necessary to provide the `printChar` function. You can find an example
|
||||
implementation for a hosted build
|
||||
[here](https://egit.irs.uni-stuttgart.de/fsfw/fsfw-example-hosted/src/branch/master/bsp_hosted/utility/printChar.c).
|
||||
|
||||
4. Link against the FSFW library
|
||||
|
||||
```sh
|
||||
target_link_libraries(${YourProjectName} PRIVATE fsfw)
|
||||
```
|
||||
|
||||
5. It should now be possible use the FSFW as a static library from the user code.
|
||||
|
||||
## Building the unittests
|
||||
|
||||
The FSFW also has unittests which use the [Catch2 library](https://github.com/catchorg/Catch2).
|
||||
These are built by setting the CMake option `FSFW_BUILD_UNITTESTS` to `ON` or `TRUE`
|
||||
from your project `CMakeLists.txt` file or from the command line.
|
||||
|
||||
You can install the Catch2 library, which prevents the build system to avoid re-downloading
|
||||
the dependency if the unit tests are completely rebuilt. The current recommended version
|
||||
can be found inside the fsfw `CMakeLists.txt` file or by using `ccmake` and looking up
|
||||
the `FSFW_CATCH2_LIB_VERSION` variable.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/catchorg/Catch2.git
|
||||
cd Catch2
|
||||
git checkout <currentRecommendedVersion>
|
||||
cmake -Bbuild -H. -DBUILD_TESTING=OFF
|
||||
sudo cmake --build build/ --target install
|
||||
```
|
||||
|
||||
The fsfw-tests binary will be built as part of the static library and dropped alongside it.
|
||||
If the unittests are built, the library and the tests will be built with coverage information by
|
||||
default. This can be disabled by setting the `FSFW_TESTS_COV_GEN` option to `OFF` or `FALSE`.
|
||||
|
||||
You can use the following commands inside the `fsfw` folder to set up the build system
|
||||
|
||||
```sh
|
||||
mkdir build-tests && cd build-tests
|
||||
cmake -DFSFW_BUILD_TESTS=ON -DFSFW_OSAL=host -DCMAKE_BUILD_TYPE=Debug ..
|
||||
```
|
||||
|
||||
You can also use `-DFSFW_OSAL=linux` on Linux systems.
|
||||
|
||||
Coverage data in HTML format can be generated using the `CodeCoverage`
|
||||
[CMake module](https://github.com/bilke/cmake-modules/tree/master).
|
||||
To build the unittests, run them and then generate the coverage data in this format,
|
||||
the following command can be used inside the build directory after the build system was set up
|
||||
|
||||
```sh
|
||||
cmake --build . -- fsfw-tests_coverage -j
|
||||
```
|
||||
|
||||
The `coverage.py` script located in the `script` folder can also be used to do this conveniently.
|
||||
|
||||
## Building the documentations
|
||||
|
||||
The FSFW documentation is built using the tools Sphinx, doxygen and breathe based on the
|
||||
instructions provided in [this blogpost](https://devblogs.microsoft.com/cppblog/clear-functional-c-documentation-with-sphinx-breathe-doxygen-cmake/). If you
|
||||
want to do this locally, set up the prerequisites first. This requires a ``python3``
|
||||
installation as well. Example here is for Ubuntu.
|
||||
|
||||
```sh
|
||||
sudo apt-get install doxygen graphviz
|
||||
```
|
||||
|
||||
And the following Python packages
|
||||
|
||||
```sh
|
||||
python3 -m pip install sphinx breathe
|
||||
```
|
||||
|
||||
You can set up a documentation build system using the following commands
|
||||
|
||||
```sh
|
||||
mkdir build-docs && cd build-docs
|
||||
cmake -DFSFW_BUILD_DOCS=ON -DFSFW_OSAL=host ..
|
||||
```
|
||||
|
||||
Then you can generate the documentation using
|
||||
|
||||
```sh
|
||||
cmake --build . -- Sphinx -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.
|
||||
|
||||
## 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 `auto-format.sh` helper script to format all source files consistently. Furthermore cmake-format is required to format CMake files which can be installed with:
|
||||
````sh
|
||||
sudo pip install cmakelang
|
||||
````
|
||||
|
||||
## Index
|
||||
|
||||
[1. High-level overview](docs/README-highlevel.md#top) <br>
|
||||
[2. Core components](docs/README-core.md#top) <br>
|
||||
[3. Configuration](docs/README-config.md#top) <br>
|
||||
[4. OSAL overview](docs/README-osal.md#top) <br>
|
||||
[5. PUS services](docs/README-pus.md#top) <br>
|
||||
[6. Device Handler overview](docs/README-devicehandlers.md#top) <br>
|
||||
[7. Controller overview](docs/README-controllers.md#top) <br>
|
||||
[8. Local Data Pools](docs/README-localpools.md#top) <br>
|
||||
|
||||
|
||||
|
||||
|
89
scripts/check_release.py
Executable file → Normal file
89
scripts/check_release.py
Executable file → Normal file
@ -1,40 +1,29 @@
|
||||
#! /bin/python
|
||||
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import urllib.request
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="List undocumented PRs"
|
||||
)
|
||||
parser.add_argument("-v", "--version", type=str, required=True)
|
||||
parser.add_argument("-m", "--milestone", type=int, required=True)
|
||||
args = parser.parse_args()
|
||||
|
||||
match = re.search("([0-9]+\.[0-9]+\.[0-9]+)", args.version)
|
||||
milestone = args.milestone
|
||||
|
||||
with urllib.request.urlopen("https://egit.irs.uni-stuttgart.de/api/v1/repos/fsfw/fsfw/milestones/" + str(milestone)) as milestone_json:
|
||||
milestone_title = json.load(milestone_json)['title']
|
||||
|
||||
match = re.search("(v[0-9]+\.[0-9]+\.[0-9]+)", milestone_title)
|
||||
|
||||
if not match:
|
||||
print("invalid version")
|
||||
print("invalid Milestone name")
|
||||
exit(1)
|
||||
|
||||
version = "v" + match.group(1)
|
||||
version = match.group(0)
|
||||
|
||||
print("looking for milestone for " + version + " ...")
|
||||
|
||||
|
||||
with urllib.request.urlopen("https://egit.irs.uni-stuttgart.de/api/v1/repos/fsfw/fsfw/milestones?name=" + version) as milestone_json:
|
||||
milestones = json.load(milestone_json)
|
||||
if (len(milestones) == 0):
|
||||
print("did not find any milestone")
|
||||
exit(1)
|
||||
if (len(milestones) > 1):
|
||||
print("found multiple milestons")
|
||||
milestone_title = milestones[0]['title']
|
||||
milestone = str(milestones[0]['id'])
|
||||
print("Using Milestone \""+ milestone_title + "\" with id " + milestone)
|
||||
print("detected Version " + version)
|
||||
|
||||
milestone_prs = []
|
||||
|
||||
@ -44,67 +33,11 @@ def main() -> None:
|
||||
with urllib.request.urlopen("https://egit.irs.uni-stuttgart.de/api/v1/repos/fsfw/fsfw/pulls?state=closed&milestone=" + str(milestone) + "&limit=100&page=" + str(page)) as pull_requests_json:
|
||||
pull_requests = json.load(pull_requests_json)
|
||||
for pr in pull_requests:
|
||||
milestone_prs.append({'number': str(pr['number']), 'title' : pr['title']})
|
||||
milestone_prs.append(pr['number'])
|
||||
page += 1
|
||||
last_count = len(pull_requests)
|
||||
|
||||
print("Found " + str(len(milestone_prs)) + " closed PRs in Milestone")
|
||||
|
||||
print("looking for CHANGELOG.md ...")
|
||||
|
||||
path = Path(".")
|
||||
|
||||
files = list(path.glob("CHANGELOG.md"))
|
||||
|
||||
if (len(files) != 1):
|
||||
files = list(path.glob("../CHANGELOG.md"))
|
||||
|
||||
if (len(files) != 1):
|
||||
print("did not find CHANGELOG.md. Run script in either root directory or scripts subfolder.")
|
||||
exit(1)
|
||||
|
||||
print("Scanning CHANGELOG.md ...")
|
||||
|
||||
changelog_prs = []
|
||||
|
||||
with open(files[0]) as changelog:
|
||||
line = changelog.readline()
|
||||
while (line):
|
||||
#print("line: " + line)
|
||||
match = re.search("\#.+(v[0-9]+\.[0-9]+\.[0-9]+)", line)
|
||||
if (match):
|
||||
if match.group(1) == version:
|
||||
#print("found version")
|
||||
line = changelog.readline()
|
||||
continue
|
||||
else:
|
||||
#print("done with " + match.group(1))
|
||||
break
|
||||
|
||||
match = re.search("PR: https://egit\.irs\.uni-stuttgart\.de/fsfw/fsfw/pulls/([0-9]+)", line)
|
||||
if match:
|
||||
changelog_prs.append(match.group(1))
|
||||
|
||||
line = changelog.readline()
|
||||
|
||||
print("Found " + str(len(changelog_prs)) + " PRs in CHANGELOG.md")
|
||||
|
||||
print("")
|
||||
|
||||
copy_array = changelog_prs.copy()
|
||||
print("PRs in CHANGELOG.md that are not in Milestone:")
|
||||
for pr in milestone_prs:
|
||||
if pr['number'] in copy_array:
|
||||
copy_array.remove(pr['number'])
|
||||
for pr in copy_array:
|
||||
print("https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/" + pr)
|
||||
|
||||
print("")
|
||||
|
||||
print("PRs in milestone that are not in CHANGELOG.md:")
|
||||
|
||||
for pr in milestone_prs:
|
||||
if pr['number'] not in changelog_prs:
|
||||
print("- " + pr['title'] + "\n PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/" + pr['number'])
|
||||
|
||||
main()
|
||||
|
@ -8,8 +8,6 @@
|
||||
#include "HealthTableIF.h"
|
||||
|
||||
class HealthTable : public HealthTableIF, public SystemObject {
|
||||
friend class CServiceHealthCommanding;
|
||||
|
||||
public:
|
||||
explicit HealthTable(object_id_t objectid);
|
||||
~HealthTable() override;
|
||||
|
@ -9,4 +9,4 @@ target_sources(
|
||||
Service17Test.cpp
|
||||
Service20ParameterManagement.cpp
|
||||
CService200ModeCommanding.cpp
|
||||
CServiceHealthCommanding.cpp)
|
||||
CService201HealthCommanding.cpp)
|
||||
|
@ -20,7 +20,6 @@ ReturnValue_t CService200ModeCommanding::isValidSubservice(uint8_t subservice) {
|
||||
case (Subservice::COMMAND_MODE_COMMAND):
|
||||
case (Subservice::COMMAND_MODE_READ):
|
||||
case (Subservice::COMMAND_MODE_ANNOUNCE):
|
||||
case (Subservice::COMMAND_MODE_ANNOUNCE_RECURSIVELY):
|
||||
return returnvalue::OK;
|
||||
default:
|
||||
return AcceptsTelecommandsIF::INVALID_SUBSERVICE;
|
||||
|
106
src/fsfw/pus/CService201HealthCommanding.cpp
Normal file
106
src/fsfw/pus/CService201HealthCommanding.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
#include "fsfw/pus/CService201HealthCommanding.h"
|
||||
|
||||
#include "fsfw/health/HasHealthIF.h"
|
||||
#include "fsfw/health/HealthMessage.h"
|
||||
#include "fsfw/objectmanager/ObjectManager.h"
|
||||
#include "fsfw/pus/servicepackets/Service201Packets.h"
|
||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||
|
||||
CService201HealthCommanding::CService201HealthCommanding(object_id_t objectId, uint16_t apid,
|
||||
uint8_t serviceId,
|
||||
uint8_t numParallelCommands,
|
||||
uint16_t commandTimeoutSeconds)
|
||||
: CommandingServiceBase(objectId, apid, "PUS 201 Health MGMT", serviceId, numParallelCommands,
|
||||
commandTimeoutSeconds) {}
|
||||
|
||||
ReturnValue_t CService201HealthCommanding::isValidSubservice(uint8_t subservice) {
|
||||
switch (subservice) {
|
||||
case (Subservice::COMMAND_SET_HEALTH):
|
||||
case (Subservice::COMMAND_ANNOUNCE_HEALTH):
|
||||
case (Subservice::COMMAND_ANNOUNCE_HEALTH_ALL):
|
||||
return returnvalue::OK;
|
||||
default:
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "Invalid Subservice" << std::endl;
|
||||
#endif
|
||||
return AcceptsTelecommandsIF::INVALID_SUBSERVICE;
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t CService201HealthCommanding::getMessageQueueAndObject(uint8_t subservice,
|
||||
const uint8_t *tcData,
|
||||
size_t tcDataLen,
|
||||
MessageQueueId_t *id,
|
||||
object_id_t *objectId) {
|
||||
if (tcDataLen < sizeof(object_id_t)) {
|
||||
return CommandingServiceBase::INVALID_TC;
|
||||
}
|
||||
SerializeAdapter::deSerialize(objectId, &tcData, &tcDataLen, SerializeIF::Endianness::BIG);
|
||||
|
||||
return checkInterfaceAndAcquireMessageQueue(id, objectId);
|
||||
}
|
||||
|
||||
ReturnValue_t CService201HealthCommanding::checkInterfaceAndAcquireMessageQueue(
|
||||
MessageQueueId_t *messageQueueToSet, const object_id_t *objectId) {
|
||||
auto *destination = ObjectManager::instance()->get<HasHealthIF>(*objectId);
|
||||
if (destination == nullptr) {
|
||||
return CommandingServiceBase::INVALID_OBJECT;
|
||||
}
|
||||
|
||||
*messageQueueToSet = destination->getCommandQueue();
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t CService201HealthCommanding::prepareCommand(CommandMessage *message,
|
||||
uint8_t subservice, const uint8_t *tcData,
|
||||
size_t tcDataLen, uint32_t *state,
|
||||
object_id_t objectId) {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
switch (subservice) {
|
||||
case (Subservice::COMMAND_SET_HEALTH): {
|
||||
HealthSetCommand healthCommand;
|
||||
result = healthCommand.deSerialize(&tcData, &tcDataLen, SerializeIF::Endianness::BIG);
|
||||
if (result != returnvalue::OK) {
|
||||
break;
|
||||
}
|
||||
HealthMessage::setHealthMessage(message, HealthMessage::HEALTH_SET,
|
||||
healthCommand.getHealth());
|
||||
break;
|
||||
}
|
||||
case (Subservice::COMMAND_ANNOUNCE_HEALTH): {
|
||||
HealthMessage::setHealthMessage(message, HealthMessage::HEALTH_ANNOUNCE);
|
||||
break;
|
||||
}
|
||||
case (Subservice::COMMAND_ANNOUNCE_HEALTH_ALL): {
|
||||
HealthMessage::setHealthMessage(message, HealthMessage::HEALTH_ANNOUNCE_ALL);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// Should never happen, subservice was already checked
|
||||
result = returnvalue::FAILED;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t CService201HealthCommanding::handleReply(const CommandMessage *reply,
|
||||
Command_t previousCommand, uint32_t *state,
|
||||
CommandMessage *optionalNextCommand,
|
||||
object_id_t objectId, bool *isStep) {
|
||||
Command_t replyId = reply->getCommand();
|
||||
if (replyId == HealthMessage::REPLY_HEALTH_SET) {
|
||||
return EXECUTION_COMPLETE;
|
||||
} else if (replyId == CommandMessageIF::REPLY_REJECTED) {
|
||||
return reply->getReplyRejectedReason();
|
||||
}
|
||||
return CommandingServiceBase::INVALID_REPLY;
|
||||
}
|
||||
|
||||
// Not used for now, health state already reported by event
|
||||
[[maybe_unused]] ReturnValue_t CService201HealthCommanding::prepareHealthSetReply(
|
||||
const CommandMessage *reply) {
|
||||
auto health = static_cast<uint8_t>(HealthMessage::getHealth(reply));
|
||||
auto oldHealth = static_cast<uint8_t>(HealthMessage::getOldHealth(reply));
|
||||
HealthSetReply healthSetReply(health, oldHealth);
|
||||
return sendTmPacket(Subservice::REPLY_HEALTH_SET, healthSetReply);
|
||||
}
|
@ -1,26 +1,8 @@
|
||||
#ifndef FSFW_PUS_CSERVICE201HEALTHCOMMANDING_H_
|
||||
#define FSFW_PUS_CSERVICE201HEALTHCOMMANDING_H_
|
||||
|
||||
#include <fsfw/health/HealthTable.h>
|
||||
|
||||
#include "fsfw/tmtcservices/CommandingServiceBase.h"
|
||||
|
||||
struct HealthServiceCfg {
|
||||
HealthServiceCfg(object_id_t objectId, uint16_t apid, object_id_t healthTable,
|
||||
uint16_t maxNumHealthInfoPerCycle)
|
||||
: objectId(objectId),
|
||||
apid(apid),
|
||||
table(healthTable),
|
||||
maxNumHealthInfoPerCycle(maxNumHealthInfoPerCycle) {}
|
||||
object_id_t objectId;
|
||||
uint16_t apid;
|
||||
object_id_t table;
|
||||
uint16_t maxNumHealthInfoPerCycle;
|
||||
uint8_t service = 201;
|
||||
uint8_t numParallelCommands = 4;
|
||||
uint16_t commandTimeoutSeconds = 60;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Custom PUS service to set health of all objects
|
||||
* implementing hasHealthIF.
|
||||
@ -35,12 +17,11 @@ struct HealthServiceCfg {
|
||||
* child class like this service
|
||||
*
|
||||
*/
|
||||
class CServiceHealthCommanding : public CommandingServiceBase {
|
||||
class CService201HealthCommanding : public CommandingServiceBase {
|
||||
public:
|
||||
CServiceHealthCommanding(HealthServiceCfg args);
|
||||
~CServiceHealthCommanding() override = default;
|
||||
|
||||
ReturnValue_t initialize() override;
|
||||
CService201HealthCommanding(object_id_t objectId, uint16_t apid, uint8_t serviceId,
|
||||
uint8_t numParallelCommands = 4, uint16_t commandTimeoutSeconds = 60);
|
||||
~CService201HealthCommanding() override = default;
|
||||
|
||||
protected:
|
||||
/* CSB abstract function implementations */
|
||||
@ -56,14 +37,7 @@ class CServiceHealthCommanding : public CommandingServiceBase {
|
||||
CommandMessage *optionalNextCommand, object_id_t objectId,
|
||||
bool *isStep) override;
|
||||
|
||||
void doPeriodicOperation() override;
|
||||
|
||||
private:
|
||||
const object_id_t healthTableId;
|
||||
HealthTable *healthTable;
|
||||
uint16_t maxNumHealthInfoPerCycle = 0;
|
||||
bool reportAllHealth = false;
|
||||
ReturnValue_t iterateHealthTable(bool reset);
|
||||
static ReturnValue_t checkInterfaceAndAcquireMessageQueue(MessageQueueId_t *MessageQueueToSet,
|
||||
const object_id_t *objectId);
|
||||
|
@ -1,158 +0,0 @@
|
||||
#include <fsfw/events/EventManagerIF.h>
|
||||
#include <fsfw/pus/CServiceHealthCommanding.h>
|
||||
|
||||
#include "fsfw/health/HasHealthIF.h"
|
||||
#include "fsfw/health/HealthMessage.h"
|
||||
#include "fsfw/objectmanager/ObjectManager.h"
|
||||
#include "fsfw/pus/servicepackets/Service201Packets.h"
|
||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||
|
||||
CServiceHealthCommanding::CServiceHealthCommanding(HealthServiceCfg args)
|
||||
: CommandingServiceBase(args.objectId, args.apid, "PUS 201 Health MGMT", args.service,
|
||||
args.numParallelCommands, args.commandTimeoutSeconds),
|
||||
healthTableId(args.table),
|
||||
maxNumHealthInfoPerCycle(args.maxNumHealthInfoPerCycle) {}
|
||||
|
||||
ReturnValue_t CServiceHealthCommanding::initialize() {
|
||||
ReturnValue_t result = CommandingServiceBase::initialize();
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
healthTable = ObjectManager::instance()->get<HealthTable>(healthTableId);
|
||||
if (healthTable == nullptr) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t CServiceHealthCommanding::isValidSubservice(uint8_t subservice) {
|
||||
switch (subservice) {
|
||||
case (Subservice::COMMAND_SET_HEALTH):
|
||||
case (Subservice::COMMAND_ANNOUNCE_HEALTH):
|
||||
case (Subservice::COMMAND_ANNOUNCE_HEALTH_ALL):
|
||||
return returnvalue::OK;
|
||||
default:
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "Invalid Subservice" << std::endl;
|
||||
#endif
|
||||
return AcceptsTelecommandsIF::INVALID_SUBSERVICE;
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t CServiceHealthCommanding::getMessageQueueAndObject(uint8_t subservice,
|
||||
const uint8_t *tcData,
|
||||
size_t tcDataLen,
|
||||
MessageQueueId_t *id,
|
||||
object_id_t *objectId) {
|
||||
switch (subservice) {
|
||||
case (Subservice::COMMAND_SET_HEALTH):
|
||||
case (Subservice::COMMAND_ANNOUNCE_HEALTH): {
|
||||
if (tcDataLen < sizeof(object_id_t)) {
|
||||
return CommandingServiceBase::INVALID_TC;
|
||||
}
|
||||
SerializeAdapter::deSerialize(objectId, &tcData, &tcDataLen, SerializeIF::Endianness::BIG);
|
||||
|
||||
return checkInterfaceAndAcquireMessageQueue(id, objectId);
|
||||
}
|
||||
case (Subservice::COMMAND_ANNOUNCE_HEALTH_ALL): {
|
||||
return returnvalue::OK;
|
||||
}
|
||||
default: {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t CServiceHealthCommanding::checkInterfaceAndAcquireMessageQueue(
|
||||
MessageQueueId_t *messageQueueToSet, const object_id_t *objectId) {
|
||||
auto *destination = ObjectManager::instance()->get<HasHealthIF>(*objectId);
|
||||
if (destination == nullptr) {
|
||||
return CommandingServiceBase::INVALID_OBJECT;
|
||||
}
|
||||
|
||||
*messageQueueToSet = destination->getCommandQueue();
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t CServiceHealthCommanding::prepareCommand(CommandMessage *message, uint8_t subservice,
|
||||
const uint8_t *tcData, size_t tcDataLen,
|
||||
uint32_t *state, object_id_t objectId) {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
switch (subservice) {
|
||||
case (Subservice::COMMAND_SET_HEALTH): {
|
||||
HealthSetCommand healthCommand;
|
||||
result = healthCommand.deSerialize(&tcData, &tcDataLen, SerializeIF::Endianness::BIG);
|
||||
if (result != returnvalue::OK) {
|
||||
break;
|
||||
}
|
||||
HealthMessage::setHealthMessage(message, HealthMessage::HEALTH_SET,
|
||||
healthCommand.getHealth());
|
||||
break;
|
||||
}
|
||||
case (Subservice::COMMAND_ANNOUNCE_HEALTH): {
|
||||
HealthMessage::setHealthMessage(message, HealthMessage::HEALTH_ANNOUNCE);
|
||||
break;
|
||||
}
|
||||
case (Subservice::COMMAND_ANNOUNCE_HEALTH_ALL): {
|
||||
ReturnValue_t result = iterateHealthTable(true);
|
||||
if (result == returnvalue::OK) {
|
||||
reportAllHealth = true;
|
||||
return EXECUTION_COMPLETE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
default: {
|
||||
// Should never happen, subservice was already checked
|
||||
result = returnvalue::FAILED;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t CServiceHealthCommanding::handleReply(const CommandMessage *reply,
|
||||
Command_t previousCommand, uint32_t *state,
|
||||
CommandMessage *optionalNextCommand,
|
||||
object_id_t objectId, bool *isStep) {
|
||||
Command_t replyId = reply->getCommand();
|
||||
if (replyId == HealthMessage::REPLY_HEALTH_SET) {
|
||||
return EXECUTION_COMPLETE;
|
||||
} else if (replyId == CommandMessageIF::REPLY_REJECTED) {
|
||||
return reply->getReplyRejectedReason();
|
||||
}
|
||||
return CommandingServiceBase::INVALID_REPLY;
|
||||
}
|
||||
|
||||
void CServiceHealthCommanding::doPeriodicOperation() {
|
||||
if (reportAllHealth) {
|
||||
for (uint8_t i = 0; i < maxNumHealthInfoPerCycle; i++) {
|
||||
ReturnValue_t result = iterateHealthTable(false);
|
||||
if (result != returnvalue::OK) {
|
||||
reportAllHealth = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not used for now, health state already reported by event
|
||||
[[maybe_unused]] ReturnValue_t CServiceHealthCommanding::prepareHealthSetReply(
|
||||
const CommandMessage *reply) {
|
||||
auto health = static_cast<uint8_t>(HealthMessage::getHealth(reply));
|
||||
auto oldHealth = static_cast<uint8_t>(HealthMessage::getOldHealth(reply));
|
||||
HealthSetReply healthSetReply(health, oldHealth);
|
||||
return sendTmPacket(Subservice::REPLY_HEALTH_SET, healthSetReply);
|
||||
}
|
||||
|
||||
ReturnValue_t CServiceHealthCommanding::iterateHealthTable(bool reset) {
|
||||
std::pair<object_id_t, HasHealthIF::HealthState> pair;
|
||||
|
||||
ReturnValue_t result = healthTable->iterate(&pair, reset);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
} else {
|
||||
EventManagerIF::triggerEvent(pair.first, HasHealthIF::HEALTH_INFO, pair.second, pair.second);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user