Compare commits
321 Commits
bdbe0cc9da
...
mohr/romeo
Author | SHA1 | Date | |
---|---|---|---|
9110fd2728 | |||
8b9a468893 | |||
7ebfcdda60 | |||
5c25a2777c | |||
7f80e91a91 | |||
732419e239 | |||
d328f3e190 | |||
4c0d9ae039 | |||
753d587b69 | |||
979a7b7d75 | |||
a30146a799 | |||
ae2823ba2d | |||
3a433915f1 | |||
d766469f1e | |||
33a2fb48e5 | |||
531d8c45e8 | |||
14a7dd885b | |||
50a4ec97ef | |||
de93bff561 | |||
ad4adc7cba | |||
6a76857f5f | |||
2044e1cde2 | |||
b56b54951b | |||
1fc50a1562 | |||
e831ba11d2 | |||
0da490481e | |||
146c3471d0 | |||
1816c3f623 | |||
d03d5aa74c | |||
26e97ddf89 | |||
be3a57a795 | |||
318a933b3b | |||
13b97abf0d | |||
e8480cf962 | |||
a5e6107001 | |||
f95c373076 | |||
e03731bcf8 | |||
9fe8579377 | |||
2714e588d7 | |||
e905288adc | |||
3805ea50a7 | |||
699bd694cd | |||
4518fec65c | |||
dac1aacab2 | |||
0042f92fdf | |||
656faf8169 | |||
f84431e965 | |||
0cec9ebb73 | |||
a440b7c394 | |||
bbfc1b2b34 | |||
025b379e8b
|
|||
c35a0a8541 | |||
0f81d5e458 | |||
e0a072859b | |||
b50f092939 | |||
2f90e12179 | |||
8b77fac099 | |||
47503824d7 | |||
5e3f5c4121 | |||
1f36c082ef | |||
aa84e93603 | |||
8f63a0e747 | |||
6fc8f756a7 | |||
067cb7d0f8 | |||
d98ed40e3d | |||
2c17af4ef8 | |||
110fb43b9c | |||
b057250bfb | |||
066dd0d397 | |||
f735c2e9d4 | |||
dc7afc5415 | |||
9bf3ff95b7 | |||
61562b18ab | |||
d76d97a36b | |||
76b377c4c0 | |||
3562bf11b9 | |||
fffb2b61e5 | |||
94e5f62331 | |||
0a9c563bbc | |||
fa7675897d | |||
3a2393885f | |||
c752b6d143 | |||
b676040c7c | |||
010509efb4 | |||
dfb1633f00 | |||
5f7172e130 | |||
0c6465cd95 | |||
f94987c46d | |||
1809ce359b | |||
8c712441ab | |||
f1b0ca7cff | |||
000df85556 | |||
1fffcc2229 | |||
a419806a05 | |||
6445debfa1 | |||
8014e4adf9 | |||
134d908f26 | |||
40a9e12416 | |||
f39054edd4 | |||
5adf89b911 | |||
c2e6a22dec | |||
c8e065a713 | |||
3ed49dbae3 | |||
4cf52d5dfe | |||
539d7aac9e | |||
0a23f2c85a | |||
46230e6c6d | |||
b22d439300 | |||
7e7b3bbbc9 | |||
5b92247fbd | |||
e2b66df72e | |||
d9da55fdab | |||
7b828f233a | |||
c3d1000cd5 | |||
8e0e57714d | |||
066f7a6f9b | |||
cc9e54ea6b | |||
31465a4e0f | |||
c0e5d1eb99 | |||
3bc5d4a2e0 | |||
b1e9dd9e4a | |||
ab86599db3 | |||
034eb34c2e | |||
4374c7c4f4 | |||
5343844be5 | |||
e300490b93 | |||
0f811777a7 | |||
e93137939e | |||
c1f42618db | |||
1f88c006d9 | |||
7e94baceef | |||
61df451dd8 | |||
29ea89044e | |||
e487f5be87 | |||
9b05e8f274 | |||
eb223dae88 | |||
3656662d88 | |||
fe71978467 | |||
b646717a76 | |||
99d8c845f2 | |||
0e7c6b117f | |||
d16c5024dc | |||
a4531e4ced | |||
97c629ad84 | |||
bf12f284fa | |||
ba62c28b64 | |||
7adb47aecb | |||
8589f4d63a | |||
ca80589233 | |||
f2ebaed092 | |||
f0b89e98df | |||
5557d95994 | |||
fc24c9b5d8 | |||
7ef69c839c | |||
9b798d798e | |||
b13453f46b | |||
d0e322d7e2 | |||
ecde164f68 | |||
50930b41ba | |||
d6ee2ed400 | |||
f2150ff9c2 | |||
1b9c98f3fe | |||
742152b28e | |||
bf4ca56658 | |||
16ffa00155 | |||
14c681c93a | |||
0958c3a00e | |||
d699d16307 | |||
3b0fed733f | |||
23d3812fe3 | |||
dec7db3ae2 | |||
65a5abab49 | |||
0129783e34 | |||
cabe0868ec | |||
f05295bada | |||
b85ca64690 | |||
3bc3da5a8d | |||
f8c07ec9cf | |||
8199b8f359 | |||
cbc8dbcdd4 | |||
d31a5306f0 | |||
a236a5ec50 | |||
03620970e2 | |||
8fe8d810e9 | |||
9483c2809d | |||
fe3d6bd432 | |||
2a842666d5 | |||
c013fcc1f5 | |||
1b8fc2af19 | |||
72d7c43445 | |||
ab9b6c8c89 | |||
69d338f9bb | |||
68223869d5 | |||
93fda71989 | |||
7b0db08962 | |||
0956fbc740 | |||
b48e0fdc0d | |||
1d084ee22f | |||
1bea2344f6 | |||
d7e16a67a7 | |||
6021d897b8 | |||
83a6f0b5f8 | |||
a9c6c088f2 | |||
b6a3c206cc | |||
5b352978c5 | |||
819a298b19 | |||
16246d6ece | |||
5c84f12440 | |||
83c2c4825c | |||
c913fe40bf | |||
70ec08bf1d | |||
ef23665d9c | |||
eefc122292 | |||
bee33526a1 | |||
f715b65d6e | |||
c11af63015 | |||
11a22577be | |||
dc1583c932 | |||
94f1f1f908 | |||
81a7de2814 | |||
d26f230bee | |||
4db124c680 | |||
955579c856 | |||
2df66c9304 | |||
54ad6b3016 | |||
73e313c35b | |||
c6585c8645 | |||
dd2f42d22b | |||
d8a4675842 | |||
1164c21ddd | |||
77b1a85b47 | |||
652c31a683 | |||
bfe120636c | |||
a8041f220f | |||
dd636b186b | |||
3349fc36f8 | |||
f3e9277e59 | |||
d592f1ecbc | |||
6ec18171a8 | |||
518dcdef4b | |||
d9730032fd | |||
b3ac72b7db | |||
cd0cb43412 | |||
32c12b3dbf | |||
3e9acf476e | |||
99101ce2bf | |||
6b991045f7 | |||
df06064df0 | |||
337cb0d6c9 | |||
c283e0c988 | |||
448d20f3bd | |||
2316728d74 | |||
6f562e5f3e | |||
176f243194 | |||
d964fa2107 | |||
7b5ae6a445 | |||
8e362a000c | |||
7877776e24 | |||
3de0ae5a48 | |||
e0c780f21c | |||
876815b1c9 | |||
b0ecf87580 | |||
68ce8b5b08 | |||
fe03da6def | |||
95ac53c417 | |||
62f638a3d2 | |||
bd64591f30 | |||
e6a877f048 | |||
ea8c557ee8 | |||
0bdd780f82 | |||
9ec397c8b7 | |||
c54d9d7ba6 | |||
30c03c110c | |||
69f1be263a | |||
c7b5309dcb | |||
775d5632de | |||
4f3361eb2b | |||
9e6c1d60e5 | |||
12d0c23c13 | |||
5c3bb13834 | |||
292fe3e5e4 | |||
33530f2819 | |||
c0000a8635 | |||
5488ee715f | |||
0fea22d031 | |||
3b8ca09299 | |||
9a2146fa2d | |||
558550ecb9 | |||
72172a972b | |||
b4b11ebd3b | |||
bcbbc9763a | |||
0042372cb6 | |||
8dea13742f | |||
0f027d29d2 | |||
ce7146e468 | |||
a681a4a797 | |||
83b7b8707c | |||
4002b74ea2 | |||
4f7f8310c9 | |||
6eea711d9f | |||
9d626e0a5d | |||
1c527c4946 | |||
4c0f67adf5 | |||
450ad1dad4 | |||
1dfb3323f7 | |||
f5adcd0625 | |||
bc593c938d | |||
3f8f17a66e | |||
3260a03544 | |||
3ffdc0d31f | |||
d367baa42c | |||
59f8dd1322 | |||
61b4e68f3d | |||
e34664d265 | |||
20eb232bf5 | |||
dd281f8a67 | |||
f9e9ff320f | |||
f56646d2c3 | |||
ef18377cef | |||
0c057c66b1 | |||
34c50ce3e2 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -10,5 +10,8 @@
|
||||
.settings
|
||||
.metadata
|
||||
|
||||
# VSCode
|
||||
.vscode
|
||||
|
||||
/build*
|
||||
/cmake-build*
|
||||
|
179
CHANGELOG.md
179
CHANGELOG.md
@ -8,24 +8,164 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
# [unreleased]
|
||||
|
||||
# [v6.0.0]
|
||||
## Fixes
|
||||
|
||||
- Important bugfix in CFDP PDU header format: The entity length field and the transaction sequence
|
||||
number fields stored the actual length of the field instead of the length minus 1 like specified
|
||||
in the CFDP standard.
|
||||
- PUS Health Service: Size check for set health command.
|
||||
Perform operation completion for announce health command.
|
||||
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/746
|
||||
- Linux OSAL `getUptime` fix: Check validity of `/proc/uptime` file before reading uptime.
|
||||
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/745
|
||||
- Small tweak for version getter
|
||||
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/744
|
||||
|
||||
## Added
|
||||
|
||||
- add CFDP subsystem ID
|
||||
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/742
|
||||
|
||||
## Changed
|
||||
- Bump ETL version to 20.35.14
|
||||
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/748
|
||||
- Renamed `PCDU_2` subsystem ID to `POWER_SWITCH_IF`.
|
||||
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/743
|
||||
- Add new `PowerSwitchIF::SWITCH_UNKNOWN` returnvalue.
|
||||
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/743
|
||||
- Assert that `FixedArrayList` is larger than 0 at compile time.
|
||||
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/740
|
||||
|
||||
## Added
|
||||
|
||||
- `EventManager`: Add function to print all listeners.
|
||||
|
||||
## Changed
|
||||
|
||||
- `EventManager`: Queue depth is configurable now
|
||||
|
||||
# [v6.0.0] 2023-02-10
|
||||
|
||||
## 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.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/736
|
||||
- Memory leak fixes for the TCP/IP TMTC bridge.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/737
|
||||
- `Service9TimeManagement`: Fix the time dump at the `SET_TIME` subservice: Include clock timeval
|
||||
seconds instead of uptime.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/726
|
||||
- HAL MGM3100 Handler: Use axis specific gain/scaling factors. Previously,
|
||||
only the X scaling factor was used.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/724
|
||||
- HAL MGM3100 Handler: Z value was previously calculated with bytes of the X value.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/733
|
||||
- DHB `setNormalDatapoolEntriesInvalid`: The default implementation did not set the validity
|
||||
to false correctly because the `read` and `write` calls were missing.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/728
|
||||
- PUS TMTC creator module: Sequence flags were set to continuation segment (0b00) instead
|
||||
of the correct unsegmented flags (0b11) as specified in the standard.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/727
|
||||
- TC Scheduler Service 11: Add size and CRC check for contained TC.
|
||||
Bug: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/issues/719
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/720
|
||||
- Only delete health table entry in `HealthHelper` destructor if
|
||||
health table was set.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/710
|
||||
- I2C Bugfixes: Do not keep iterator as member and fix some incorrect handling with the iterator.
|
||||
Also properly reset the reply size for successfull transfers and erroneous transfers.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/700
|
||||
- Bugfix for Serial Buffer Stream: Setting `doActive` to false now
|
||||
actually fully disables printing.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/680
|
||||
- `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
|
||||
|
||||
- Empty constructor for `CdsShortTimeStamper` which does not do an object manager registration.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/730
|
||||
- `Service9TimeManagement`: Add `DUMP_TIME` (129) subservice.
|
||||
- `TcpTmTcServer`: Allow setting the `SO_REUSEADDR` and `SO_REUSEPORT`
|
||||
option on the TCP server. CTOR prototype has changed and expects an explicit
|
||||
TCP configuration struct to be passed.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/722
|
||||
- `DleParser` helper class to parse DLE encoded packets from a byte stream.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/711
|
||||
- `UioMapper` is able to resolve symlinks now.
|
||||
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
|
||||
so they can be used without template specification.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/707
|
||||
- Remove default secondary header argument for
|
||||
`uint16_t getTcSpacePacketIdFromApid(uint16_t apid, bool secondaryHeaderFlag)` and
|
||||
`uint16_t getTmSpacePacketIdFromApid(uint16_t apid, bool secondaryHeaderFlag)`
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/689
|
||||
- Removed `HasReturnvaluesIF` class in favor of `returnvalue` namespace with `OK` and `FAILED`
|
||||
constants.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/659
|
||||
@ -54,10 +194,37 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- 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
|
||||
- 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
|
||||
- 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
|
||||
|
||||
|
||||
## CFDP
|
||||
|
||||
@ -77,7 +244,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
implementation without an extra component
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/682
|
||||
|
||||
# [v5.0.0] 25.07.2022
|
||||
# [v5.0.0] 2022-07-25
|
||||
|
||||
## Changes
|
||||
|
||||
|
@ -13,7 +13,7 @@ list(APPEND CMAKE_MODULE_PATH
|
||||
# Version file handling #
|
||||
# ##############################################################################
|
||||
|
||||
set(FSFW_VERSION_IF_GIT_FAILS 5)
|
||||
set(FSFW_VERSION_IF_GIT_FAILS 6)
|
||||
set(FSFW_SUBVERSION_IF_GIT_FAILS 0)
|
||||
set(FSFW_REVISION_IF_GIT_FAILS 0)
|
||||
|
||||
@ -72,7 +72,7 @@ set(FSFW_ETL_LIB_MAJOR_VERSION
|
||||
20
|
||||
CACHE STRING "ETL library major version requirement")
|
||||
set(FSFW_ETL_LIB_VERSION
|
||||
${FSFW_ETL_LIB_MAJOR_VERSION}.28.0
|
||||
${FSFW_ETL_LIB_MAJOR_VERSION}.36.0
|
||||
CACHE STRING "ETL library exact version requirement")
|
||||
set(FSFW_ETL_LINK_TARGET etl::etl)
|
||||
|
||||
@ -80,7 +80,7 @@ set(FSFW_CATCH2_LIB_MAJOR_VERSION
|
||||
3
|
||||
CACHE STRING "Catch2 library major version requirement")
|
||||
set(FSFW_CATCH2_LIB_VERSION
|
||||
v${FSFW_CATCH2_LIB_MAJOR_VERSION}.1.0
|
||||
v${FSFW_CATCH2_LIB_MAJOR_VERSION}.3.2
|
||||
CACHE STRING "Catch2 library exact version requirement")
|
||||
|
||||
# Keep this off by default for now. See PR:
|
||||
@ -122,6 +122,7 @@ if(UNIX)
|
||||
option(FSFW_HAL_LINUX_ADD_PERIPHERAL_DRIVERS "Add Linux peripheral drivers"
|
||||
OFF)
|
||||
option(FSFW_HAL_LINUX_ADD_LIBGPIOD "Attempt to add Linux GPIOD drivers" OFF)
|
||||
option(FSFW_HAL_LINUX_ADD_SERIAL_DRIVERS "Add serial drivers" ON)
|
||||
endif()
|
||||
|
||||
# Optional sources
|
||||
@ -152,12 +153,12 @@ if(FSFW_BUILD_TESTS)
|
||||
"${MSG_PREFIX} Building the FSFW unittests in addition to the static library"
|
||||
)
|
||||
# Check whether the user has already installed Catch2 first
|
||||
find_package(Catch2 ${FSFW_CATCH2_LIB_MAJOR_VERSION})
|
||||
find_package(Catch2 ${FSFW_CATCH2_LIB_MAJOR_VERSION} QUIET)
|
||||
# Not installed, so use FetchContent to download and provide Catch2
|
||||
if(NOT Catch2_FOUND)
|
||||
message(
|
||||
STATUS
|
||||
"${MSG_PREFIX} Catch2 installation not found. Downloading Catch2 library with FetchContent"
|
||||
"${MSG_PREFIX} Catch2 installation not found. Downloading Catch2 library with FetchContent."
|
||||
)
|
||||
include(FetchContent)
|
||||
|
||||
@ -200,8 +201,8 @@ find_package(${FSFW_ETL_LIB_NAME} ${FSFW_ETL_LIB_MAJOR_VERSION} QUIET)
|
||||
if(NOT ${FSFW_ETL_LIB_NAME}_FOUND)
|
||||
message(
|
||||
STATUS
|
||||
"${MSG_PREFIX} No ETL installation was found with find_package. Installing and providing "
|
||||
"etl with FindPackage")
|
||||
"${MSG_PREFIX} ETL installation not found. Downloading ETL with FetchContent."
|
||||
)
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
@ -326,7 +327,8 @@ if(FSFW_BUILD_TESTS)
|
||||
"/usr/local/include/*"
|
||||
"*/fsfw_tests/*"
|
||||
"*/catch2-src/*"
|
||||
"*/fsfw_hal/*")
|
||||
"*/fsfw_hal/*"
|
||||
"unittests/*")
|
||||
endif()
|
||||
|
||||
target_link_options(${FSFW_TEST_TGT} PRIVATE -fprofile-arcs
|
||||
@ -344,8 +346,15 @@ if(FSFW_BUILD_TESTS)
|
||||
DEPENDENCIES ${FSFW_TEST_TGT})
|
||||
else()
|
||||
setup_target_for_coverage_lcov(
|
||||
NAME ${FSFW_TEST_TGT}_coverage EXECUTABLE ${FSFW_TEST_TGT}
|
||||
DEPENDENCIES ${FSFW_TEST_TGT})
|
||||
NAME
|
||||
${FSFW_TEST_TGT}_coverage
|
||||
EXECUTABLE
|
||||
${FSFW_TEST_TGT}
|
||||
DEPENDENCIES
|
||||
${FSFW_TEST_TGT}
|
||||
GENHTML_ARGS
|
||||
--html-epilog
|
||||
${CMAKE_SOURCE_DIR}/unittests/lcov_epilog.html)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
@ -5,7 +5,7 @@ RUN apt-get --yes upgrade
|
||||
|
||||
#tzdata is a dependency, won't install otherwise
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get --yes install gcc g++ cmake make lcov git valgrind nano iputils-ping python3 pip doxygen graphviz
|
||||
RUN apt-get --yes install gcc g++ cmake make lcov git valgrind nano iputils-ping python3 pip doxygen graphviz rsync
|
||||
|
||||
RUN python3 -m pip install sphinx breathe
|
||||
|
||||
@ -23,3 +23,7 @@ RUN git clone https://github.com/ETLCPP/etl.git && \
|
||||
|
||||
#ssh needs a valid user to work
|
||||
RUN adduser --uid 114 jenkins
|
||||
|
||||
#add documentation server to known hosts
|
||||
RUN echo "|1|/LzCV4BuTmTb2wKnD146l9fTKgQ=|NJJtVjvWbtRt8OYqFgcYRnMQyVw= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNL8ssTonYtgiR/6RRlSIK9WU1ywOcJmxFTLcEblAwH7oifZzmYq3XRfwXrgfMpylEfMFYfCU8JRqtmi19xc21A=" >> /etc/ssh/ssh_known_hosts
|
||||
RUN echo "|1|CcBvBc3EG03G+XM5rqRHs6gK/Gg=|oGeJQ+1I8NGI2THIkJsW92DpTzs= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNL8ssTonYtgiR/6RRlSIK9WU1ywOcJmxFTLcEblAwH7oifZzmYq3XRfwXrgfMpylEfMFYfCU8JRqtmi19xc21A=" >> /etc/ssh/ssh_known_hosts
|
114
automation/Jenkinsfile
vendored
114
automation/Jenkinsfile
vendored
@ -1,45 +1,87 @@
|
||||
pipeline {
|
||||
environment {
|
||||
BUILDDIR = 'cmake-build-tests'
|
||||
BUILDDIR_HOST = 'cmake-build-tests-host'
|
||||
BUILDDIR_LINUX = 'cmake-build-tests-linux'
|
||||
DOCDDIR = 'cmake-build-documentation'
|
||||
}
|
||||
agent {
|
||||
docker {
|
||||
image 'fsfw-ci:d5'
|
||||
args '--network host'
|
||||
image 'fsfw-ci:d6'
|
||||
args '--network host --sysctl fs.mqueue.msg_max=100'
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage('Clean') {
|
||||
steps {
|
||||
sh 'rm -rf $BUILDDIR'
|
||||
}
|
||||
}
|
||||
stage('Configure') {
|
||||
steps {
|
||||
dir(BUILDDIR) {
|
||||
sh 'cmake -DFSFW_OSAL=host -DFSFW_BUILD_TESTS=ON -DFSFW_CICD_BUILD=ON ..'
|
||||
stage('Host') {
|
||||
stages{
|
||||
stage('Clean') {
|
||||
steps {
|
||||
sh 'rm -rf $BUILDDIR_HOST'
|
||||
}
|
||||
}
|
||||
stage('Configure') {
|
||||
steps {
|
||||
dir(BUILDDIR_HOST) {
|
||||
sh 'cmake -DFSFW_OSAL=host -DFSFW_BUILD_TESTS=ON -DFSFW_CICD_BUILD=ON ..'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Build') {
|
||||
steps {
|
||||
dir(BUILDDIR_HOST) {
|
||||
sh 'cmake --build . -j4'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Unittests') {
|
||||
steps {
|
||||
dir(BUILDDIR_HOST) {
|
||||
sh 'cmake --build . -- fsfw-tests_coverage -j4'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Valgrind') {
|
||||
steps {
|
||||
dir(BUILDDIR_HOST) {
|
||||
sh 'valgrind --leak-check=full --error-exitcode=1 ./fsfw-tests'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Build') {
|
||||
steps {
|
||||
dir(BUILDDIR) {
|
||||
sh 'cmake --build . -j4'
|
||||
stage('Linux') {
|
||||
stages{
|
||||
stage('Clean') {
|
||||
steps {
|
||||
sh 'rm -rf $BUILDDIR_LINUX'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Unittests') {
|
||||
steps {
|
||||
dir(BUILDDIR) {
|
||||
sh 'cmake --build . -- fsfw-tests_coverage -j4'
|
||||
stage('Configure') {
|
||||
steps {
|
||||
dir(BUILDDIR_LINUX) {
|
||||
sh 'cmake -DFSFW_OSAL=linux -DFSFW_BUILD_TESTS=ON -DFSFW_CICD_BUILD=ON ..'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Valgrind') {
|
||||
steps {
|
||||
dir(BUILDDIR) {
|
||||
sh 'valgrind --leak-check=full --error-exitcode=1 ./fsfw-tests'
|
||||
stage('Build') {
|
||||
steps {
|
||||
dir(BUILDDIR_LINUX) {
|
||||
sh 'cmake --build . -j4'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Unittests') {
|
||||
steps {
|
||||
dir(BUILDDIR_LINUX) {
|
||||
sh 'cmake --build . -- fsfw-tests_coverage -j4'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Valgrind') {
|
||||
steps {
|
||||
dir(BUILDDIR_LINUX) {
|
||||
sh 'valgrind --leak-check=full --error-exitcode=1 ./fsfw-tests'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -52,14 +94,12 @@ pipeline {
|
||||
sh 'cmake -DFSFW_BUILD_DOCS=ON -DFSFW_OSAL=host ..'
|
||||
sh 'make Sphinx'
|
||||
sshagent(credentials: ['documentation-buildfix']) {
|
||||
sh 'ssh -o StrictHostKeyChecking=no buildfix@documentation.intra.irs.uni-stuttgart.de rm -rf /mnt/data/www/html/fsfw/development/*'
|
||||
sh 'scp -o StrictHostKeyChecking=no -r docs/sphinx/* buildfix@documentation.intra.irs.uni-stuttgart.de:/mnt/data/www/html/fsfw/development'
|
||||
sh 'rsync -r --delete docs/sphinx/* buildfix@documentation.irs.uni-stuttgart.de:/fsfw/development'
|
||||
}
|
||||
}
|
||||
dir(BUILDDIR) {
|
||||
dir(BUILDDIR_LINUX) {
|
||||
sshagent(credentials: ['documentation-buildfix']) {
|
||||
sh 'ssh -o StrictHostKeyChecking=no buildfix@documentation.intra.irs.uni-stuttgart.de rm -rf /mnt/data/www/html/fsfw/coverage/development/*'
|
||||
sh 'scp -o StrictHostKeyChecking=no -r fsfw-tests_coverage/* buildfix@documentation.intra.irs.uni-stuttgart.de:/mnt/data/www/html/fsfw/coverage/development'
|
||||
sh 'rsync -r --delete fsfw-tests_coverage/* buildfix@documentation.irs.uni-stuttgart.de:/fsfw/coverage/development'
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -73,14 +113,12 @@ pipeline {
|
||||
sh 'cmake -DFSFW_BUILD_DOCS=ON -DFSFW_OSAL=host ..'
|
||||
sh 'make Sphinx'
|
||||
sshagent(credentials: ['documentation-buildfix']) {
|
||||
sh 'ssh -o StrictHostKeyChecking=no buildfix@documentation.intra.irs.uni-stuttgart.de rm -rf /mnt/data/www/html/fsfw/master/*'
|
||||
sh 'scp -o StrictHostKeyChecking=no -r docs/sphinx/* buildfix@documentation.intra.irs.uni-stuttgart.de:/mnt/data/www/html/fsfw/master'
|
||||
sh 'rsync -r --delete docs/sphinx/* buildfix@documentation.irs.uni-stuttgart.de:/fsfw/master'
|
||||
}
|
||||
}
|
||||
dir(BUILDDIR) {
|
||||
dir(BUILDDIR_LINUX) {
|
||||
sshagent(credentials: ['documentation-buildfix']) {
|
||||
sh 'ssh -o StrictHostKeyChecking=no buildfix@documentation.intra.irs.uni-stuttgart.de rm -rf /mnt/data/www/html/fsfw/coverage/master/*'
|
||||
sh 'scp -o StrictHostKeyChecking=no -r fsfw-tests_coverage/* buildfix@documentation.intra.irs.uni-stuttgart.de:/mnt/data/www/html/fsfw/coverage/master'
|
||||
sh 'rsync -r --delete fsfw-tests_coverage/* buildfix@documentation.irs.uni-stuttgart.de:/fsfw/coverage/master'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ find_program( GCOV_PATH gcov )
|
||||
find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl)
|
||||
find_program( FASTCOV_PATH NAMES fastcov fastcov.py )
|
||||
find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat )
|
||||
find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
|
||||
find_program( GCOVR_PATH gcovr )
|
||||
find_program( CPPFILT_PATH NAMES c++filt )
|
||||
|
||||
if(NOT GCOV_PATH)
|
||||
|
@ -1,40 +0,0 @@
|
||||
Configuring the FSFW
|
||||
======
|
||||
|
||||
The FSFW can be configured via the `fsfwconfig` folder. A template folder has
|
||||
been provided 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
|
||||
[modgen Python scripts](https://git.ksat-stuttgart.de/source/modgen.git).
|
||||
3. The generated translation files for the object IDs should be named `translatesObjects.cpp`
|
||||
and `translateObjects.h` and should be copied to the `fsfwconfig/objects` folder
|
||||
4. The generated translation files for the event IDs should be named `translateEvents.cpp` and
|
||||
`translateEvents.h` and should be copied to the `fsfwconfig/events` folder
|
||||
|
||||
An example implementations of these translation file generators can be found as part
|
||||
of the [SOURCE project here](https://git.ksat-stuttgart.de/source/sourceobsw/-/tree/development/generators)
|
||||
or the [FSFW example](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_example_public/src/branch/master/generators)
|
||||
|
||||
## Configuring the Event Manager
|
||||
|
||||
The number of allowed subscriptions can be modified with the following
|
||||
parameters:
|
||||
|
||||
``` c++
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
@ -1 +0,0 @@
|
||||
## Controllers
|
@ -1,55 +0,0 @@
|
||||
## FSFW Core Modules
|
||||
|
||||
These 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
|
||||
|
||||
### ObjectManager
|
||||
|
||||
* Must be created during program startup
|
||||
* The component which handles all references. All SystemObjects register at this component.
|
||||
* Any SystemObject needs to have a unique ObjectId. Those can be managed like objects::framework_objects.
|
||||
* A reference to an object can be get by calling the following function. T must be the specific Interface you want to call.
|
||||
A nullptr check of the returning Pointer must be done. This function is based on Run-time type information.
|
||||
|
||||
```cpp
|
||||
template <typename T> T* ObjectManagerIF::get( object_id_t id )
|
||||
```
|
||||
* A typical way to create all objects on startup is a handing a static produce function to the
|
||||
ObjectManager on creation. By calling objectManager->initialize() the produce function will be
|
||||
called and all SystemObjects 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
|
||||
|
@ -1 +0,0 @@
|
||||
## Device Handlers
|
@ -1,135 +0,0 @@
|
||||
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 `ReturnValue_t` to signal to the caller that something has
|
||||
gone wrong. Returnvalues must be unique. For this the function `returnvalue::makeCode`
|
||||
or the macro `MAKE_RETURN` can be used. The `CLASS_ID` is a unique id for that type of object.
|
||||
See `returnvalues/FwClassIds` folder. 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 [OSAL README](doc/README-osal.md#top) 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
|
||||
[core component section](doc/README-core.md#top):
|
||||
|
||||
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.
|
||||
3. Clock: This module provided common time related functions
|
||||
4. EventManager: This module allows routing of events generated by `SystemObjects`
|
||||
5. 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 `defaultcft/fsfwconfig/objects`
|
||||
inside the function `Factory::setStaticFrameworkObjectIds()`.
|
||||
|
||||
# Events
|
||||
|
||||
Events are tied to objects. EventIds can be generated by calling the Macro MAKE_EVENT.
|
||||
This works analog to the returnvalues. Every object that needs own EventIds has to get a
|
||||
unique SUBSYSTEM_ID. Every SystemObject can call triggerEvent from the parent class.
|
||||
Therefore, event messages contain the specific EventId and the objectId of the object that
|
||||
has triggered.
|
||||
|
||||
# Internal Communication
|
||||
|
||||
Components communicate mostly via Messages through Queues.
|
||||
Those queues are created by calling the singleton `QueueFactory::instance()->create()` which
|
||||
will create `MessageQueue` instances for the used OSAL.
|
||||
|
||||
# External Communication
|
||||
|
||||
The external communication with the mission control system is mostly up to the user implementation.
|
||||
The FSFW provides PUS Services which can be used to but don't need to be used.
|
||||
The services can be seen as a conversion from a TC to a message based communication and back.
|
||||
|
||||
## TMTC Communication
|
||||
|
||||
The FSFW provides some components to facilitate TMTC handling via the PUS commands.
|
||||
For example, a UDP or TCP PUS server socket can be opened on a specific port using the
|
||||
files located in `osal/common`. The FSFW example uses this functionality to allow sending telecommands
|
||||
and receiving telemetry using the [TMTC commander application](https://github.com/spacefisch/tmtccmd).
|
||||
Simple commands like the PUS Service 17 ping service can be tested by simply running the
|
||||
`tmtc_client_cli.py` or `tmtc_client_gui.py` utility in
|
||||
the [example tmtc folder](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_example_public/src/branch/master/tmtc)
|
||||
while the `fsfw_example` application is running.
|
||||
|
||||
More generally, any class responsible for handling incoming telecommands and sending telemetry
|
||||
can implement the generic `TmTcBridge` class located in `tmtcservices`. Many applications
|
||||
also use a dedicated polling task for reading telecommands which passes telecommands
|
||||
to the `TmTcBridge` implementation.
|
||||
|
||||
## CCSDS Frames, CCSDS Space Packets and PUS
|
||||
|
||||
If the communication is based on CCSDS Frames and Space Packets, several classes can be used to
|
||||
distributed the packets to the corresponding services. Those can be found in `tcdistribution`.
|
||||
If Space Packets are used, a timestamper has to be provided by the user.
|
||||
An example can be found in the `timemanager` folder, which uses `CCSDSTime::CDS_short`.
|
||||
|
||||
# Device Handlers
|
||||
|
||||
DeviceHandlers are another important component of the FSFW.
|
||||
The idea is, to have a software counterpart of every physical device to provide a simple mode,
|
||||
health and commanding interface. By separating the underlying Communication Interface with
|
||||
`DeviceCommunicationIF`, a device handler (DH) can be tested on different hardware.
|
||||
The DH has mechanisms to monitor the communication with the physical device which allow
|
||||
for FDIR reaction. Device Handlers can be created by implementing `DeviceHandlerBase`.
|
||||
A standard FDIR component for the DH will be created automatically but can
|
||||
be overwritten by the user. More information on DeviceHandlers can be found in the
|
||||
related [documentation section](doc/README-devicehandlers.md#top).
|
||||
|
||||
# Modes and Health
|
||||
|
||||
The two interfaces `HasModesIF` and `HasHealthIF` provide access for commanding and monitoring
|
||||
of components. On-board Mode Management is implement in hierarchy system.
|
||||
DeviceHandlers 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 next level which
|
||||
are the Subsystems.
|
||||
|
||||
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.
|
||||
|
||||
# Unit Tests
|
||||
|
||||
Unit Tests are provided in the unittest folder. Those use the catch2 framework but do not include
|
||||
catch2 itself. More information on how to run these tests can be found in the separate
|
||||
[`fsfw_tests` reposoitory](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_tests)
|
@ -1,174 +0,0 @@
|
||||
## Local Data Pools Developer Information
|
||||
|
||||
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:: ../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
|
||||
from the SOURCE CubeSat project:
|
||||
|
||||
```cpp
|
||||
class GyroPrimaryDataset: public StaticLocalDataSet<3 * sizeof(float)> {
|
||||
public:
|
||||
/**
|
||||
* Constructor for data users
|
||||
* @param gyroId
|
||||
*/
|
||||
GyroPrimaryDataset(object_id_t gyroId):
|
||||
StaticLocalDataSet(sid_t(gyroId, gyrodefs::GYRO_DATA_SET_ID)) {
|
||||
setAllVariablesReadOnly();
|
||||
}
|
||||
|
||||
lp_var_t<float> angVelocityX = lp_var_t<float>(sid.objectId,
|
||||
gyrodefs::ANGULAR_VELOCITY_X, this);
|
||||
lp_var_t<float> angVelocityY = lp_var_t<float>(sid.objectId,
|
||||
gyrodefs::ANGULAR_VELOCITY_Y, this);
|
||||
lp_var_t<float> angVelocityZ = lp_var_t<float>(sid.objectId,
|
||||
gyrodefs::ANGULAR_VELOCITY_Z, this);
|
||||
private:
|
||||
|
||||
friend class GyroHandler;
|
||||
/**
|
||||
* Constructor for data creator
|
||||
* @param hkOwner
|
||||
*/
|
||||
GyroPrimaryDataset(HasLocalDataPoolIF* hkOwner):
|
||||
StaticLocalDataSet(hkOwner, gyrodefs::GYRO_DATA_SET_ID) {}
|
||||
};
|
||||
```
|
||||
|
||||
There is a public constructor for users which sets all variables to read-only and there is a
|
||||
constructor for the GyroHandler data creator by marking it private and declaring the `GyroHandler`
|
||||
as a friend class. Both the atittude controller and the `GyroHandler` can now
|
||||
use the same class definition to access the pool variables with `read` and `commit` semantics
|
||||
in a thread-safe way. Generally, each class requiring access will have the set class as a member
|
||||
class. The data creator will also be generally a `DeviceHandlerBase` subclass and some additional
|
||||
steps are necessary to expose the set for housekeeping purposes.
|
||||
|
||||
### Using the local data pools in a `DeviceHandlerBase` subclass
|
||||
|
||||
It is very common to store data generated by devices like a sensor into a pool which can
|
||||
then be used by other objects. Therefore, the `DeviceHandlerBase` already has a
|
||||
local pool. Using the aforementioned example, our `GyroHandler` will now have the set class
|
||||
as a member:
|
||||
|
||||
```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:
|
||||
|
||||
```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:
|
||||
|
||||
```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.
|
||||
|
||||
```cpp
|
||||
ReturnValue_t GyroHandler::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
||||
LocalDataPoolManager &poolManager) {
|
||||
localDataPoolMap.emplace(gyrodefs::ANGULAR_VELOCITY_X,
|
||||
new PoolEntry<float>({0.0}));
|
||||
localDataPoolMap.emplace(gyrodefs::ANGULAR_VELOCITY_Y,
|
||||
new PoolEntry<float>({0.0}));
|
||||
localDataPoolMap.emplace(gyrodefs::ANGULAR_VELOCITY_Z,
|
||||
new PoolEntry<float>({0.0}));
|
||||
localDataPoolMap.emplace(gyrodefs::GENERAL_CONFIG_REG42,
|
||||
new PoolEntry<uint8_t>({0}));
|
||||
localDataPoolMap.emplace(gyrodefs::RANGE_CONFIG_REG43,
|
||||
new PoolEntry<uint8_t>({0}));
|
||||
|
||||
poolManager.subscribeForPeriodicPacket(gyroData.getSid(), false, 4.0, false);
|
||||
return returnvalue::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:
|
||||
|
||||
```cpp
|
||||
PoolReadGuard readHelper(&gyroData);
|
||||
if(readHelper.getReadResult() == returnvalue::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
|
||||
|
||||
|
@ -1,32 +0,0 @@
|
||||
# Operating System Abstraction Layer (OSAL)
|
||||
|
||||
Some specific information on the provided OSALs are provided.
|
||||
|
||||
## Linux OSAL
|
||||
|
||||
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.
|
||||
|
||||
More information on how to set up a Linux system accordingly can be found in the
|
||||
[Linux README of the FSFW example application](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_example/src/branch/master/doc/README-linux.md#top)
|
||||
|
||||
## 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.
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
## PUS Services
|
@ -50,6 +50,11 @@ exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
||||
#
|
||||
html_theme = "alabaster"
|
||||
|
||||
html_theme_options = {
|
||||
"extra_nav_links": {"Impressum" : "https://www.uni-stuttgart.de/impressum", "Datenschutz": "https://info.irs.uni-stuttgart.de/datenschutz/datenschutzWebmit.html"}
|
||||
}
|
||||
|
||||
|
||||
# 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".
|
||||
|
@ -6,15 +6,14 @@ High-level overview
|
||||
Structure
|
||||
----------
|
||||
|
||||
The general structure is driven by the usage of interfaces provided by objects.
|
||||
The FSFW uses C++17 as baseline.
|
||||
It also 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 will not throw exceptions.
|
||||
The general structure is driven by the usage of interfaces provided by objects.
|
||||
The FSFW uses C++17 as baseline. Most modern compilers like GCC should have support for this
|
||||
standard, even for micocontrollers.
|
||||
|
||||
The FSFW might use dynamic allocation during program initialization but not during runtime.
|
||||
It offers pool objects, static containers and it also exposes the
|
||||
`Embedded Template Library <https://www.etlcpp.com/>`_ to allow writing code which does not perform
|
||||
allocation during runtime. The fsfw uses run-time type information but will not throw exceptions.
|
||||
|
||||
Failure Handling
|
||||
-----------------
|
||||
|
110
scripts/check_release.py
Executable file
110
scripts/check_release.py
Executable file
@ -0,0 +1,110 @@
|
||||
#! /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)
|
||||
args = parser.parse_args()
|
||||
|
||||
match = re.search("([0-9]+\.[0-9]+\.[0-9]+)", args.version)
|
||||
|
||||
if not match:
|
||||
print("invalid version")
|
||||
exit(1)
|
||||
|
||||
version = "v" + match.group(1)
|
||||
|
||||
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)
|
||||
|
||||
milestone_prs = []
|
||||
|
||||
page = 1
|
||||
last_count = 1;
|
||||
while last_count != 0:
|
||||
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']})
|
||||
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()
|
@ -207,7 +207,7 @@ def check_for_cmake_build_dir(build_dir_list: list) -> list:
|
||||
def perform_lcov_operation(directory: str, chdir: bool):
|
||||
if chdir:
|
||||
os.chdir(directory)
|
||||
cmd_runner("cmake --build . -- fsfw-tests_coverage -j")
|
||||
cmd_runner("cmake --build . -j -- fsfw-tests_coverage")
|
||||
|
||||
|
||||
def determine_build_dir(build_dir_list: List[str]):
|
||||
|
@ -8,4 +8,4 @@ if(FSFW_ADD_HAL)
|
||||
add_subdirectory(fsfw_hal)
|
||||
endif()
|
||||
|
||||
add_subdirectory(fsfw_tests)
|
||||
#add_subdirectory(fsfw_tests)
|
||||
|
@ -7,7 +7,7 @@ add_subdirectory(cfdp)
|
||||
add_subdirectory(container)
|
||||
add_subdirectory(controller)
|
||||
add_subdirectory(datapool)
|
||||
add_subdirectory(datapoollocal)
|
||||
#add_subdirectory(datapoollocal)
|
||||
add_subdirectory(devicehandlers)
|
||||
add_subdirectory(events)
|
||||
add_subdirectory(fdir)
|
||||
@ -15,20 +15,22 @@ add_subdirectory(globalfunctions)
|
||||
add_subdirectory(health)
|
||||
add_subdirectory(housekeeping)
|
||||
add_subdirectory(internalerror)
|
||||
add_subdirectory(introspection)
|
||||
add_subdirectory(ipc)
|
||||
add_subdirectory(memory)
|
||||
add_subdirectory(modes)
|
||||
add_subdirectory(objectmanager)
|
||||
add_subdirectory(parameters)
|
||||
add_subdirectory(power)
|
||||
#add_subdirectory(power)
|
||||
add_subdirectory(serialize)
|
||||
add_subdirectory(serviceinterface)
|
||||
add_subdirectory(storagemanager)
|
||||
add_subdirectory(subsystem)
|
||||
add_subdirectory(tasks)
|
||||
add_subdirectory(tcdistribution)
|
||||
add_subdirectory(thermal)
|
||||
#add_subdirectory(thermal)
|
||||
add_subdirectory(timemanager)
|
||||
add_subdirectory(tmtc)
|
||||
add_subdirectory(tmtcpacket)
|
||||
add_subdirectory(tmtcservices)
|
||||
add_subdirectory(filesystem)
|
||||
@ -36,7 +38,7 @@ add_subdirectory(filesystem)
|
||||
# Optional
|
||||
|
||||
if(FSFW_ADD_MONITORING)
|
||||
add_subdirectory(monitoring)
|
||||
#add_subdirectory(monitoring)
|
||||
endif()
|
||||
if(FSFW_ADD_PUS)
|
||||
add_subdirectory(pus)
|
||||
|
@ -1,6 +1,7 @@
|
||||
#ifndef FSFW_INC_FSFW_ACTION_H_
|
||||
#define FSFW_INC_FSFW_ACTION_H_
|
||||
|
||||
#include "fsfw/action/Action.h"
|
||||
#include "fsfw/action/ActionHelper.h"
|
||||
#include "fsfw/action/ActionMessage.h"
|
||||
#include "fsfw/action/CommandActionHelper.h"
|
||||
|
70
src/fsfw/action/Action.cpp
Normal file
70
src/fsfw/action/Action.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
#include "Action.h"
|
||||
|
||||
#include <fsfw/serialize/SerializeAdapter.h>
|
||||
#include <fsfw/ipc/MessageQueueIF.h>
|
||||
|
||||
#undef Action
|
||||
|
||||
#ifdef FSFW_INTROSPECTION
|
||||
|
||||
Action::Action() {}
|
||||
|
||||
void Action::setEnum(EnumIF *theEnum) {
|
||||
id = theEnum->getValue();
|
||||
name = theEnum->getDescription();
|
||||
}
|
||||
|
||||
const char *Action::getName() { return name; }
|
||||
#else
|
||||
Action::Action(ActionId_t id) : id(id) {}
|
||||
#endif
|
||||
ActionId_t Action::getId() { return id; }
|
||||
|
||||
void Action::clear() {
|
||||
tc = store_address_t();
|
||||
tcOffset = 0;
|
||||
commandedBy = MessageQueueIF::NO_QUEUE;
|
||||
}
|
||||
|
||||
void Action::registerParameter(ParameterIF *parameter) { parameterList.push_back(parameter); }
|
||||
|
||||
std::vector<ParameterIF *> const *Action::getParameters() const { return ¶meterList; }
|
||||
|
||||
size_t Action::getSerializedSize() const {
|
||||
size_t size = SerializeAdapter::getSerializedSize(&id);
|
||||
for (auto parameter : *getParameters()) {
|
||||
size += parameter->getSerializedSize();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
ReturnValue_t Action::serialize(uint8_t **buffer, size_t *size, size_t maxSize,
|
||||
Endianness streamEndianness) const {
|
||||
ReturnValue_t result = SerializeAdapter::serialize(&id, buffer, size, maxSize, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
for (auto parameter : *getParameters()) {
|
||||
result = parameter->serialize(buffer, size, maxSize, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t Action::deSerialize(const uint8_t **buffer, size_t *size,
|
||||
Endianness streamEndianness) {
|
||||
ReturnValue_t result = returnvalue::OK;/* TODO not needed as must have been read before to find this
|
||||
action = SerializeAdapter::deSerialize(&id, buffer, size, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}*/
|
||||
for (auto parameter : *getParameters()) {
|
||||
result = parameter->deSerialize(buffer, size, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
64
src/fsfw/action/Action.h
Normal file
64
src/fsfw/action/Action.h
Normal file
@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <fsfw/introspection/HasTmTcParametersIF.h>
|
||||
#include <fsfw/serialize/SerializeIF.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ActionMessage.h"
|
||||
|
||||
#ifdef FSFW_INTROSPECTION
|
||||
#include "../introspection/Enum.h"
|
||||
#endif
|
||||
|
||||
// TODO ActionId_t
|
||||
|
||||
class Action : public SerializeIF, public HasTmTcParametersIF {
|
||||
public:
|
||||
#ifdef FSFW_INTROSPECTION
|
||||
Action();
|
||||
void setEnum(EnumIF *id);
|
||||
const char *getName();
|
||||
#else
|
||||
Action(ActionId_t id);
|
||||
#endif
|
||||
|
||||
store_address_t getTc();
|
||||
size_t getTcOffset();
|
||||
|
||||
// if an action is triggered by a TC, this should be set to be able to handle replies
|
||||
// TODO make deleting it safe
|
||||
// TODO integrate with internal commands
|
||||
store_address_t tc;
|
||||
size_t tcOffset;
|
||||
MessageQueueId_t commandedBy;
|
||||
|
||||
ActionId_t getId();
|
||||
|
||||
|
||||
|
||||
void clear();
|
||||
|
||||
[[nodiscard]] virtual ReturnValue_t handle() = 0;
|
||||
|
||||
void registerParameter(ParameterIF *parameter) override;
|
||||
|
||||
std::vector<ParameterIF *> const *getParameters() const override;
|
||||
|
||||
ReturnValue_t serialize(uint8_t **buffer, size_t *size, size_t maxSize,
|
||||
Endianness streamEndianness) const override;
|
||||
|
||||
size_t getSerializedSize() const override;
|
||||
|
||||
ReturnValue_t deSerialize(const uint8_t **buffer, size_t *size,
|
||||
Endianness streamEndianness) override;
|
||||
|
||||
private:
|
||||
ActionId_t id;
|
||||
|
||||
#ifdef FSFW_INTROSPECTION
|
||||
const char *name;
|
||||
#endif
|
||||
std::vector<ParameterIF *> parameterList;
|
||||
};
|
@ -1,3 +1,6 @@
|
||||
#include <fsfw/serialize/SerializeAdapter.h>
|
||||
#include <fsfw/tmtc/TmMessage.h>
|
||||
|
||||
#include "fsfw/action.h"
|
||||
#include "fsfw/ipc/MessageQueueSenderIF.h"
|
||||
#include "fsfw/objectmanager/ObjectManager.h"
|
||||
@ -10,8 +13,8 @@ ActionHelper::~ActionHelper() = default;
|
||||
|
||||
ReturnValue_t ActionHelper::handleActionMessage(CommandMessage* command) {
|
||||
if (command->getCommand() == ActionMessage::EXECUTE_ACTION) {
|
||||
ActionId_t currentAction = ActionMessage::getActionId(command);
|
||||
prepareExecution(command->getSender(), currentAction, ActionMessage::getStoreId(command));
|
||||
prepareExecution(command->getSender(), ActionMessage::getOffset(command),
|
||||
ActionMessage::getStoreId(command));
|
||||
return returnvalue::OK;
|
||||
} else {
|
||||
return CommandMessage::UNKNOWN_COMMAND;
|
||||
@ -23,6 +26,12 @@ ReturnValue_t ActionHelper::initialize(MessageQueueIF* queueToUse_) {
|
||||
if (ipcStore == nullptr) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
tmManager = ObjectManager::instance()->get<TmManager>(objects::TM_MANAGER);
|
||||
if (tmManager == nullptr) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
if (queueToUse_ != nullptr) {
|
||||
setQueueToUse(queueToUse_);
|
||||
}
|
||||
@ -57,28 +66,89 @@ void ActionHelper::finish(bool success, MessageQueueId_t reportTo, ActionId_t co
|
||||
|
||||
void ActionHelper::setQueueToUse(MessageQueueIF* queue) { queueToUse = queue; }
|
||||
|
||||
void ActionHelper::prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId,
|
||||
MessageQueueIF const* ActionHelper::getQueue() const { return queueToUse; }
|
||||
|
||||
void ActionHelper::prepareExecution(MessageQueueId_t commandedBy, size_t offset,
|
||||
store_address_t dataAddress) {
|
||||
const uint8_t* dataPtr = nullptr;
|
||||
size_t size = 0;
|
||||
ReturnValue_t result = ipcStore->getData(dataAddress, &dataPtr, &size);
|
||||
const uint8_t* tcData = nullptr;
|
||||
size_t tcDataSize = 0;
|
||||
ReturnValue_t result = ipcStore->getData(dataAddress, &tcData, &tcDataSize);
|
||||
if (result != returnvalue::OK) {
|
||||
CommandMessage reply;
|
||||
ActionMessage::setStepReply(&reply, actionId, 0, result);
|
||||
queueToUse->sendMessage(commandedBy, &reply);
|
||||
ActionMessage::setStepReply(&reply, 0 /*TODO*/, 0, result);
|
||||
// queueToUse->sendMessage(commandedBy, &reply);
|
||||
ipcStore->deleteData(dataAddress);
|
||||
return;
|
||||
}
|
||||
result = owner->executeAction(actionId, commandedBy, dataPtr, size);
|
||||
ipcStore->deleteData(dataAddress);
|
||||
|
||||
FsfwProtocolHeader header;
|
||||
|
||||
const uint8_t* dataPtr = tcData + offset;
|
||||
size_t size = tcDataSize - offset;
|
||||
|
||||
result = header.deSerialize(&dataPtr, &size, SerializeIF::Endianness::NETWORK);
|
||||
|
||||
if (header.getInterface() != HasActionsIF::INTERFACE_ID or
|
||||
header.getFunction() != HasActionsIF::Functions::EXECUTE_ACTION) {
|
||||
CommandMessage reply;
|
||||
ActionMessage::setStepReply(&reply, 0 /*TODO*/, 0, result);
|
||||
// queueToUse->sendMessage(commandedBy, &reply);
|
||||
ipcStore->deleteData(dataAddress);
|
||||
return;
|
||||
}
|
||||
|
||||
ActionId_t actionId;
|
||||
|
||||
result =
|
||||
SerializeAdapter::deSerialize(&actionId, &dataPtr, &size, SerializeIF::Endianness::NETWORK);
|
||||
if (result != returnvalue::OK) {
|
||||
CommandMessage reply;
|
||||
ActionMessage::setStepReply(&reply, 0 /*TODO*/, 0, result);
|
||||
// queueToUse->sendMessage(commandedBy, &reply);
|
||||
ipcStore->deleteData(dataAddress);
|
||||
return;
|
||||
}
|
||||
|
||||
auto actionIter = actionMap.find(actionId);
|
||||
if (actionIter == actionMap.end()) {
|
||||
CommandMessage reply;
|
||||
ActionMessage::setStepReply(&reply, actionId, 0, HasActionsIF::INVALID_ACTION_ID);
|
||||
// queueToUse->sendMessage(commandedBy, &reply);
|
||||
ipcStore->deleteData(dataAddress);
|
||||
return;
|
||||
}
|
||||
Action* action = actionIter->second;
|
||||
|
||||
result = action->deSerialize(&dataPtr, &size, SerializeIF::Endianness::NETWORK);
|
||||
if ((result != returnvalue::OK) || (size != 0)) { // TODO write unittest for second condition
|
||||
CommandMessage reply;
|
||||
ActionMessage::setStepReply(&reply, actionId, 0, HasActionsIF::INVALID_PARAMETERS);
|
||||
// queueToUse->sendMessage(commandedBy, &reply);
|
||||
ipcStore->deleteData(dataAddress);
|
||||
return;
|
||||
}
|
||||
// TODO call action->check()
|
||||
action->commandedBy = commandedBy;
|
||||
result = owner->executeAction(action);
|
||||
|
||||
// TODO safetify dynamic cast
|
||||
result = tmManager->sendTmPacket(dynamic_cast<SystemObjectIF*>(owner)->getObjectId(), HasActionsIF::INTERFACE_ID,
|
||||
HasActionsIF::Functions::EXECUTION_IN_PROGRESS, action,
|
||||
dataAddress, offset);
|
||||
if (result != returnvalue::OK) {
|
||||
sif::error << "replying action failed " << std::hex << result << std::dec << std::endl;
|
||||
}
|
||||
|
||||
// ipcStore->deleteData(dataAddress);
|
||||
if (result == HasActionsIF::EXECUTION_FINISHED) {
|
||||
CommandMessage reply;
|
||||
ActionMessage::setCompletionReply(&reply, actionId, true, result);
|
||||
queueToUse->sendMessage(commandedBy, &reply);
|
||||
// queueToUse->sendMessage(commandedBy, &reply);
|
||||
}
|
||||
if (result != returnvalue::OK) {
|
||||
CommandMessage reply;
|
||||
ActionMessage::setStepReply(&reply, actionId, 0, result);
|
||||
queueToUse->sendMessage(commandedBy, &reply);
|
||||
// queueToUse->sendMessage(commandedBy, &reply);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -163,3 +233,11 @@ ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, ActionId_t rep
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ActionHelper::registerAction(Action* action) {
|
||||
// TODO error handling
|
||||
ActionId_t id = action->getId();
|
||||
actionMap.emplace(id, action);
|
||||
}
|
||||
|
||||
std::map<ActionId_t, Action*> const* ActionHelper::getActionMap() { return &actionMap; }
|
@ -1,9 +1,12 @@
|
||||
#ifndef FSFW_ACTION_ACTIONHELPER_H_
|
||||
#define FSFW_ACTION_ACTIONHELPER_H_
|
||||
|
||||
#include <fsfw/tmtc/TmManager.h>
|
||||
|
||||
#include "ActionMessage.h"
|
||||
#include "fsfw/ipc/MessageQueueIF.h"
|
||||
#include "fsfw/serialize/SerializeIF.h"
|
||||
|
||||
/**
|
||||
* @brief Action Helper is a helper class which handles action messages
|
||||
*
|
||||
@ -98,6 +101,16 @@ class ActionHelper {
|
||||
*/
|
||||
void setQueueToUse(MessageQueueIF* queue);
|
||||
|
||||
/**
|
||||
* Needed so templateAction can check if actionHelper was
|
||||
* contructed already to aid in debuggig a nasty coding error.
|
||||
*/
|
||||
MessageQueueIF const* getQueue() const;
|
||||
|
||||
void registerAction(Action* action);
|
||||
|
||||
std::map<ActionId_t, Action*> const* getActionMap();
|
||||
|
||||
protected:
|
||||
//! Increase of value of this per step
|
||||
static const uint8_t STEP_OFFSET = 1;
|
||||
@ -106,8 +119,11 @@ class ActionHelper {
|
||||
//! Queue to be used as response sender, has to be set in ctor or with
|
||||
//! setQueueToUse
|
||||
MessageQueueIF* queueToUse;
|
||||
TmManager* tmManager = nullptr;
|
||||
//! Pointer to an IPC Store, initialized during construction or
|
||||
StorageManagerIF* ipcStore = nullptr;
|
||||
//! Map of all implemented Actions
|
||||
std::map<ActionId_t, Action*> actionMap;
|
||||
|
||||
/**
|
||||
* Internal function called by handleActionMessage
|
||||
@ -115,7 +131,7 @@ class ActionHelper {
|
||||
* @param actionId ID of action to be done
|
||||
* @param dataAddress Address of additional data in IPC Store
|
||||
*/
|
||||
virtual void prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId,
|
||||
virtual void prepareExecution(MessageQueueId_t commandedBy, size_t offset,
|
||||
store_address_t dataAddress);
|
||||
/**
|
||||
* @brief Default implementation is empty.
|
||||
|
@ -6,14 +6,14 @@ ActionMessage::ActionMessage() = default;
|
||||
|
||||
ActionMessage::~ActionMessage() = default;
|
||||
|
||||
void ActionMessage::setCommand(CommandMessage* message, ActionId_t fid,
|
||||
void ActionMessage::setCommand(CommandMessage* message, size_t offset,
|
||||
store_address_t parameters) {
|
||||
message->setCommand(EXECUTE_ACTION);
|
||||
message->setParameter(fid);
|
||||
message->setParameter(offset);
|
||||
message->setParameter2(parameters.raw);
|
||||
}
|
||||
|
||||
ActionId_t ActionMessage::getActionId(const CommandMessage* message) {
|
||||
size_t ActionMessage::getOffset(const CommandMessage* message) {
|
||||
return ActionId_t(message->getParameter());
|
||||
}
|
||||
|
||||
|
@ -27,9 +27,9 @@ class ActionMessage {
|
||||
static const Command_t COMPLETION_FAILED = MAKE_COMMAND_ID(6);
|
||||
|
||||
virtual ~ActionMessage();
|
||||
static void setCommand(CommandMessage* message, ActionId_t fid, store_address_t parameters);
|
||||
static void setCommand(CommandMessage* message, size_t offset, store_address_t parameters);
|
||||
|
||||
static ActionId_t getActionId(const CommandMessage* message);
|
||||
static size_t getOffset(const CommandMessage* message);
|
||||
static store_address_t getStoreId(const CommandMessage* message);
|
||||
|
||||
static void setStepReply(CommandMessage* message, ActionId_t fid, uint8_t step,
|
||||
|
@ -1,3 +1,3 @@
|
||||
target_sources(
|
||||
${LIB_FSFW_NAME} PRIVATE ActionHelper.cpp ActionMessage.cpp
|
||||
${LIB_FSFW_NAME} PRIVATE Action.cpp ActionHelper.cpp ActionMessage.cpp
|
||||
CommandActionHelper.cpp SimpleActionHelper.cpp)
|
||||
|
@ -74,24 +74,24 @@ ReturnValue_t CommandActionHelper::handleReply(CommandMessage *reply) {
|
||||
switch (reply->getCommand()) {
|
||||
case ActionMessage::COMPLETION_SUCCESS:
|
||||
commandCount--;
|
||||
owner->completionSuccessfulReceived(ActionMessage::getActionId(reply));
|
||||
// owner->completionSuccessfulReceived(ActionMessage::getActionId(reply)); TODO
|
||||
return returnvalue::OK;
|
||||
case ActionMessage::COMPLETION_FAILED:
|
||||
commandCount--;
|
||||
owner->completionFailedReceived(ActionMessage::getActionId(reply),
|
||||
ActionMessage::getReturnCode(reply));
|
||||
// owner->completionFailedReceived(ActionMessage::getActionId(reply),
|
||||
// ActionMessage::getReturnCode(reply));
|
||||
return returnvalue::OK;
|
||||
case ActionMessage::STEP_SUCCESS:
|
||||
owner->stepSuccessfulReceived(ActionMessage::getActionId(reply),
|
||||
ActionMessage::getStep(reply));
|
||||
// owner->stepSuccessfulReceived(ActionMessage::getActionId(reply),
|
||||
// ActionMessage::getStep(reply));
|
||||
return returnvalue::OK;
|
||||
case ActionMessage::STEP_FAILED:
|
||||
commandCount--;
|
||||
owner->stepFailedReceived(ActionMessage::getActionId(reply), ActionMessage::getStep(reply),
|
||||
ActionMessage::getReturnCode(reply));
|
||||
// owner->stepFailedReceived(ActionMessage::getActionId(reply), ActionMessage::getStep(reply),
|
||||
// ActionMessage::getReturnCode(reply));
|
||||
return returnvalue::OK;
|
||||
case ActionMessage::DATA_REPLY:
|
||||
extractDataForOwner(ActionMessage::getActionId(reply), ActionMessage::getStoreId(reply));
|
||||
// extractDataForOwner(ActionMessage::getActionId(reply), ActionMessage::getStoreId(reply));
|
||||
return returnvalue::OK;
|
||||
default:
|
||||
return returnvalue::FAILED;
|
||||
|
@ -1,11 +1,13 @@
|
||||
#ifndef FSFW_ACTION_HASACTIONSIF_H_
|
||||
#define FSFW_ACTION_HASACTIONSIF_H_
|
||||
|
||||
#include "Action.h"
|
||||
#include "ActionHelper.h"
|
||||
#include "ActionMessage.h"
|
||||
#include "SimpleActionHelper.h"
|
||||
#include "fsfw/ipc/MessageQueueIF.h"
|
||||
#include "fsfw/returnvalues/returnvalue.h"
|
||||
#include <fsfw/tmtc/FsfwProtocolHeader.h>
|
||||
|
||||
/**
|
||||
* @brief
|
||||
@ -40,12 +42,18 @@ class HasActionsIF {
|
||||
static const ReturnValue_t INVALID_PARAMETERS = MAKE_RETURN_CODE(2);
|
||||
static const ReturnValue_t EXECUTION_FINISHED = MAKE_RETURN_CODE(3);
|
||||
static const ReturnValue_t INVALID_ACTION_ID = MAKE_RETURN_CODE(4);
|
||||
|
||||
enum Functions : FsfwProtocolHeader::FunctionType_t { EXECUTE_ACTION, EXECUTION_IN_PROGRESS };
|
||||
|
||||
virtual ~HasActionsIF() = default;
|
||||
/**
|
||||
* Function to get the MessageQueueId_t of the implementing object
|
||||
* @return MessageQueueId_t of the object
|
||||
*
|
||||
*/
|
||||
[[nodiscard]] virtual MessageQueueId_t getCommandQueue() const = 0;
|
||||
|
||||
virtual ActionHelper* getActionHelper() = 0;
|
||||
/**
|
||||
* Execute or initialize the execution of a certain function.
|
||||
* The ActionHelpers will execute this function and behave differently
|
||||
@ -55,8 +63,7 @@ class HasActionsIF {
|
||||
* -@c EXECUTION_FINISHED Finish reply will be generated
|
||||
* -@c Not returnvalue::OK Step failure reply will be generated
|
||||
*/
|
||||
virtual ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
|
||||
const uint8_t* data, size_t size) = 0;
|
||||
virtual ReturnValue_t executeAction(Action* action) = 0;
|
||||
};
|
||||
|
||||
#endif /* FSFW_ACTION_HASACTIONSIF_H_ */
|
||||
|
@ -1,4 +1,6 @@
|
||||
#include "fsfw/action.h"
|
||||
#include <fsfw/serialize/SerializeAdapter.h>
|
||||
|
||||
|
||||
SimpleActionHelper::SimpleActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue)
|
||||
: ActionHelper(setOwner, useThisQueue), isExecuting(false) {}
|
||||
@ -30,25 +32,57 @@ void SimpleActionHelper::resetHelper() {
|
||||
lastCommander = 0;
|
||||
}
|
||||
|
||||
void SimpleActionHelper::prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId,
|
||||
store_address_t dataAddress) {
|
||||
void SimpleActionHelper::prepareExecution(MessageQueueId_t commandedBy, size_t offset,
|
||||
store_address_t dataAddress) {
|
||||
CommandMessage reply;
|
||||
if (isExecuting) {
|
||||
ipcStore->deleteData(dataAddress);
|
||||
ActionMessage::setStepReply(&reply, actionId, 0, HasActionsIF::IS_BUSY);
|
||||
ActionMessage::setStepReply(&reply, 0 /*TODO*/, 0, HasActionsIF::IS_BUSY);
|
||||
queueToUse->sendMessage(commandedBy, &reply);
|
||||
}
|
||||
const uint8_t* dataPtr = nullptr;
|
||||
size_t size = 0;
|
||||
ReturnValue_t result = ipcStore->getData(dataAddress, &dataPtr, &size);
|
||||
if (result != returnvalue::OK) {
|
||||
ActionMessage::setStepReply(&reply, actionId, 0, result);
|
||||
ActionMessage::setStepReply(&reply, 0 /*TODO*/, 0, result);
|
||||
queueToUse->sendMessage(commandedBy, &reply);
|
||||
ipcStore->deleteData(dataAddress);
|
||||
return;
|
||||
}
|
||||
lastCommander = commandedBy;
|
||||
lastAction = actionId;
|
||||
result = owner->executeAction(actionId, commandedBy, dataPtr, size);
|
||||
|
||||
|
||||
dataPtr += offset;
|
||||
size -=offset;
|
||||
|
||||
ActionId_t actionId;
|
||||
|
||||
result = SerializeAdapter::deSerialize(&actionId, &dataPtr, &size, SerializeIF::Endianness::NETWORK);
|
||||
if (result != returnvalue::OK) {
|
||||
CommandMessage reply;
|
||||
ActionMessage::setStepReply(&reply, actionId, 0, result);
|
||||
queueToUse->sendMessage(commandedBy, &reply);
|
||||
ipcStore->deleteData(dataAddress);
|
||||
return;
|
||||
}
|
||||
|
||||
auto actionIter = actionMap.find(actionId);
|
||||
if (actionIter == actionMap.end()){
|
||||
CommandMessage reply;
|
||||
ActionMessage::setStepReply(&reply, actionId, 0, HasActionsIF::INVALID_ACTION_ID);
|
||||
queueToUse->sendMessage(commandedBy, &reply);
|
||||
ipcStore->deleteData(dataAddress);
|
||||
return;
|
||||
}
|
||||
Action* action = actionIter->second;
|
||||
result = action->deSerialize(&dataPtr, &size, SerializeIF::Endianness::NETWORK);
|
||||
if ((result != returnvalue::OK) or (size != 0)) {
|
||||
CommandMessage reply;
|
||||
ActionMessage::setStepReply(&reply, actionId, 0, HasActionsIF::INVALID_PARAMETERS);
|
||||
queueToUse->sendMessage(commandedBy, &reply);
|
||||
ipcStore->deleteData(dataAddress);
|
||||
return;
|
||||
}
|
||||
result = action->handle();
|
||||
ipcStore->deleteData(dataAddress);
|
||||
switch (result) {
|
||||
case returnvalue::OK:
|
||||
|
@ -17,8 +17,8 @@ class SimpleActionHelper : public ActionHelper {
|
||||
ReturnValue_t reportData(SerializeIF* data);
|
||||
|
||||
protected:
|
||||
void prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId,
|
||||
store_address_t dataAddress) override;
|
||||
virtual void prepareExecution(MessageQueueId_t commandedBy, size_t offset,
|
||||
store_address_t dataAddress) override;
|
||||
void resetHelper() override;
|
||||
|
||||
private:
|
||||
|
29
src/fsfw/action/TemplateAction.h
Normal file
29
src/fsfw/action/TemplateAction.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "Action.h"
|
||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||
|
||||
template <class owner_class, class action_type, class ActionEnum>
|
||||
class TemplateAction : public Action {
|
||||
public:
|
||||
#ifdef FSFW_INTROSPECTION
|
||||
TemplateAction(owner_class *myOwner, ActionEnum id) : Action(), myOwner(myOwner) {
|
||||
Action::setEnum(&id);
|
||||
if (myOwner->getActionHelper() == nullptr) {
|
||||
sif::error
|
||||
<< "TemplateAction::TemplateAction: Action instances need to be created (ie located) after the actionHelper instance."
|
||||
<< "Program will segfault now..." << std::endl;
|
||||
}
|
||||
myOwner->getActionHelper()->registerAction(this);
|
||||
}
|
||||
#else
|
||||
TemplateAction(owner_class *myOwner, ActionEnum id) : Action((uint32_t)id), myOwner(myOwner) {
|
||||
myOwner->getActionHelper()->registerAction(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
ReturnValue_t handle() override { return myOwner->handleAction(dynamic_cast<action_type *>(this)); }
|
||||
|
||||
private:
|
||||
owner_class *myOwner;
|
||||
};
|
@ -51,8 +51,9 @@ class VarLenField : public SerializeIF {
|
||||
return os;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
using SerializeIF::deSerialize; // we overloaded above, so this is needed to uncofuse the
|
||||
// compiler
|
||||
ReturnValue_t deSerialize(const uint8_t **buffer, size_t *size,
|
||||
Endianness streamEndianness) override;
|
||||
|
||||
|
@ -17,7 +17,7 @@ ReturnValue_t FinishPduCreator::serialize(uint8_t **buffer, size_t *size, size_t
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
if (*size + 1 >= maxSize) {
|
||||
if (*size + 1 > maxSize) {
|
||||
return SerializeIF::BUFFER_TOO_SHORT;
|
||||
}
|
||||
**buffer = finishInfo.getConditionCode() << 4 | finishInfo.getDeliveryCode() << 2 |
|
||||
|
@ -24,8 +24,8 @@ ReturnValue_t HeaderCreator::serialize(uint8_t **buffer, size_t *size, size_t ma
|
||||
*buffer += 1;
|
||||
**buffer = pduDataFieldLen & 0x00ff;
|
||||
*buffer += 1;
|
||||
**buffer = segmentationCtrl << 7 | pduConf.sourceId.getWidth() << 4 | segmentMetadataFlag << 3 |
|
||||
pduConf.seqNum.getWidth();
|
||||
**buffer = segmentationCtrl << 7 | ((pduConf.sourceId.getWidth() - 1) << 4) |
|
||||
segmentMetadataFlag << 3 | (pduConf.seqNum.getWidth() - 1);
|
||||
*buffer += 1;
|
||||
*size += 4;
|
||||
ReturnValue_t result = pduConf.sourceId.serialize(buffer, size, maxSize, streamEndianness);
|
||||
|
@ -78,11 +78,11 @@ cfdp::SegmentationControl PduHeaderReader::getSegmentationControl() const {
|
||||
}
|
||||
|
||||
cfdp::WidthInBytes PduHeaderReader::getLenEntityIds() const {
|
||||
return static_cast<cfdp::WidthInBytes>((pointers.fixedHeader->fourthByte >> 4) & 0x07);
|
||||
return static_cast<cfdp::WidthInBytes>(((pointers.fixedHeader->fourthByte >> 4) & 0b111) + 1);
|
||||
}
|
||||
|
||||
cfdp::WidthInBytes PduHeaderReader::getLenSeqNum() const {
|
||||
return static_cast<cfdp::WidthInBytes>(pointers.fixedHeader->fourthByte & 0x07);
|
||||
return static_cast<cfdp::WidthInBytes>((pointers.fixedHeader->fourthByte & 0b111) + 1);
|
||||
}
|
||||
|
||||
cfdp::SegmentMetadataFlag PduHeaderReader::getSegmentMetadataFlag() const {
|
||||
|
@ -23,6 +23,8 @@ class EntityIdTlv : public TlvIF {
|
||||
*/
|
||||
ReturnValue_t deSerialize(cfdp::Tlv& tlv, Endianness endianness);
|
||||
|
||||
using SerializeIF::deSerialize; // we overloaded this function, so this is needed to unconfuse
|
||||
// the compiler
|
||||
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
|
||||
Endianness streamEndianness) override;
|
||||
|
||||
|
@ -26,6 +26,8 @@ class FilestoreRequestTlv : public cfdp::FilestoreTlvBase {
|
||||
*/
|
||||
ReturnValue_t deSerialize(cfdp::Tlv &tlv, Endianness endianness);
|
||||
|
||||
using SerializeIF::deSerialize; // we overloaded above, so this is needed to uncofuse the
|
||||
// compiler
|
||||
ReturnValue_t deSerialize(const uint8_t **buffer, size_t *size,
|
||||
Endianness streamEndianness) override;
|
||||
|
||||
|
@ -29,6 +29,8 @@ class FilestoreResponseTlv : public cfdp::FilestoreTlvBase {
|
||||
*/
|
||||
ReturnValue_t deSerialize(const cfdp::Tlv& tlv, Endianness endianness);
|
||||
|
||||
using SerializeIF::deSerialize; // we overloaded this function, so this is needed to unconfuse
|
||||
// the compiler
|
||||
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
|
||||
Endianness streamEndianness) override;
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "../returnvalues/returnvalue.h"
|
||||
#include "../serialize/SerializeAdapter.h"
|
||||
#include "../serialize/SerializeIF.h"
|
||||
#include "definitions.h"
|
||||
|
||||
/**
|
||||
* @brief A List that stores its values in an array.
|
||||
@ -19,9 +20,6 @@ class ArrayList {
|
||||
friend class SerialArrayListAdapter;
|
||||
|
||||
public:
|
||||
static const uint8_t INTERFACE_ID = CLASS_ID::ARRAY_LIST;
|
||||
static const ReturnValue_t FULL = MAKE_RETURN_CODE(0x01);
|
||||
|
||||
/**
|
||||
* This is the allocating constructor.
|
||||
* It allocates an array of the specified size.
|
||||
@ -187,7 +185,7 @@ class ArrayList {
|
||||
*/
|
||||
ReturnValue_t insert(T entry) {
|
||||
if (size >= maxSize_) {
|
||||
return FULL;
|
||||
return containers::LIST_FULL;
|
||||
}
|
||||
entries[size] = entry;
|
||||
++size;
|
||||
|
@ -12,6 +12,7 @@ template <typename T, size_t MAX_SIZE, typename count_t = uint8_t>
|
||||
class FixedArrayList : public ArrayList<T, count_t> {
|
||||
static_assert(MAX_SIZE <= std::numeric_limits<count_t>::max(),
|
||||
"count_t is not large enough to hold MAX_SIZE");
|
||||
static_assert(MAX_SIZE > 0, "MAX_SIZE is 0");
|
||||
|
||||
private:
|
||||
T data[MAX_SIZE];
|
||||
@ -20,15 +21,19 @@ class FixedArrayList : public ArrayList<T, count_t> {
|
||||
FixedArrayList() : ArrayList<T, count_t>(data, MAX_SIZE) {}
|
||||
|
||||
FixedArrayList(const FixedArrayList& other) : ArrayList<T, count_t>(data, MAX_SIZE) {
|
||||
memcpy(this->data, other.data, sizeof(this->data));
|
||||
this->entries = data;
|
||||
this->size = other.size;
|
||||
for (size_t idx = 0; idx < this->size; idx++) {
|
||||
data[idx] = other.data[idx];
|
||||
}
|
||||
}
|
||||
|
||||
FixedArrayList& operator=(FixedArrayList other) {
|
||||
memcpy(this->data, other.data, sizeof(this->data));
|
||||
this->entries = data;
|
||||
this->size = other.size;
|
||||
for (size_t idx = 0; idx < this->size; idx++) {
|
||||
data[idx] = other.data[idx];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,8 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "../returnvalues/returnvalue.h"
|
||||
#include "ArrayList.h"
|
||||
#include "definitions.h"
|
||||
|
||||
/**
|
||||
* @brief Map implementation for maps with a pre-defined size.
|
||||
@ -24,11 +24,6 @@ class FixedMap : public SerializeIF {
|
||||
"derived class from SerializeIF to be serialize-able");
|
||||
|
||||
public:
|
||||
static const uint8_t INTERFACE_ID = CLASS_ID::FIXED_MAP;
|
||||
static const ReturnValue_t KEY_ALREADY_EXISTS = MAKE_RETURN_CODE(0x01);
|
||||
static const ReturnValue_t MAP_FULL = MAKE_RETURN_CODE(0x02);
|
||||
static const ReturnValue_t KEY_DOES_NOT_EXIST = MAKE_RETURN_CODE(0x03);
|
||||
|
||||
private:
|
||||
static const key_t EMPTY_SLOT = -1;
|
||||
ArrayList<std::pair<key_t, T>, uint32_t> theMap;
|
||||
@ -76,10 +71,10 @@ class FixedMap : public SerializeIF {
|
||||
|
||||
ReturnValue_t insert(key_t key, T value, Iterator* storedValue = nullptr) {
|
||||
if (exists(key) == returnvalue::OK) {
|
||||
return KEY_ALREADY_EXISTS;
|
||||
return containers::KEY_ALREADY_EXISTS;
|
||||
}
|
||||
if (_size == theMap.maxSize()) {
|
||||
return MAP_FULL;
|
||||
return containers::MAP_FULL;
|
||||
}
|
||||
theMap[_size].first = key;
|
||||
theMap[_size].second = value;
|
||||
@ -93,7 +88,7 @@ class FixedMap : public SerializeIF {
|
||||
ReturnValue_t insert(std::pair<key_t, T> pair) { return insert(pair.first, pair.second); }
|
||||
|
||||
ReturnValue_t exists(key_t key) const {
|
||||
ReturnValue_t result = KEY_DOES_NOT_EXIST;
|
||||
ReturnValue_t result = containers::KEY_DOES_NOT_EXIST;
|
||||
if (findIndex(key) < _size) {
|
||||
result = returnvalue::OK;
|
||||
}
|
||||
@ -103,7 +98,7 @@ class FixedMap : public SerializeIF {
|
||||
ReturnValue_t erase(Iterator* iter) {
|
||||
uint32_t i;
|
||||
if ((i = findIndex((*iter).value->first)) >= _size) {
|
||||
return KEY_DOES_NOT_EXIST;
|
||||
return containers::KEY_DOES_NOT_EXIST;
|
||||
}
|
||||
theMap[i] = theMap[_size - 1];
|
||||
--_size;
|
||||
@ -114,7 +109,7 @@ class FixedMap : public SerializeIF {
|
||||
ReturnValue_t erase(key_t key) {
|
||||
uint32_t i;
|
||||
if ((i = findIndex(key)) >= _size) {
|
||||
return KEY_DOES_NOT_EXIST;
|
||||
return containers::KEY_DOES_NOT_EXIST;
|
||||
}
|
||||
theMap[i] = theMap[_size - 1];
|
||||
--_size;
|
||||
@ -160,8 +155,8 @@ class FixedMap : public SerializeIF {
|
||||
|
||||
uint32_t maxSize() const { return theMap.maxSize(); }
|
||||
|
||||
virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
|
||||
Endianness streamEndianness) const {
|
||||
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
|
||||
Endianness streamEndianness) const override {
|
||||
ReturnValue_t result =
|
||||
SerializeAdapter::serialize(&this->_size, buffer, size, maxSize, streamEndianness);
|
||||
uint32_t i = 0;
|
||||
@ -175,7 +170,7 @@ class FixedMap : public SerializeIF {
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual size_t getSerializedSize() const {
|
||||
size_t getSerializedSize() const override {
|
||||
uint32_t printSize = sizeof(_size);
|
||||
uint32_t i = 0;
|
||||
|
||||
@ -187,8 +182,8 @@ class FixedMap : public SerializeIF {
|
||||
return printSize;
|
||||
}
|
||||
|
||||
virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
|
||||
Endianness streamEndianness) {
|
||||
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
|
||||
Endianness streamEndianness) override {
|
||||
ReturnValue_t result =
|
||||
SerializeAdapter::deSerialize(&this->_size, buffer, size, streamEndianness);
|
||||
if (this->_size > theMap.maxSize()) {
|
||||
|
14
src/fsfw/container/definitions.h
Normal file
14
src/fsfw/container/definitions.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef FSFW_CONTAINER_DEFINITIONS_H_
|
||||
#define FSFW_CONTAINER_DEFINITIONS_H_
|
||||
|
||||
#include "fsfw/retval.h"
|
||||
|
||||
namespace containers {
|
||||
static const ReturnValue_t KEY_ALREADY_EXISTS = returnvalue::makeCode(CLASS_ID::FIXED_MAP, 0x01);
|
||||
static const ReturnValue_t MAP_FULL = returnvalue::makeCode(CLASS_ID::FIXED_MAP, 0x02);
|
||||
static const ReturnValue_t KEY_DOES_NOT_EXIST = returnvalue::makeCode(CLASS_ID::FIXED_MAP, 0x03);
|
||||
|
||||
static const ReturnValue_t LIST_FULL = returnvalue::makeCode(CLASS_ID::ARRAY_LIST, 0x01);
|
||||
} // namespace containers
|
||||
|
||||
#endif /* FSFW_CONTAINER_DEFINITIONS_H_ */
|
@ -1,2 +1 @@
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE ControllerBase.cpp
|
||||
ExtendedControllerBase.cpp)
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE ControllerBase.cpp)
|
||||
|
@ -12,7 +12,10 @@ ControllerBase::ControllerBase(object_id_t setObjectId, object_id_t parentId,
|
||||
mode(MODE_OFF),
|
||||
submode(SUBMODE_NONE),
|
||||
modeHelper(this),
|
||||
healthHelper(this, setObjectId) {
|
||||
healthHelper(this, setObjectId),
|
||||
actionHelper(this, commandQueue),
|
||||
datapoolHelper(this),
|
||||
housekeepingHelper(this) {
|
||||
commandQueue = QueueFactory::instance()->createMessageQueue(commandQueueDepth);
|
||||
}
|
||||
|
||||
@ -45,6 +48,21 @@ ReturnValue_t ControllerBase::initialize() {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = actionHelper.initialize(commandQueue);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = housekeepingHelper.initialize();
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = datapoolHelper.initialize();
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
@ -64,6 +82,12 @@ void ControllerBase::handleQueue() {
|
||||
if (result == returnvalue::OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result = actionHelper.handleActionMessage(&command);
|
||||
if (result == returnvalue::OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result = handleCommandMessage(&command);
|
||||
if (result == returnvalue::OK) {
|
||||
continue;
|
||||
@ -99,6 +123,15 @@ ReturnValue_t ControllerBase::performOperation(uint8_t opCode) {
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
const ModeHelper* ControllerBase::getModeHelper() const { return &modeHelper; }
|
||||
|
||||
ActionHelper* ControllerBase::getActionHelper() { return &actionHelper; }
|
||||
|
||||
ReturnValue_t ControllerBase::executeAction(Action* action) { return action->handle(); }
|
||||
|
||||
DatapoolHelper* ControllerBase::getDatapoolHelper() { return &datapoolHelper; }
|
||||
HousekeepingHelper* ControllerBase::getHousekeepingHelper() { return &housekeepingHelper; }
|
||||
|
||||
void ControllerBase::modeChanged(Mode_t mode_, Submode_t submode_) {}
|
||||
|
||||
ReturnValue_t ControllerBase::setHealth(HealthState health) {
|
||||
|
@ -1,13 +1,15 @@
|
||||
#ifndef FSFW_CONTROLLER_CONTROLLERBASE_H_
|
||||
#define FSFW_CONTROLLER_CONTROLLERBASE_H_
|
||||
|
||||
#include "fsfw/health/HasHealthIF.h"
|
||||
#include "fsfw/health/HealthHelper.h"
|
||||
#include "fsfw/modes/HasModesIF.h"
|
||||
#include "fsfw/modes/ModeHelper.h"
|
||||
#include "fsfw/objectmanager/SystemObject.h"
|
||||
#include "fsfw/tasks/ExecutableObjectIF.h"
|
||||
#include "fsfw/tasks/PeriodicTaskIF.h"
|
||||
#include <fsfw/health/HasHealthIF.h>
|
||||
#include <fsfw/health/HealthHelper.h>
|
||||
#include <fsfw/introspection/ClasslessEnum.h>
|
||||
#include <fsfw/modes/HasModesIF.h>
|
||||
#include <fsfw/action/HasActionsIF.h>
|
||||
#include <fsfw/datapool/HasDatapoolIF.h>
|
||||
#include <fsfw/objectmanager/SystemObject.h>
|
||||
#include <fsfw/tasks/ExecutableObjectIF.h>
|
||||
#include <fsfw/tasks/PeriodicTaskIF.h>
|
||||
|
||||
/**
|
||||
* @brief Generic base class for controller classes
|
||||
@ -17,10 +19,14 @@
|
||||
*/
|
||||
class ControllerBase : public HasModesIF,
|
||||
public HasHealthIF,
|
||||
public HasDatapoolIF,
|
||||
public HasActionsIF,
|
||||
public ExecutableObjectIF,
|
||||
public SystemObject {
|
||||
public:
|
||||
static const Mode_t MODE_NORMAL = 2;
|
||||
FSFW_CLASSLESS_ENUM(ControllerModes, Mode_t,
|
||||
((CONTROLLER_MODE_ON, MODE_ON, "On"))((CONTROLLER_MODE_OFF, MODE_OFF,
|
||||
"Off"))((MODE_NORMAL, 2, "Normal")))
|
||||
|
||||
ControllerBase(object_id_t setObjectId, object_id_t parentId, size_t commandQueueDepth = 3);
|
||||
~ControllerBase() override;
|
||||
@ -39,6 +45,17 @@ class ControllerBase : public HasModesIF,
|
||||
void setTaskIF(PeriodicTaskIF *task) override;
|
||||
ReturnValue_t initializeAfterTaskCreation() override;
|
||||
|
||||
/** HasModeIF overrides */
|
||||
const ModeHelper *getModeHelper() const override;
|
||||
|
||||
/** HasActionsIF override */
|
||||
ActionHelper* getActionHelper() override;
|
||||
ReturnValue_t executeAction(Action* action) override;
|
||||
|
||||
/** HasDatapoolIF overrides */
|
||||
DatapoolHelper* getDatapoolHelper() override;
|
||||
HousekeepingHelper* getHousekeepingHelper() override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Implemented by child class. Handle command messages which are not
|
||||
@ -68,6 +85,12 @@ class ControllerBase : public HasModesIF,
|
||||
|
||||
HealthHelper healthHelper;
|
||||
|
||||
ActionHelper actionHelper;
|
||||
|
||||
DatapoolHelper datapoolHelper;
|
||||
|
||||
HousekeepingHelper housekeepingHelper;
|
||||
|
||||
/**
|
||||
* Pointer to the task which executes this component,
|
||||
* is invalid before setTaskIF was called.
|
||||
|
@ -8,11 +8,12 @@ ExtendedControllerBase::ExtendedControllerBase(object_id_t objectId, object_id_t
|
||||
|
||||
ExtendedControllerBase::~ExtendedControllerBase() = default;
|
||||
|
||||
ReturnValue_t ExtendedControllerBase::executeAction(ActionId_t actionId,
|
||||
MessageQueueId_t commandedBy,
|
||||
const uint8_t *data, size_t size) {
|
||||
/* Needs to be overriden and implemented by child class. */
|
||||
return returnvalue::OK;
|
||||
ActionHelper *ExtendedControllerBase::getActionHelper() {
|
||||
return &actionHelper;
|
||||
}
|
||||
|
||||
ReturnValue_t ExtendedControllerBase::executeAction(Action *action) {
|
||||
return action->handle();
|
||||
}
|
||||
|
||||
object_id_t ExtendedControllerBase::getObjectId() const { return SystemObject::getObjectId(); }
|
||||
|
@ -29,6 +29,10 @@ class ExtendedControllerBase : public ControllerBase,
|
||||
ReturnValue_t performOperation(uint8_t opCode) override;
|
||||
ReturnValue_t initializeAfterTaskCreation() override;
|
||||
|
||||
/* HasActionsIF overrides */
|
||||
ActionHelper* getActionHelper() override;
|
||||
ReturnValue_t executeAction(Action* actionId) override;
|
||||
|
||||
protected:
|
||||
LocalDataPoolManager poolManager;
|
||||
ActionHelper actionHelper;
|
||||
@ -49,10 +53,6 @@ class ExtendedControllerBase : public ControllerBase,
|
||||
/* Handle the four messages mentioned above */
|
||||
void handleQueue() override;
|
||||
|
||||
/* HasActionsIF overrides */
|
||||
ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
|
||||
const uint8_t* data, size_t size) override;
|
||||
|
||||
/* HasLocalDatapoolIF overrides */
|
||||
LocalDataPoolManager* getHkManagerHandle() override;
|
||||
[[nodiscard]] object_id_t getObjectId() const override;
|
||||
|
@ -1 +1 @@
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE PoolDataSetBase.cpp PoolEntry.cpp)
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE Dataset.cpp DatapoolHelper.cpp)
|
||||
|
20
src/fsfw/datapool/DatapoolHelper.cpp
Normal file
20
src/fsfw/datapool/DatapoolHelper.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
#include "DatapoolHelper.h"
|
||||
|
||||
DatapoolHelper::DatapoolHelper(HasDatapoolIF* owner) : HousekeepingHelper(owner) {}
|
||||
|
||||
ReturnValue_t DatapoolHelper::initialize() {
|
||||
return HousekeepingHelper::initialize();
|
||||
}
|
||||
|
||||
const Dataset* DatapoolHelper::getDataSet(HousekeepingSetId_t id) {
|
||||
auto iter = dataSets.find(id);
|
||||
if (iter == dataSets.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
void DatapoolHelper::registerSet(Dataset* set) {
|
||||
auto id = set->getId();
|
||||
dataSets.emplace(id, set);
|
||||
}
|
28
src/fsfw/datapool/DatapoolHelper.h
Normal file
28
src/fsfw/datapool/DatapoolHelper.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "Dataset.h"
|
||||
#include "HasDatapoolIF.h"
|
||||
|
||||
#include <fsfw/housekeeping/HousekeepingHelper.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <map>
|
||||
|
||||
class DatapoolHelper : public HousekeepingHelper {
|
||||
public:
|
||||
DatapoolHelper(HasDatapoolIF *owner);
|
||||
~DatapoolHelper() = default;
|
||||
|
||||
const Dataset* getDataSet(HousekeepingSetId_t id);
|
||||
|
||||
const std::map<HousekeepingSetId_t, Dataset*>* getDatasets() const {
|
||||
return &dataSets;
|
||||
}
|
||||
|
||||
void registerSet(Dataset* set);
|
||||
|
||||
ReturnValue_t initialize();
|
||||
|
||||
private:
|
||||
std::map<HousekeepingSetId_t, Dataset*> dataSets;
|
||||
};
|
141
src/fsfw/datapool/Dataset.cpp
Normal file
141
src/fsfw/datapool/Dataset.cpp
Normal file
@ -0,0 +1,141 @@
|
||||
#include "Dataset.h"
|
||||
|
||||
#include <fsfw/ipc/MutexFactory.h>
|
||||
#include <fsfw/objectmanager/ObjectManager.h>
|
||||
#include <fsfw/returnvalues/returnvalue.h>
|
||||
|
||||
//TODO for non allocated: detect if initialized, and if not, call it
|
||||
|
||||
#include "HasDatapoolIF.h"
|
||||
|
||||
#ifdef FSFW_INTROSPECTION
|
||||
Dataset::Dataset(HasDatapoolIF* owner, const EnumIF &id, bool allowUserCommit)
|
||||
: HousekeepingSet(owner, id), allocated(true), allowUserCommit(allowUserCommit) {
|
||||
this->owner.pointer = owner;
|
||||
owner->getDatapoolHelper()->registerSet(this);
|
||||
mutex = MutexFactory::instance()->createMutex();
|
||||
}
|
||||
|
||||
Dataset::Dataset(uint32_t owner_id, const EnumIF &id)
|
||||
: HousekeepingSet(nullptr, id), allocated(false), allowUserCommit(false) {
|
||||
this->owner.id = owner_id;
|
||||
}
|
||||
#else
|
||||
Dataset::Dataset(HasDatapoolIF* owner, HousekeepingSetId_t id, bool allowUserCommit)
|
||||
: HousekeepingSet(owner, id), allocated(true), allowUserCommit(allowUserCommit) {
|
||||
this->owner.pointer = owner;
|
||||
owner->getDatapoolHelper()->registerSet(this);
|
||||
mutex = MutexFactory::instance()->createMutex();
|
||||
}
|
||||
|
||||
Dataset::Dataset(uint32_t owner_id, HousekeepingSetId_t id) : HousekeepingSet(nullptr, id) {
|
||||
this->owner.id = owner_id;
|
||||
}
|
||||
#endif
|
||||
|
||||
Dataset::~Dataset() { MutexFactory::instance()->deleteMutex(mutex); }
|
||||
|
||||
void Dataset::commit() {
|
||||
if ((!allocated) && (!allowUserCommit)) {
|
||||
return;
|
||||
}
|
||||
|
||||
lock();
|
||||
for (auto variable : variables) {
|
||||
variable->commit();
|
||||
}
|
||||
unlock();
|
||||
}
|
||||
|
||||
void Dataset::commit(bool valid) {
|
||||
if ((!allocated) && (!allowUserCommit)) {
|
||||
return;
|
||||
}
|
||||
setAllValid(valid);
|
||||
commit();
|
||||
}
|
||||
|
||||
void Dataset::setAllValid(bool valid) {
|
||||
if ((!allocated) && (!allowUserCommit)) {
|
||||
return;
|
||||
}
|
||||
for (auto variable : variables) {
|
||||
variable->setValid(valid);
|
||||
}
|
||||
}
|
||||
|
||||
void Dataset::read() {
|
||||
lock();
|
||||
for (auto variable : variables) {
|
||||
variable->read();
|
||||
}
|
||||
unlock();
|
||||
}
|
||||
|
||||
bool Dataset::hasChanged() {
|
||||
bool changed = hasChangedNoRead();
|
||||
read();
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool Dataset::hasChangedOrOlderThan(uint32_t seconds) {
|
||||
bool changed = hasChanged();
|
||||
// TODO time
|
||||
read();
|
||||
return changed;
|
||||
}
|
||||
|
||||
const std::vector<DatasetEntryIF*>* Dataset::getVariables() const { return &variables; }
|
||||
|
||||
ReturnValue_t Dataset::initialize() {
|
||||
if (allocated) {
|
||||
// nothing to do
|
||||
return returnvalue::OK;
|
||||
}
|
||||
HasDatapoolIF* actualOwner = ObjectManager::instance()->get<HasDatapoolIF>(owner.id);
|
||||
if (actualOwner == nullptr) {
|
||||
puts("owner type");
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
const Dataset* theOtherSet = actualOwner->getDatapoolHelper()->getDataSet(this->id);
|
||||
if (theOtherSet == nullptr) {
|
||||
puts("no set");
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
if (theOtherSet->variables.size() != variables.size()) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
this->mutex = theOtherSet->mutex;
|
||||
this->allowUserCommit = theOtherSet->allowUserCommit;
|
||||
|
||||
for (size_t i = 0; i < variables.size(); i++) {
|
||||
variables[i]->connect(theOtherSet->variables[i]);
|
||||
}
|
||||
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
// operator[]
|
||||
|
||||
|
||||
bool Dataset::registerEntry(DatasetEntryIF* entry) {
|
||||
variables.push_back(entry);
|
||||
return allocated;
|
||||
}
|
||||
|
||||
void Dataset::lock() { mutex->lockMutex(MutexIF::TimeoutType::BLOCKING); }
|
||||
|
||||
void Dataset::unlock() { mutex->unlockMutex(); }
|
||||
|
||||
bool Dataset::hasChangedNoRead() {
|
||||
bool changed = false;
|
||||
for (auto variable : variables) {
|
||||
if (variable->changed()) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
183
src/fsfw/datapool/Dataset.h
Normal file
183
src/fsfw/datapool/Dataset.h
Normal file
@ -0,0 +1,183 @@
|
||||
#pragma once
|
||||
|
||||
#include <fsfw/housekeeping/HousekeepingSet.h>
|
||||
#include <fsfw/introspection/Enum.h>
|
||||
#include <fsfw/ipc/MutexIF.h>
|
||||
#include <fsfw/returnvalues/returnvalue.h>
|
||||
#include <fsfw/storagemanager/storeAddress.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "DatasetEntryIF.h"
|
||||
|
||||
// TODO ring inclusion
|
||||
// #include "HasDatapoolIF.h"
|
||||
class HasDatapoolIF;
|
||||
|
||||
// TODO allow user commit and reporting TM
|
||||
|
||||
/**
|
||||
* This class has a dual use
|
||||
*
|
||||
* 1) It is used as an IPC method:
|
||||
* It implements a shared memory which can be written and read by different parties
|
||||
*
|
||||
* 2) It is used to define data that is to be downlinked, either periodically or
|
||||
* triggered by an (abstract, not in the sense of fsfw/events) event occuring, ie
|
||||
* data was received from an external source and should be shared with ground.
|
||||
*
|
||||
* Generating a downlinked report is coupled to commiting, as without a commit there is no
|
||||
* change in information to be reported.
|
||||
*
|
||||
* Nominally, the decision to report is done by the set itself, depending on its settings.
|
||||
* If a report is to be forced regardless of these settings, the commitAndReport() functions
|
||||
* are provided which will always generate a report.
|
||||
*
|
||||
* Periodic reports are not tied to a specific tc and are reported as "unrequested". When forcing
|
||||
* a report, optionally a tc can be provided which (logically) triggered the report.
|
||||
*
|
||||
* And, as life is complicated, there is a third set of commits: commitAndReportIfRequested(). Same
|
||||
* as before, but this time, a report is only generated, if the store_address_t is valid. These are
|
||||
* used if at the place where the commit is called, it is not known if there is a request (cf DHB
|
||||
* interpretDeviceReply)
|
||||
*/
|
||||
|
||||
class Dataset : public HousekeepingSet {
|
||||
protected:
|
||||
#ifdef FSFW_INTROSPECTION
|
||||
Dataset(HasDatapoolIF* owner, const EnumIF &id, bool allowUserCommit);
|
||||
Dataset(uint32_t owner_id, const EnumIF &id);
|
||||
#else
|
||||
Dataset(HasDatapoolIF* owner, HousekeepingSetId_t id, bool allowUserCommit);
|
||||
Dataset(uint32_t owner_id, HousekeepingSetId_t id);
|
||||
#endif
|
||||
public:
|
||||
~Dataset() override;
|
||||
/**
|
||||
* Copy content of local copies into actual variable
|
||||
*
|
||||
*/
|
||||
void commit();
|
||||
|
||||
/**
|
||||
* Copy content of local copies into actual variable
|
||||
*
|
||||
* calls setValid(valid) before committing
|
||||
*
|
||||
*/
|
||||
void commit(bool valid);
|
||||
|
||||
/**
|
||||
* Copy content of local copies into actual variable
|
||||
* Force sending of TM packet
|
||||
*
|
||||
*/
|
||||
void commitAndReport();
|
||||
|
||||
/**
|
||||
* Copy content of local copies into actual variable
|
||||
* Force sending of TM packet
|
||||
*
|
||||
* calls setValid(valid) before committing
|
||||
*
|
||||
*/
|
||||
void commitAndReport(bool valid);
|
||||
|
||||
/**
|
||||
* Copy content of local copies into actual variable
|
||||
* Force sending of TM packet, in reference to tc
|
||||
*
|
||||
*/
|
||||
void commitAndReport(store_address_t tc, size_t tc_offset);
|
||||
|
||||
/**
|
||||
* Copy content of local copies into actual variable
|
||||
* Force sending of TM packet, in reference to tc
|
||||
*
|
||||
* calls setValid(valid) before committing
|
||||
*
|
||||
*/
|
||||
void commitAndReport(bool valid, store_address_t tc, size_t tc_offset);
|
||||
|
||||
/**
|
||||
* Copy content of local copies into actual variable
|
||||
* Force sending of TM packet, in reference to tc, if tc is valid
|
||||
*
|
||||
*/
|
||||
void commitAndReportIfRequested(store_address_t tc, size_t tc_offset);
|
||||
|
||||
/**
|
||||
* Copy content of local copies into actual variable
|
||||
* Force sending of TM packet, in reference to tc, if tc is valid
|
||||
*
|
||||
* calls setValid(valid) before committing
|
||||
*
|
||||
*/
|
||||
void commitAndReportIfRequested(bool valid, store_address_t tc, size_t tc_offset);
|
||||
|
||||
/**
|
||||
* set all contained variables to #valid
|
||||
*
|
||||
*/
|
||||
void setAllValid(bool valid);
|
||||
|
||||
/**
|
||||
* Copy content of actual variables into local copies
|
||||
*
|
||||
*
|
||||
*/
|
||||
void read();
|
||||
|
||||
/**
|
||||
* returns true if local copies and actual variables differ
|
||||
*
|
||||
* implicitely calls read()
|
||||
*/
|
||||
bool hasChanged();
|
||||
|
||||
/**
|
||||
* returns true if local copies and actual variables differ
|
||||
* or time since last time true has been returned is greater than
|
||||
* supplied time
|
||||
*
|
||||
* implicitely calls read()
|
||||
*
|
||||
*/
|
||||
bool hasChangedOrOlderThan(uint32_t seconds);
|
||||
|
||||
/**
|
||||
* get List of contained Valiables
|
||||
*/
|
||||
virtual const std::vector<DatasetEntryIF*>* getVariables() const;
|
||||
|
||||
ReturnValue_t initialize();
|
||||
|
||||
// operator[]
|
||||
|
||||
bool registerEntry(DatasetEntryIF*);
|
||||
|
||||
protected:
|
||||
|
||||
bool allocated;
|
||||
bool allowUserCommit;
|
||||
union {
|
||||
object_id_t id;
|
||||
HasDatapoolIF* pointer;
|
||||
} owner;
|
||||
MutexIF* mutex;
|
||||
|
||||
std::vector<DatasetEntryIF*> variables;
|
||||
|
||||
/**
|
||||
* lock the mutex of the set
|
||||
*/
|
||||
void lock();
|
||||
|
||||
/**
|
||||
* unlock the mutex of the set
|
||||
*/
|
||||
void unlock();
|
||||
|
||||
bool hasChangedNoRead();
|
||||
};
|
87
src/fsfw/datapool/DatasetEntry.h
Normal file
87
src/fsfw/datapool/DatasetEntry.h
Normal file
@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include <fsfw/introspection/Parameter.h>
|
||||
#include <fsfw/introspection/Types.h>
|
||||
#include <fsfw/introspection/TypesHelper.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Dataset.h"
|
||||
#include "DatasetEntryIF.h"
|
||||
// TODO: ifdef introspection stuff
|
||||
|
||||
template <typename T>
|
||||
class DatasetEntry : public Parameter<T>, public DatasetEntryIF {
|
||||
protected:
|
||||
#ifdef FSFW_INTROSPECTION
|
||||
DatasetEntry(Dataset *owner, const char *name)
|
||||
: Parameter<T>(owner, name)
|
||||
#else
|
||||
DatasetEntry(Dataset *owner)
|
||||
: Parameter<T>(owner)
|
||||
#endif
|
||||
{
|
||||
allocated = owner->registerEntry(this);
|
||||
if (!allocated) {
|
||||
return;
|
||||
}
|
||||
storedValue = new T();
|
||||
storedValid = new bool;
|
||||
}
|
||||
|
||||
public:
|
||||
#ifdef FSFW_INTROSPECTION
|
||||
static DatasetEntry createDatasetEntry(Dataset *owner, const char *name) {
|
||||
return DatasetEntry(owner, name);
|
||||
}
|
||||
#else
|
||||
static DatasetEntry createDatasetEntry(Dataset *owner) { return DatasetEntry(owner); }
|
||||
#endif
|
||||
|
||||
~DatasetEntry() {
|
||||
if (allocated) {
|
||||
delete storedValue;
|
||||
delete storedValid;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void setValid(bool isValid) { valid = isValid; }
|
||||
|
||||
virtual bool getValid() { return valid; }
|
||||
|
||||
//T value; //TODO can this be private?
|
||||
|
||||
protected:
|
||||
virtual void commit() {
|
||||
*storedValue = Parameter<T>::value;
|
||||
*storedValid = valid;
|
||||
}
|
||||
virtual void read() {
|
||||
Parameter<T>::value = *storedValue;
|
||||
valid = *storedValid;
|
||||
}
|
||||
|
||||
virtual void connect(DatasetEntryIF *entry) {
|
||||
DatasetEntry<T> *theOther = dynamic_cast<DatasetEntry<T> *>(entry);
|
||||
if (theOther == nullptr) {
|
||||
// Configuration error
|
||||
return;
|
||||
}
|
||||
this->storedValue = theOther->storedValue;
|
||||
this->storedValid = theOther->storedValid;
|
||||
}
|
||||
|
||||
virtual bool changed() { return ((Parameter<T>::value != *storedValue) || (valid != *storedValid)); }
|
||||
|
||||
private:
|
||||
|
||||
T *storedValue;
|
||||
bool *storedValid;
|
||||
bool valid;
|
||||
bool allocated;
|
||||
};
|
||||
|
||||
#ifdef FSFW_INTROSPECTION
|
||||
#define createDatasetEntry(p1, p2) createDatasetEntry(p1, p2)
|
||||
#else
|
||||
#define createDatasetEntry(p1, p2) createDatasetEntry(p1)
|
||||
#endif
|
39
src/fsfw/datapool/DatasetEntryIF.h
Normal file
39
src/fsfw/datapool/DatasetEntryIF.h
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef FSFW_DATAPOOL_POOLENTRYIF_H_
|
||||
#define FSFW_DATAPOOL_POOLENTRYIF_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <fsfw/introspection/Types.h>
|
||||
|
||||
class DatasetEntryIF {
|
||||
friend class Dataset;
|
||||
|
||||
public:
|
||||
|
||||
virtual ~DatasetEntryIF() {}
|
||||
/**
|
||||
* @brief This method allows to set the valid information of the pool entry.
|
||||
*/
|
||||
virtual void setValid(bool isValid) = 0;
|
||||
/**
|
||||
* @brief This method allows to set the valid information of the pool entry.
|
||||
*/
|
||||
virtual bool getValid() = 0;
|
||||
|
||||
|
||||
protected:
|
||||
virtual void commit() = 0;
|
||||
virtual void read() = 0;
|
||||
virtual void connect(DatasetEntryIF* entry) = 0;
|
||||
|
||||
|
||||
/**
|
||||
* returns if value and actual value is different
|
||||
*/
|
||||
virtual bool changed() = 0;
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOL_POOLENTRYIF_H_ */
|
13
src/fsfw/datapool/HasDatapoolIF.h
Normal file
13
src/fsfw/datapool/HasDatapoolIF.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
//TODO ring
|
||||
class DatapoolHelper;
|
||||
#include "DatapoolHelper.h"
|
||||
|
||||
#include <fsfw/housekeeping/GeneratesHousekeepingIF.h>
|
||||
|
||||
class HasDatapoolIF: public GeneratesHousekeepingIF {
|
||||
public:
|
||||
virtual ~HasDatapoolIF() = default;
|
||||
virtual DatapoolHelper* getDatapoolHelper() = 0;
|
||||
};
|
@ -1,217 +0,0 @@
|
||||
#include "fsfw/datapool/PoolDataSetBase.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "fsfw/datapool/ReadCommitIFAttorney.h"
|
||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||
|
||||
PoolDataSetBase::PoolDataSetBase(PoolVariableIF** registeredVariablesArray,
|
||||
const size_t maxFillCount)
|
||||
: registeredVariables(registeredVariablesArray), maxFillCount(maxFillCount) {}
|
||||
|
||||
PoolDataSetBase::~PoolDataSetBase() {}
|
||||
|
||||
ReturnValue_t PoolDataSetBase::registerVariable(PoolVariableIF* variable) {
|
||||
if (registeredVariables == nullptr) {
|
||||
/* Underlying container invalid */
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
if (state != States::STATE_SET_UNINITIALISED) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "DataSet::registerVariable: Call made in wrong position." << std::endl;
|
||||
#else
|
||||
sif::printError("DataSet::registerVariable: Call made in wrong position.");
|
||||
#endif
|
||||
return DataSetIF::DATA_SET_UNINITIALISED;
|
||||
}
|
||||
if (variable == nullptr) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "DataSet::registerVariable: Pool variable is nullptr." << std::endl;
|
||||
#else
|
||||
sif::printError("DataSet::registerVariable: Pool variable is nullptr.\n");
|
||||
#endif
|
||||
return DataSetIF::POOL_VAR_NULL;
|
||||
}
|
||||
if (fillCount >= maxFillCount) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "DataSet::registerVariable: DataSet is full." << std::endl;
|
||||
#else
|
||||
sif::printError("DataSet::registerVariable: DataSet is full.\n");
|
||||
#endif
|
||||
return DataSetIF::DATA_SET_FULL;
|
||||
}
|
||||
registeredVariables[fillCount] = variable;
|
||||
fillCount++;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t PoolDataSetBase::read(MutexIF::TimeoutType timeoutType, uint32_t lockTimeout) {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
ReturnValue_t error = result;
|
||||
if (state == States::STATE_SET_UNINITIALISED) {
|
||||
lockDataPool(timeoutType, lockTimeout);
|
||||
for (uint16_t count = 0; count < fillCount; count++) {
|
||||
result = readVariable(count);
|
||||
if (result != returnvalue::OK) {
|
||||
error = result;
|
||||
}
|
||||
}
|
||||
state = States::STATE_SET_WAS_READ;
|
||||
unlockDataPool();
|
||||
} else {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "PoolDataSetBase::read: Call made in wrong position. Don't forget to "
|
||||
"commit member datasets!"
|
||||
<< std::endl;
|
||||
#else
|
||||
sif::printWarning(
|
||||
"PoolDataSetBase::read: Call made in wrong position. Don't forget to "
|
||||
"commit member datasets!\n");
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
result = SET_WAS_ALREADY_READ;
|
||||
}
|
||||
|
||||
if (error != returnvalue::OK) {
|
||||
result = error;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16_t PoolDataSetBase::getFillCount() const { return fillCount; }
|
||||
|
||||
ReturnValue_t PoolDataSetBase::readVariable(uint16_t count) {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
if (registeredVariables[count] == nullptr) {
|
||||
/* Configuration error. */
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
/* These checks are often performed by the respective variable implementation too, but I guess
|
||||
a double check does not hurt. */
|
||||
if (registeredVariables[count]->getReadWriteMode() != PoolVariableIF::VAR_WRITE and
|
||||
registeredVariables[count]->getDataPoolId() != PoolVariableIF::NO_PARAMETER) {
|
||||
if (protectEveryReadCommitCall) {
|
||||
result =
|
||||
registeredVariables[count]->read(timeoutTypeForSingleVars, mutexTimeoutForSingleVars);
|
||||
} else {
|
||||
/* The readWithoutLock function is protected, so we use the attorney here */
|
||||
result = ReadCommitIFAttorney::readWithoutLock(registeredVariables[count]);
|
||||
}
|
||||
|
||||
if (result != returnvalue::OK) {
|
||||
result = INVALID_PARAMETER_DEFINITION;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t PoolDataSetBase::commit(MutexIF::TimeoutType timeoutType, uint32_t lockTimeout) {
|
||||
if (state == States::STATE_SET_WAS_READ) {
|
||||
handleAlreadyReadDatasetCommit(timeoutType, lockTimeout);
|
||||
return returnvalue::OK;
|
||||
} else {
|
||||
return handleUnreadDatasetCommit(timeoutType, lockTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
void PoolDataSetBase::handleAlreadyReadDatasetCommit(MutexIF::TimeoutType timeoutType,
|
||||
uint32_t lockTimeout) {
|
||||
lockDataPool(timeoutType, lockTimeout);
|
||||
for (uint16_t count = 0; count < fillCount; count++) {
|
||||
if ((registeredVariables[count]->getReadWriteMode() != PoolVariableIF::VAR_READ) and
|
||||
(registeredVariables[count]->getDataPoolId() != PoolVariableIF::NO_PARAMETER)) {
|
||||
if (protectEveryReadCommitCall) {
|
||||
registeredVariables[count]->commit(timeoutTypeForSingleVars, mutexTimeoutForSingleVars);
|
||||
} else {
|
||||
/* The commitWithoutLock function is protected, so we use the attorney here */
|
||||
ReadCommitIFAttorney::commitWithoutLock(registeredVariables[count]);
|
||||
}
|
||||
}
|
||||
}
|
||||
state = States::STATE_SET_UNINITIALISED;
|
||||
unlockDataPool();
|
||||
}
|
||||
|
||||
ReturnValue_t PoolDataSetBase::handleUnreadDatasetCommit(MutexIF::TimeoutType timeoutType,
|
||||
uint32_t lockTimeout) {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
lockDataPool(timeoutType, lockTimeout);
|
||||
for (uint16_t count = 0; count < fillCount; count++) {
|
||||
if ((registeredVariables[count]->getReadWriteMode() == PoolVariableIF::VAR_WRITE) and
|
||||
(registeredVariables[count]->getDataPoolId() != PoolVariableIF::NO_PARAMETER)) {
|
||||
if (protectEveryReadCommitCall) {
|
||||
result =
|
||||
registeredVariables[count]->commit(timeoutTypeForSingleVars, mutexTimeoutForSingleVars);
|
||||
} else {
|
||||
/* The commitWithoutLock function is protected, so we use the attorney here */
|
||||
ReadCommitIFAttorney::commitWithoutLock(registeredVariables[count]);
|
||||
}
|
||||
|
||||
} else if (registeredVariables[count]->getDataPoolId() != PoolVariableIF::NO_PARAMETER) {
|
||||
if (result != COMMITING_WITHOUT_READING) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "DataSet::commit(): commit-without-read call made "
|
||||
"with non write-only variable."
|
||||
<< std::endl;
|
||||
#endif
|
||||
result = COMMITING_WITHOUT_READING;
|
||||
}
|
||||
}
|
||||
}
|
||||
state = States::STATE_SET_UNINITIALISED;
|
||||
unlockDataPool();
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t PoolDataSetBase::lockDataPool(MutexIF::TimeoutType timeoutType,
|
||||
uint32_t lockTimeout) {
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t PoolDataSetBase::unlockDataPool() { return returnvalue::OK; }
|
||||
|
||||
ReturnValue_t PoolDataSetBase::serialize(uint8_t** buffer, size_t* size, const size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const {
|
||||
ReturnValue_t result = returnvalue::FAILED;
|
||||
for (uint16_t count = 0; count < fillCount; count++) {
|
||||
result = registeredVariables[count]->serialize(buffer, size, maxSize, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t PoolDataSetBase::deSerialize(const uint8_t** buffer, size_t* size,
|
||||
SerializeIF::Endianness streamEndianness) {
|
||||
ReturnValue_t result = returnvalue::FAILED;
|
||||
for (uint16_t count = 0; count < fillCount; count++) {
|
||||
result = registeredVariables[count]->deSerialize(buffer, size, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t PoolDataSetBase::getSerializedSize() const {
|
||||
uint32_t size = 0;
|
||||
for (uint16_t count = 0; count < fillCount; count++) {
|
||||
size += registeredVariables[count]->getSerializedSize();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void PoolDataSetBase::setContainer(PoolVariableIF** variablesContainer) {
|
||||
this->registeredVariables = variablesContainer;
|
||||
}
|
||||
|
||||
PoolVariableIF** PoolDataSetBase::getContainer() const { return registeredVariables; }
|
||||
|
||||
void PoolDataSetBase::setReadCommitProtectionBehaviour(bool protectEveryReadCommit,
|
||||
MutexIF::TimeoutType timeoutType,
|
||||
uint32_t mutexTimeout) {
|
||||
this->protectEveryReadCommitCall = protectEveryReadCommit;
|
||||
this->timeoutTypeForSingleVars = timeoutType;
|
||||
this->mutexTimeoutForSingleVars = mutexTimeout;
|
||||
}
|
@ -1,173 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOL_POOLDATASETBASE_H_
|
||||
#define FSFW_DATAPOOL_POOLDATASETBASE_H_
|
||||
|
||||
#include "PoolDataSetIF.h"
|
||||
#include "PoolVariableIF.h"
|
||||
#include "fsfw/ipc/MutexIF.h"
|
||||
#include "fsfw/serialize/SerializeIF.h"
|
||||
|
||||
/**
|
||||
* @brief The DataSetBase class manages a set of locally checked out variables.
|
||||
* @details
|
||||
* This class manages a list, where a set of local variables (or pool variables)
|
||||
* are registered. They are checked-out (i.e. their values are looked
|
||||
* up and copied) with the read call. After the user finishes working with the
|
||||
* pool variables, he can write back all variable values to the pool with
|
||||
* the commit call. The data set manages locking and freeing the data pool,
|
||||
* to ensure that all values are read and written back at once.
|
||||
*
|
||||
* An internal state manages usage of this class. Variables may only be
|
||||
* registered before the read call is made, and the commit call only
|
||||
* after the read call.
|
||||
*
|
||||
* If pool variables are writable and not committed until destruction
|
||||
* of the set, the DataSet class automatically sets the valid flag in the
|
||||
* data pool to invalid (without) changing the variable's value.
|
||||
*
|
||||
* The base class lockDataPool und unlockDataPool implementation are empty
|
||||
* and should be implemented to protect the underlying pool type.
|
||||
* @author Bastian Baetz
|
||||
* @ingroup data_pool
|
||||
*/
|
||||
class PoolDataSetBase : public PoolDataSetIF, public SerializeIF {
|
||||
public:
|
||||
/**
|
||||
* @brief Creates an empty dataset. Use registerVariable or
|
||||
* supply a pointer to this dataset to PoolVariable
|
||||
* initializations to register pool variables.
|
||||
*/
|
||||
PoolDataSetBase(PoolVariableIF** registeredVariablesArray, size_t maxFillCount);
|
||||
|
||||
/* Forbidden for now */
|
||||
PoolDataSetBase(const PoolDataSetBase& otherSet) = delete;
|
||||
const PoolDataSetBase& operator=(const PoolDataSetBase& otherSet) = delete;
|
||||
|
||||
~PoolDataSetBase() override;
|
||||
|
||||
/**
|
||||
* @brief The read call initializes reading out all registered variables.
|
||||
* It is mandatory to call commit after every read call!
|
||||
* @details
|
||||
* It iterates through the list of registered variables and calls all read()
|
||||
* functions of the registered pool variables (which read out their values
|
||||
* from the data pool) which are not write-only.
|
||||
* In case of an error (e.g. a wrong data type, or an invalid data pool id),
|
||||
* the operation is aborted and @c INVALID_PARAMETER_DEFINITION returned.
|
||||
*
|
||||
* The data pool is locked during the whole read operation and
|
||||
* freed afterwards. It is mandatory to call commit after a read call,
|
||||
* even if the read operation is not successful!
|
||||
* @return
|
||||
* - @c returnvalue::OK if all variables were read successfully.
|
||||
* - @c INVALID_PARAMETER_DEFINITION if a pool entry does not exist or there
|
||||
* is a type conflict.
|
||||
* - @c SET_WAS_ALREADY_READ if read() is called twice without calling
|
||||
* commit() in between
|
||||
*/
|
||||
virtual ReturnValue_t read(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
uint32_t lockTimeout = 20) override;
|
||||
/**
|
||||
* @brief The commit call initializes writing back the registered variables.
|
||||
* @details
|
||||
* It iterates through the list of registered variables and calls the
|
||||
* commit() method of the remaining registered variables (which write back
|
||||
* their values to the pool).
|
||||
*
|
||||
* The data pool is locked during the whole commit operation and
|
||||
* freed afterwards. The state changes to "was committed" after this operation.
|
||||
*
|
||||
* If the set does contain at least one variable which is not write-only
|
||||
* commit() can only be called after read(). If the set only contains
|
||||
* variables which are write only, commit() can be called without a
|
||||
* preceding read() call. Every read call must be followed by a commit call!
|
||||
* @return - @c returnvalue::OK if all variables were read successfully.
|
||||
* - @c COMMITING_WITHOUT_READING if set was not read yet and
|
||||
* contains non write-only variables
|
||||
*/
|
||||
virtual ReturnValue_t commit(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
uint32_t lockTimeout = 20) override;
|
||||
|
||||
/**
|
||||
* Register the passed pool variable instance into the data set.
|
||||
* @param variable
|
||||
* @return
|
||||
*/
|
||||
virtual ReturnValue_t registerVariable(PoolVariableIF* variable) override;
|
||||
|
||||
/**
|
||||
* Provides the means to lock the underlying data structure to ensure
|
||||
* thread-safety. Default implementation is empty
|
||||
* @return Always returns -@c returnvalue::OK
|
||||
*/
|
||||
virtual ReturnValue_t lockDataPool(
|
||||
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
uint32_t timeoutMs = 20) override;
|
||||
/**
|
||||
* Provides the means to unlock the underlying data structure to ensure
|
||||
* thread-safety. Default implementation is empty
|
||||
* @return Always returns -@c returnvalue::OK
|
||||
*/
|
||||
virtual ReturnValue_t unlockDataPool() override;
|
||||
|
||||
virtual uint16_t getFillCount() const override;
|
||||
|
||||
/* SerializeIF implementations */
|
||||
virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, const size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const override;
|
||||
virtual size_t getSerializedSize() const override;
|
||||
virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
|
||||
SerializeIF::Endianness streamEndianness) override;
|
||||
|
||||
/**
|
||||
* Can be used to individually protect every read and commit call.
|
||||
* @param protectEveryReadCommit
|
||||
* @param mutexTimeout
|
||||
*/
|
||||
void setReadCommitProtectionBehaviour(
|
||||
bool protectEveryReadCommit, MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
uint32_t mutexTimeout = 20);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief The fill_count attribute ensures that the variables
|
||||
* register in the correct array position and that the maximum
|
||||
* number of variables is not exceeded.
|
||||
*/
|
||||
uint16_t fillCount = 0;
|
||||
/**
|
||||
* States of the seet.
|
||||
*/
|
||||
enum class States {
|
||||
STATE_SET_UNINITIALISED, //!< DATA_SET_UNINITIALISED
|
||||
STATE_SET_WAS_READ //!< DATA_SET_WAS_READ
|
||||
};
|
||||
/**
|
||||
* @brief state manages the internal state of the data set,
|
||||
* which is important e.g. for the behavior on destruction.
|
||||
*/
|
||||
States state = States::STATE_SET_UNINITIALISED;
|
||||
|
||||
/**
|
||||
* @brief This array represents all pool variables registered in this set.
|
||||
* Child classes can use a static or dynamic container to create
|
||||
* an array of registered variables and assign the first entry here.
|
||||
*/
|
||||
PoolVariableIF** registeredVariables = nullptr;
|
||||
const size_t maxFillCount = 0;
|
||||
|
||||
void setContainer(PoolVariableIF** variablesContainer);
|
||||
PoolVariableIF** getContainer() const;
|
||||
|
||||
private:
|
||||
bool protectEveryReadCommitCall = false;
|
||||
MutexIF::TimeoutType timeoutTypeForSingleVars = MutexIF::TimeoutType::WAITING;
|
||||
uint32_t mutexTimeoutForSingleVars = 20;
|
||||
|
||||
ReturnValue_t readVariable(uint16_t count);
|
||||
void handleAlreadyReadDatasetCommit(
|
||||
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, uint32_t timeoutMs = 20);
|
||||
ReturnValue_t handleUnreadDatasetCommit(
|
||||
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, uint32_t timeoutMs = 20);
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOL_POOLDATASETBASE_H_ */
|
@ -1,34 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOL_POOLDATASETIF_H_
|
||||
#define FSFW_DATAPOOL_POOLDATASETIF_H_
|
||||
|
||||
#include "DataSetIF.h"
|
||||
#include "ReadCommitIF.h"
|
||||
|
||||
/**
|
||||
* @brief Extendes the DataSetIF by adding abstract functions to lock
|
||||
* and unlock a data pool and read/commit semantics.
|
||||
*/
|
||||
class PoolDataSetIF : virtual public DataSetIF, virtual public ReadCommitIF {
|
||||
public:
|
||||
virtual ~PoolDataSetIF(){};
|
||||
|
||||
/**
|
||||
* @brief Most underlying data structures will have a pool like structure
|
||||
* and will require a lock and unlock mechanism to ensure
|
||||
* thread-safety
|
||||
* @return Lock operation result
|
||||
*/
|
||||
virtual ReturnValue_t lockDataPool(
|
||||
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
uint32_t timeoutMs = 20) = 0;
|
||||
|
||||
/**
|
||||
* @brief Unlock call corresponding to the lock call.
|
||||
* @return Unlock operation result
|
||||
*/
|
||||
virtual ReturnValue_t unlockDataPool() = 0;
|
||||
|
||||
virtual bool isValid() const = 0;
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOL_POOLDATASETIF_H_ */
|
@ -1,102 +0,0 @@
|
||||
#include "fsfw/datapool/PoolEntry.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "fsfw/globalfunctions/arrayprinter.h"
|
||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||
|
||||
template <typename T>
|
||||
PoolEntry<T>::PoolEntry(uint8_t len, bool setValid) : length(len), valid(setValid) {
|
||||
this->address = new T[this->length]();
|
||||
std::memset(this->address, 0, this->getByteSize());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
PoolEntry<T>::PoolEntry(std::initializer_list<T> initValues, bool setValid)
|
||||
: length(static_cast<uint8_t>(initValues.size())), valid(setValid) {
|
||||
this->address = new T[this->length]();
|
||||
if (initValues.size() > 0) {
|
||||
std::copy(initValues.begin(), initValues.end(), this->address);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
PoolEntry<T>::PoolEntry(const T* initValue, uint8_t setLength, bool setValid)
|
||||
: length(setLength), valid(setValid) {
|
||||
this->address = new T[this->length]();
|
||||
if (initValue != nullptr) {
|
||||
std::memcpy(this->address, initValue, this->getByteSize());
|
||||
}
|
||||
}
|
||||
|
||||
// As the data pool is global, this dtor is only be called on program exit.
|
||||
// Warning! Never copy pool entries!
|
||||
template <typename T>
|
||||
PoolEntry<T>::~PoolEntry() {
|
||||
delete[] this->address;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
uint16_t PoolEntry<T>::getByteSize() {
|
||||
return (sizeof(T) * this->length);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
uint8_t PoolEntry<T>::getSize() {
|
||||
return this->length;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void* PoolEntry<T>::getRawData() {
|
||||
return this->address;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void PoolEntry<T>::setValid(bool isValid) {
|
||||
this->valid = isValid;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool PoolEntry<T>::getValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void PoolEntry<T>::print() {
|
||||
const char* validString = nullptr;
|
||||
if (valid) {
|
||||
validString = "Valid";
|
||||
} else {
|
||||
validString = "Invalid";
|
||||
}
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::info << "PoolEntry information." << std::endl;
|
||||
sif::info << "PoolEntry validity: " << validString << std::endl;
|
||||
#else
|
||||
sif::printInfo("PoolEntry information.\n");
|
||||
sif::printInfo("PoolEntry validity: %s\n", validString);
|
||||
#endif
|
||||
arrayprinter::print(reinterpret_cast<uint8_t*>(address), getByteSize());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T* PoolEntry<T>::getDataPtr() {
|
||||
return this->address;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Type PoolEntry<T>::getType() {
|
||||
return PodTypeConversion<T>::type;
|
||||
}
|
||||
|
||||
template class PoolEntry<uint8_t>;
|
||||
template class PoolEntry<uint16_t>;
|
||||
template class PoolEntry<uint32_t>;
|
||||
template class PoolEntry<uint64_t>;
|
||||
template class PoolEntry<int8_t>;
|
||||
template class PoolEntry<int16_t>;
|
||||
template class PoolEntry<int32_t>;
|
||||
template class PoolEntry<int64_t>;
|
||||
template class PoolEntry<float>;
|
||||
template class PoolEntry<double>;
|
@ -1,140 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOL_POOLENTRY_H_
|
||||
#define FSFW_DATAPOOL_POOLENTRY_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
|
||||
#include "PoolEntryIF.h"
|
||||
|
||||
/**
|
||||
* @brief This is a small helper class that defines a single data pool entry.
|
||||
* @details
|
||||
* The helper is used to store all information together with the data as a
|
||||
* single data pool entry. The content's type is defined by the template
|
||||
* argument.
|
||||
*
|
||||
* It is prepared for use with plain old data types, but may be
|
||||
* extended to complex types if necessary. It can be initialized with a
|
||||
* certain value, size and validity flag.
|
||||
*
|
||||
* It holds a pointer to the real data and offers methods to access this data
|
||||
* and to acquire additional information (such as validity and array/byte size).
|
||||
* It is NOT intended to be used outside DataPool implementations as it performs
|
||||
* dynamic memory allocation.
|
||||
*
|
||||
* @ingroup data_pool
|
||||
*/
|
||||
template <typename T>
|
||||
class PoolEntry : public PoolEntryIF {
|
||||
public:
|
||||
static_assert(not std::is_same<T, bool>::value,
|
||||
"Do not use boolean for the PoolEntry type, use uint8_t "
|
||||
"instead! The ECSS standard defines a boolean as a one bit "
|
||||
"field. Therefore it is preferred to store a boolean as an "
|
||||
"uint8_t");
|
||||
|
||||
PoolEntry(uint8_t len = 1, bool setValid = false);
|
||||
|
||||
/**
|
||||
* @brief In the classe's constructor, space is allocated on the heap and
|
||||
* potential initialization values are copied to that space.
|
||||
* @details
|
||||
* Not passing any arguments will initialize an non-array pool entry
|
||||
* with an initial invalid state and the value 0.
|
||||
* Please note that if an initializer list is passed, the length of the
|
||||
* initializer list needs to be correct for vector entries because
|
||||
* required allocated space will be deduced from the initializer list length
|
||||
* and the pool entry type.
|
||||
* @param initValue
|
||||
* Initializer list with values to initialize with, for example {0, 0} to
|
||||
* initialize the a pool entry of a vector with two entries to 0.
|
||||
* @param setValid
|
||||
* Sets the initialization flag. It is invalid by default.
|
||||
*/
|
||||
PoolEntry(std::initializer_list<T> initValue, bool setValid = false);
|
||||
|
||||
/**
|
||||
* @brief In the classe's constructor, space is allocated on the heap and
|
||||
* potential init values are copied to that space.
|
||||
* @param initValue
|
||||
* A pointer to the single value or array that holds the init value.
|
||||
* With the default value (nullptr), the entry is initalized with all 0.
|
||||
* @param setLength
|
||||
* Defines the array length of this entry.
|
||||
* @param setValid
|
||||
* Sets the initialization flag. It is invalid by default.
|
||||
*/
|
||||
PoolEntry(const T* initValue, uint8_t setLength = 1, bool setValid = false);
|
||||
|
||||
//! Explicitely deleted copy ctor, copying is not allowed.
|
||||
PoolEntry(const PoolEntry&) = delete;
|
||||
//! Explicitely deleted copy assignment, copying is not allowed.
|
||||
PoolEntry& operator=(const PoolEntry&) = delete;
|
||||
|
||||
/**
|
||||
* @brief The allocated memory for the variable is freed
|
||||
* in the destructor.
|
||||
* @details
|
||||
* As the data pool is global, this dtor is only called on program exit.
|
||||
* PoolEntries shall never be copied, as a copy might delete the variable
|
||||
* on the heap.
|
||||
*/
|
||||
~PoolEntry();
|
||||
|
||||
/**
|
||||
* Return typed pointer to start of data.
|
||||
* @return
|
||||
*/
|
||||
T* getDataPtr();
|
||||
|
||||
/**
|
||||
* @brief getSize returns the array size of the entry.
|
||||
* @details
|
||||
* For non-array pool entries return type size, for vector entries
|
||||
* return type size times the number of entries.
|
||||
*/
|
||||
uint8_t getSize();
|
||||
/**
|
||||
* @brief This operation returns the size in bytes.
|
||||
* @details The size is calculated by sizeof(type) * array_size.
|
||||
*/
|
||||
uint16_t getByteSize();
|
||||
/**
|
||||
* @brief This operation returns a the address pointer casted to void*.
|
||||
*/
|
||||
void* getRawData();
|
||||
/**
|
||||
* @brief This method allows to set the valid information
|
||||
* of the pool entry.
|
||||
*/
|
||||
void setValid(bool isValid);
|
||||
/**
|
||||
* @brief This method allows to get the valid information
|
||||
* of the pool entry.
|
||||
*/
|
||||
bool getValid();
|
||||
/**
|
||||
* @brief This is a debug method that prints all values and the valid
|
||||
* information to the screen. It prints all array entries in a row.
|
||||
*/
|
||||
void print();
|
||||
Type getType();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief This attribute stores the length information.
|
||||
*/
|
||||
uint8_t length;
|
||||
/**
|
||||
* @brief Here, the validity information for a variable is stored.
|
||||
* Every entry (single variable or vector) has one valid flag.
|
||||
*/
|
||||
uint8_t valid;
|
||||
/**
|
||||
* @brief This is the address pointing to the allocated memory.
|
||||
*/
|
||||
T* address;
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOL_POOLENTRY_H_ */
|
@ -1,28 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOL_POOLVARLIST_H_
|
||||
#define FSFW_DATAPOOL_POOLVARLIST_H_
|
||||
|
||||
#include "../datapool/PoolVariableIF.h"
|
||||
#include "../datapoolglob/GlobalPoolVariable.h"
|
||||
template <class T, uint8_t n_var>
|
||||
class PoolVarList {
|
||||
private:
|
||||
GlobPoolVar<T> variables[n_var];
|
||||
|
||||
public:
|
||||
PoolVarList(const uint32_t set_id[n_var], DataSetIF* dataSet,
|
||||
PoolVariableIF::ReadWriteMode_t setReadWriteMode) {
|
||||
// I really should have a look at the new init list c++ syntax.
|
||||
if (dataSet == NULL) {
|
||||
return;
|
||||
}
|
||||
for (uint8_t count = 0; count < n_var; count++) {
|
||||
variables[count].dataPoolId = set_id[count];
|
||||
variables[count].readWriteMode = setReadWriteMode;
|
||||
dataSet->registerVariable(&variables[count]);
|
||||
}
|
||||
}
|
||||
|
||||
GlobPoolVar<T>& operator[](int i) { return variables[i]; }
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOL_POOLVARLIST_H_ */
|
@ -1,62 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOL_POOLVARIABLEIF_H_
|
||||
#define FSFW_DATAPOOL_POOLVARIABLEIF_H_
|
||||
|
||||
#include "../returnvalues/returnvalue.h"
|
||||
#include "../serialize/SerializeIF.h"
|
||||
#include "ReadCommitIF.h"
|
||||
|
||||
/**
|
||||
* @brief This interface is used to control data pool
|
||||
* variable representations.
|
||||
* @details
|
||||
* To securely handle data pool variables, all pool entries are locally
|
||||
* managed by data pool variable access classes, which are called pool
|
||||
* variables. To ensure a common state of a set of variables needed in a
|
||||
* function, these local pool variables again are managed by other classes,
|
||||
* like the DataSet classes. This interface provides unified access to
|
||||
* local pool variables for such manager classes.
|
||||
* @author Bastian Baetz
|
||||
* @ingroup data_pool
|
||||
*/
|
||||
class PoolVariableIF : public SerializeIF, public ReadCommitIF {
|
||||
public:
|
||||
static constexpr uint8_t INTERFACE_ID = CLASS_ID::POOL_VARIABLE_IF;
|
||||
static constexpr ReturnValue_t INVALID_READ_WRITE_MODE = MAKE_RETURN_CODE(0xA0);
|
||||
static constexpr ReturnValue_t INVALID_POOL_ENTRY = MAKE_RETURN_CODE(0xA1);
|
||||
|
||||
static constexpr bool VALID = 1;
|
||||
static constexpr bool INVALID = 0;
|
||||
static constexpr uint32_t NO_PARAMETER = 0xffffffff;
|
||||
|
||||
enum ReadWriteMode_t { VAR_READ, VAR_WRITE, VAR_READ_WRITE };
|
||||
|
||||
/**
|
||||
* @brief This is an empty virtual destructor,
|
||||
* as it is proposed for C++ interfaces.
|
||||
*/
|
||||
virtual ~PoolVariableIF() {}
|
||||
/**
|
||||
* @brief This method returns if the variable is write-only,
|
||||
* read-write or read-only.
|
||||
*/
|
||||
virtual ReadWriteMode_t getReadWriteMode() const = 0;
|
||||
virtual void setReadWriteMode(ReadWriteMode_t newMode) = 0;
|
||||
|
||||
/**
|
||||
* @brief This operation shall return the data pool id of the variable.
|
||||
*/
|
||||
virtual uint32_t getDataPoolId() const = 0;
|
||||
/**
|
||||
* @brief With this call, the valid information of the
|
||||
* variable is returned.
|
||||
*/
|
||||
virtual bool isValid() const = 0;
|
||||
/**
|
||||
* @brief With this call, the valid information of the variable is set.
|
||||
*/
|
||||
virtual void setValid(bool validity) = 0;
|
||||
};
|
||||
|
||||
using pool_rwm_t = PoolVariableIF::ReadWriteMode_t;
|
||||
|
||||
#endif /* FSFW_DATAPOOL_POOLVARIABLEIF_H_ */
|
@ -1,27 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOL_READCOMMITIF_H_
|
||||
#define FSFW_DATAPOOL_READCOMMITIF_H_
|
||||
|
||||
#include "../ipc/MutexIF.h"
|
||||
#include "../returnvalues/returnvalue.h"
|
||||
|
||||
/**
|
||||
* @brief Common interface for all software objects which employ read-commit
|
||||
* semantics.
|
||||
*/
|
||||
class ReadCommitIF {
|
||||
friend class ReadCommitIFAttorney;
|
||||
|
||||
public:
|
||||
virtual ~ReadCommitIF() {}
|
||||
virtual ReturnValue_t read(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) = 0;
|
||||
virtual ReturnValue_t commit(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) = 0;
|
||||
|
||||
protected:
|
||||
/* Optional and protected because this is interesting for classes grouping members with commit
|
||||
and read semantics where the lock is only necessary once. */
|
||||
virtual ReturnValue_t readWithoutLock() { return read(MutexIF::TimeoutType::WAITING, 20); }
|
||||
|
||||
virtual ReturnValue_t commitWithoutLock() { return commit(MutexIF::TimeoutType::WAITING, 20); }
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOL_READCOMMITIF_H_ */
|
@ -1,30 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOL_READCOMMITIFATTORNEY_H_
|
||||
#define FSFW_DATAPOOL_READCOMMITIFATTORNEY_H_
|
||||
|
||||
#include <fsfw/datapool/ReadCommitIF.h>
|
||||
#include <fsfw/returnvalues/returnvalue.h>
|
||||
|
||||
/**
|
||||
* @brief This class determines which members are allowed to access protected members
|
||||
* of the ReadCommitIF.
|
||||
*/
|
||||
class ReadCommitIFAttorney {
|
||||
private:
|
||||
static ReturnValue_t readWithoutLock(ReadCommitIF* readCommitIF) {
|
||||
if (readCommitIF == nullptr) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
return readCommitIF->readWithoutLock();
|
||||
}
|
||||
|
||||
static ReturnValue_t commitWithoutLock(ReadCommitIF* readCommitIF) {
|
||||
if (readCommitIF == nullptr) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
return readCommitIF->commitWithoutLock();
|
||||
}
|
||||
|
||||
friend class PoolDataSetBase;
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOL_READCOMMITIFATTORNEY_H_ */
|
@ -1,16 +0,0 @@
|
||||
#ifndef FRAMEWORK_DATAPOOL_SHAREDDATASETIF_H_
|
||||
#define FRAMEWORK_DATAPOOL_SHAREDDATASETIF_H_
|
||||
|
||||
#include "PoolDataSetIF.h"
|
||||
|
||||
class SharedDataSetIF {
|
||||
public:
|
||||
virtual ~SharedDataSetIF(){};
|
||||
|
||||
private:
|
||||
virtual ReturnValue_t lockDataset(MutexIF::TimeoutType timeoutType,
|
||||
dur_millis_t mutexTimeout) = 0;
|
||||
virtual ReturnValue_t unlockDataset() = 0;
|
||||
};
|
||||
|
||||
#endif /* FRAMEWORK_DATAPOOL_SHAREDDATASETIF_H_ */
|
@ -1,11 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_
|
||||
#define FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_
|
||||
|
||||
/* Collected related headers */
|
||||
#include "fsfw/datapoollocal/LocalDataSet.h"
|
||||
#include "fsfw/datapoollocal/LocalPoolVariable.h"
|
||||
#include "fsfw/datapoollocal/LocalPoolVector.h"
|
||||
#include "fsfw/datapoollocal/SharedLocalDataSet.h"
|
||||
#include "fsfw/datapoollocal/StaticLocalDataSet.h"
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_ */
|
@ -12,7 +12,7 @@ ChildHandlerBase::ChildHandlerBase(object_id_t setObjectId, object_id_t deviceCo
|
||||
parentId(parent),
|
||||
childHandlerFdir(setObjectId) {
|
||||
this->setHkDestination(hkDestination);
|
||||
this->setThermalStateRequestPoolIds(thermalStatePoolId, thermalRequestPoolId);
|
||||
this->setThermalStateRequestPoolIds(); //TODO
|
||||
}
|
||||
|
||||
ChildHandlerBase::~ChildHandlerBase() {}
|
||||
|
@ -1,10 +1,9 @@
|
||||
#include "fsfw/devicehandlers/DeviceHandlerBase.h"
|
||||
|
||||
#include "fsfw/datapoollocal/LocalPoolVariable.h"
|
||||
#include "DeviceHandlerBase.h"
|
||||
#include "fsfw/devicehandlers/AcceptsDeviceResponsesIF.h"
|
||||
#include "fsfw/devicehandlers/DeviceTmReportingWrapper.h"
|
||||
#include "fsfw/globalfunctions/CRC.h"
|
||||
#include "fsfw/housekeeping/HousekeepingMessage.h"
|
||||
#include "fsfw/ipc/MessageQueueMessage.h"
|
||||
#include "fsfw/ipc/QueueFactory.h"
|
||||
#include "fsfw/objectmanager/ObjectManager.h"
|
||||
@ -32,7 +31,7 @@ DeviceHandlerBase::DeviceHandlerBase(object_id_t setObjectId, object_id_t device
|
||||
modeHelper(this),
|
||||
parameterHelper(this),
|
||||
actionHelper(this, nullptr),
|
||||
poolManager(this, nullptr),
|
||||
datapoolHelper(this),
|
||||
childTransitionFailure(returnvalue::OK),
|
||||
fdirInstance(fdirInstance),
|
||||
defaultFDIRUsed(fdirInstance == nullptr),
|
||||
@ -58,12 +57,7 @@ void DeviceHandlerBase::setHkDestination(object_id_t hkDestination) {
|
||||
this->hkDestination = hkDestination;
|
||||
}
|
||||
|
||||
void DeviceHandlerBase::setThermalStateRequestPoolIds(lp_id_t thermalStatePoolId,
|
||||
lp_id_t heaterRequestPoolId,
|
||||
uint32_t thermalSetId) {
|
||||
thermalSet =
|
||||
new DeviceHandlerThermalSet(this, thermalSetId, thermalStatePoolId, heaterRequestPoolId);
|
||||
}
|
||||
void DeviceHandlerBase::setThermalStateRequestPoolIds() {}
|
||||
|
||||
DeviceHandlerBase::~DeviceHandlerBase() {
|
||||
if (comCookie != nullptr) {
|
||||
@ -114,9 +108,6 @@ ReturnValue_t DeviceHandlerBase::performOperation(uint8_t counter) {
|
||||
break;
|
||||
case CommunicationAction::GET_READ:
|
||||
doGetRead();
|
||||
/* This will be performed after datasets have been updated by the
|
||||
custom device implementation. */
|
||||
poolManager.performHkOperation();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -205,6 +196,10 @@ ReturnValue_t DeviceHandlerBase::initialize() {
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
result = datapoolHelper.initialize();
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
result = fdirInstance->initialize();
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
@ -215,22 +210,22 @@ ReturnValue_t DeviceHandlerBase::initialize() {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = poolManager.initialize(commandQueue);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
// result = poolManager.initialize(commandQueue);
|
||||
// if (result != returnvalue::OK) {
|
||||
// return result;
|
||||
// }
|
||||
|
||||
fillCommandAndReplyMap();
|
||||
|
||||
if (thermalSet != nullptr) {
|
||||
// Set temperature target state to NON_OP.
|
||||
result = thermalSet->read();
|
||||
if (result == returnvalue::OK) {
|
||||
thermalSet->heaterRequest.value = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL;
|
||||
thermalSet->heaterRequest.setValid(true);
|
||||
thermalSet->commit();
|
||||
}
|
||||
}
|
||||
// if (thermalSet != nullptr) { //TODO
|
||||
// // Set temperature target state to NON_OP.
|
||||
// result = thermalSet->read();
|
||||
// if (result == returnvalue::OK) {
|
||||
// thermalSet->heaterRequest.value = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL;
|
||||
// thermalSet->heaterRequest.setValid(true);
|
||||
// thermalSet->commit();
|
||||
// }
|
||||
// }
|
||||
|
||||
return returnvalue::OK;
|
||||
}
|
||||
@ -290,10 +285,10 @@ void DeviceHandlerBase::readCommandQueue() {
|
||||
return;
|
||||
}
|
||||
|
||||
result = poolManager.handleHousekeepingMessage(&command);
|
||||
if (result == returnvalue::OK) {
|
||||
return;
|
||||
}
|
||||
// result = poolManager.handleHousekeepingMessage(&command); //TODO
|
||||
// if (result == returnvalue::OK) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
result = handleDeviceHandlerMessage(&command);
|
||||
if (result == returnvalue::OK) {
|
||||
@ -359,6 +354,8 @@ void DeviceHandlerBase::doStateMachine() {
|
||||
if ((switchState == PowerSwitchIF::SWITCH_ON) || (switchState == NO_SWITCH)) {
|
||||
// NOTE: TransitionSourceMode and -SubMode are set by handleCommandedModeTransition
|
||||
childTransitionFailure = CHILD_TIMEOUT;
|
||||
transitionSourceMode = _MODE_SHUT_DOWN;
|
||||
transitionSourceSubMode = SUBMODE_NONE;
|
||||
setMode(_MODE_START_UP);
|
||||
callChildStatemachine();
|
||||
}
|
||||
@ -418,7 +415,7 @@ ReturnValue_t DeviceHandlerBase::isModeCombinationValid(Mode_t mode, Submode_t s
|
||||
}
|
||||
|
||||
ReturnValue_t DeviceHandlerBase::insertInCommandAndReplyMap(
|
||||
DeviceCommandId_t deviceCommand, uint16_t maxDelayCycles, LocalPoolDataSetBase* replyDataSet,
|
||||
DeviceCommandId_t deviceCommand, uint16_t maxDelayCycles, void* replyDataSet /*TODO*/,
|
||||
size_t replyLen, bool periodic, bool hasDifferentReplyId, DeviceCommandId_t replyId,
|
||||
Countdown* countdown) {
|
||||
// No need to check, as we may try to insert multiple times.
|
||||
@ -433,14 +430,14 @@ ReturnValue_t DeviceHandlerBase::insertInCommandAndReplyMap(
|
||||
|
||||
ReturnValue_t DeviceHandlerBase::insertInReplyMap(DeviceCommandId_t replyId,
|
||||
uint16_t maxDelayCycles,
|
||||
LocalPoolDataSetBase* dataSet, size_t replyLen,
|
||||
void* dataSet/*TODO*/, size_t replyLen,
|
||||
bool periodic, Countdown* countdown) {
|
||||
DeviceReplyInfo info;
|
||||
info.maxDelayCycles = maxDelayCycles;
|
||||
info.periodic = periodic;
|
||||
info.delayCycles = 0;
|
||||
info.replyLen = replyLen;
|
||||
info.dataSet = dataSet;
|
||||
//info.dataSet = dataSet;
|
||||
info.command = deviceCommandMap.end();
|
||||
info.countdown = countdown;
|
||||
auto resultPair = deviceReplyMap.emplace(replyId, info);
|
||||
@ -458,7 +455,7 @@ ReturnValue_t DeviceHandlerBase::insertInCommandMap(DeviceCommandId_t deviceComm
|
||||
info.expectedReplies = 0;
|
||||
info.isExecuting = false;
|
||||
info.sendReplyTo = NO_COMMANDER;
|
||||
info.useAlternativeReplyId = alternativeReplyId;
|
||||
info.useAlternativeReplyId = useAlternativeReply;
|
||||
info.alternativeReplyId = alternativeReplyId;
|
||||
auto resultPair = deviceCommandMap.emplace(deviceCommand, info);
|
||||
if (resultPair.second) {
|
||||
@ -517,31 +514,31 @@ ReturnValue_t DeviceHandlerBase::updatePeriodicReply(bool enable, DeviceCommandI
|
||||
if (enable) {
|
||||
info->active = true;
|
||||
if (info->countdown != nullptr) {
|
||||
info->delayCycles = info->maxDelayCycles;
|
||||
} else {
|
||||
info->countdown->resetTimer();
|
||||
} else {
|
||||
info->delayCycles = info->maxDelayCycles;
|
||||
}
|
||||
} else {
|
||||
info->active = false;
|
||||
if (info->countdown != nullptr) {
|
||||
info->delayCycles = 0;
|
||||
} else {
|
||||
info->countdown->timeOut();
|
||||
} else {
|
||||
info->delayCycles = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t DeviceHandlerBase::setReplyDataset(DeviceCommandId_t replyId,
|
||||
LocalPoolDataSetBase* dataSet) {
|
||||
auto replyIter = deviceReplyMap.find(replyId);
|
||||
if (replyIter == deviceReplyMap.end()) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
replyIter->second.dataSet = dataSet;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
// ReturnValue_t DeviceHandlerBase::setReplyDataset(DeviceCommandId_t replyId,
|
||||
// void* dataSet/*TODO*/) {
|
||||
// auto replyIter = deviceReplyMap.find(replyId);
|
||||
// if (replyIter == deviceReplyMap.end()) {
|
||||
// return returnvalue::FAILED;
|
||||
// }
|
||||
// replyIter->second.dataSet = dataSet;
|
||||
// return returnvalue::OK;
|
||||
// }
|
||||
|
||||
void DeviceHandlerBase::callChildStatemachine() {
|
||||
if (mode == _MODE_START_UP) {
|
||||
@ -577,15 +574,15 @@ void DeviceHandlerBase::setMode(Mode_t newMode, uint8_t newSubmode) {
|
||||
}
|
||||
Clock::getUptime(&timeoutStart);
|
||||
|
||||
if (mode == MODE_OFF and thermalSet != nullptr) {
|
||||
ReturnValue_t result = thermalSet->read();
|
||||
if (result == returnvalue::OK) {
|
||||
if (thermalSet->heaterRequest.value != ThermalComponentIF::STATE_REQUEST_IGNORE) {
|
||||
thermalSet->heaterRequest.value = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL;
|
||||
}
|
||||
thermalSet->heaterRequest.commit(PoolVariableIF::VALID);
|
||||
}
|
||||
}
|
||||
// if (mode == MODE_OFF and thermalSet != nullptr) { //TODO
|
||||
// ReturnValue_t result = thermalSet->read();
|
||||
// if (result == returnvalue::OK) {
|
||||
// if (thermalSet->heaterRequest.value != ThermalComponentIF::STATE_REQUEST_IGNORE) {
|
||||
// thermalSet->heaterRequest.value = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL;
|
||||
// }
|
||||
// thermalSet->heaterRequest.commit(PoolVariableIF::VALID);
|
||||
// }
|
||||
// }
|
||||
/* TODO: This will probably be done by the LocalDataPoolManager now */
|
||||
// changeHK(mode, submode, true);
|
||||
}
|
||||
@ -1074,14 +1071,14 @@ ReturnValue_t DeviceHandlerBase::checkModeCommand(Mode_t commandedMode, Submode_
|
||||
}
|
||||
|
||||
if ((commandedMode == MODE_ON) && (mode == MODE_OFF) and (thermalSet != nullptr)) {
|
||||
ReturnValue_t result = thermalSet->read();
|
||||
if (result == returnvalue::OK) {
|
||||
if ((thermalSet->heaterRequest.value != ThermalComponentIF::STATE_REQUEST_IGNORE) and
|
||||
(not ThermalComponentIF::isOperational(thermalSet->thermalState.value))) {
|
||||
triggerEvent(ThermalComponentIF::TEMP_NOT_IN_OP_RANGE, thermalSet->thermalState.value);
|
||||
return NON_OP_TEMPERATURE;
|
||||
}
|
||||
}
|
||||
// ReturnValue_t result = thermalSet->read(); //TODO
|
||||
// if (result == returnvalue::OK) {
|
||||
// if ((thermalSet->heaterRequest.value != ThermalComponentIF::STATE_REQUEST_IGNORE) and
|
||||
// (not ThermalComponentIF::isOperational(thermalSet->thermalState.value))) {
|
||||
// triggerEvent(ThermalComponentIF::TEMP_NOT_IN_OP_RANGE, thermalSet->thermalState.value);
|
||||
// return NON_OP_TEMPERATURE;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
return isModeCombinationValid(commandedMode, commandedSubmode);
|
||||
@ -1133,15 +1130,15 @@ void DeviceHandlerBase::handleTransitionToOnMode(Mode_t commandedMode, Submode_t
|
||||
// need to call it twice
|
||||
childTransitionDelay = getTransitionDelayMs(_MODE_START_UP, MODE_ON);
|
||||
triggerEvent(CHANGING_MODE, commandedMode, commandedSubmode);
|
||||
if (thermalSet != nullptr) {
|
||||
ReturnValue_t result = thermalSet->read();
|
||||
if (result == returnvalue::OK) {
|
||||
if (thermalSet->heaterRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) {
|
||||
thermalSet->heaterRequest = ThermalComponentIF::STATE_REQUEST_OPERATIONAL;
|
||||
thermalSet->commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (thermalSet != nullptr) { //TODO
|
||||
// ReturnValue_t result = thermalSet->read();
|
||||
// if (result == returnvalue::OK) {
|
||||
// if (thermalSet->heaterRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) {
|
||||
// thermalSet->heaterRequest = ThermalComponentIF::STATE_REQUEST_OPERATIONAL;
|
||||
// thermalSet->commit();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
} else {
|
||||
setTransition(MODE_ON, commandedSubmode);
|
||||
}
|
||||
@ -1311,22 +1308,23 @@ void DeviceHandlerBase::handleDeviceTm(const SerializeIF& dataSet, DeviceCommand
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t DeviceHandlerBase::executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
|
||||
const uint8_t* data, size_t size) {
|
||||
ActionHelper* DeviceHandlerBase::getActionHelper() { return &actionHelper; }
|
||||
|
||||
ReturnValue_t DeviceHandlerBase::executeAction(Action* action) {
|
||||
ReturnValue_t result = acceptExternalDeviceCommands();
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
DeviceCommandMap::iterator iter = deviceCommandMap.find(actionId);
|
||||
DeviceCommandMap::iterator iter = deviceCommandMap.find(action->getId());
|
||||
if (iter == deviceCommandMap.end()) {
|
||||
result = COMMAND_NOT_SUPPORTED;
|
||||
} else if (iter->second.isExecuting) {
|
||||
result = COMMAND_ALREADY_SENT;
|
||||
} else {
|
||||
iter->second.sendReplyTo = commandedBy;
|
||||
result = buildCommandFromCommand(actionId, data, size);
|
||||
result = action->handle();
|
||||
}
|
||||
if (result == returnvalue::OK) {
|
||||
iter->second.sendReplyTo = action->commandedBy;
|
||||
iter->second.isExecuting = true;
|
||||
cookieInfo.pendingCommand = iter;
|
||||
cookieInfo.state = COOKIE_WRITE_READY;
|
||||
@ -1456,24 +1454,12 @@ void DeviceHandlerBase::debugInterface(uint8_t positionTracker, object_id_t obje
|
||||
|
||||
void DeviceHandlerBase::performOperationHook() {}
|
||||
|
||||
ReturnValue_t DeviceHandlerBase::initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
|
||||
LocalDataPoolManager& poolManager) {
|
||||
if (thermalSet != nullptr) {
|
||||
localDataPoolMap.emplace(thermalSet->thermalStatePoolId,
|
||||
new PoolEntry<DeviceHandlerIF::dh_thermal_state_t>);
|
||||
localDataPoolMap.emplace(thermalSet->heaterRequestPoolId,
|
||||
new PoolEntry<DeviceHandlerIF::dh_heater_request_t>);
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t DeviceHandlerBase::initializeAfterTaskCreation() {
|
||||
// In this function, the task handle should be valid if the task
|
||||
// was implemented correctly. We still check to be 1000 % sure :-)
|
||||
if (executingTask != nullptr) {
|
||||
pstIntervalMs = executingTask->getPeriodMs();
|
||||
}
|
||||
this->poolManager.initializeAfterTaskCreation();
|
||||
|
||||
if (setStartupImmediately) {
|
||||
startTransition(MODE_ON, SUBMODE_NONE);
|
||||
@ -1481,21 +1467,10 @@ ReturnValue_t DeviceHandlerBase::initializeAfterTaskCreation() {
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
LocalPoolDataSetBase* DeviceHandlerBase::getDataSetHandle(sid_t sid) {
|
||||
auto iter = deviceReplyMap.find(sid.ownerSetId);
|
||||
if (iter != deviceReplyMap.end()) {
|
||||
return iter->second.dataSet;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
object_id_t DeviceHandlerBase::getObjectId() const { return SystemObject::getObjectId(); }
|
||||
|
||||
void DeviceHandlerBase::setStartUpImmediately() { this->setStartupImmediately = true; }
|
||||
|
||||
dur_millis_t DeviceHandlerBase::getPeriodicOperationFrequency() const { return pstIntervalMs; }
|
||||
|
||||
DeviceCommandId_t DeviceHandlerBase::getPendingCommand() const {
|
||||
if (cookieInfo.pendingCommand != deviceCommandMap.end()) {
|
||||
return cookieInfo.pendingCommand->first;
|
||||
@ -1504,13 +1479,19 @@ DeviceCommandId_t DeviceHandlerBase::getPendingCommand() const {
|
||||
}
|
||||
|
||||
void DeviceHandlerBase::setNormalDatapoolEntriesInvalid() {
|
||||
for (const auto& reply : deviceReplyMap) {
|
||||
if (reply.second.dataSet != nullptr) {
|
||||
reply.second.dataSet->setValidity(false, true);
|
||||
}
|
||||
}
|
||||
// for (const auto& reply : deviceReplyMap) {
|
||||
// if (reply.second.dataSet != nullptr) {
|
||||
// PoolReadGuard pg(reply.second.dataSet);
|
||||
// if (pg.getReadResult() == returnvalue::OK) {
|
||||
// reply.second.dataSet->setValidity(false, true);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
DatapoolHelper* DeviceHandlerBase::getDatapoolHelper() { return &datapoolHelper; }
|
||||
HousekeepingHelper* DeviceHandlerBase::getHousekeepingHelper() { return &datapoolHelper; }
|
||||
|
||||
void DeviceHandlerBase::printWarningOrError(sif::OutputTypes errorType, const char* functionName,
|
||||
ReturnValue_t errorCode, const char* errorPrint) {
|
||||
if (errorPrint == nullptr) {
|
||||
@ -1551,8 +1532,6 @@ void DeviceHandlerBase::printWarningOrError(sif::OutputTypes errorType, const ch
|
||||
}
|
||||
}
|
||||
|
||||
LocalDataPoolManager* DeviceHandlerBase::getHkManagerHandle() { return &poolManager; }
|
||||
|
||||
MessageQueueId_t DeviceHandlerBase::getCommanderQueueId(DeviceCommandId_t replyId) const {
|
||||
auto commandIter = deviceCommandMap.find(replyId);
|
||||
if (commandIter == deviceCommandMap.end()) {
|
||||
@ -1560,3 +1539,5 @@ MessageQueueId_t DeviceHandlerBase::getCommanderQueueId(DeviceCommandId_t replyI
|
||||
}
|
||||
return commandIter->second.sendReplyTo;
|
||||
}
|
||||
|
||||
ModeHelper const* DeviceHandlerBase::getModeHelper() const { return &modeHelper; }
|
||||
|
@ -6,12 +6,11 @@
|
||||
#include "DeviceCommunicationIF.h"
|
||||
#include "DeviceHandlerFailureIsolation.h"
|
||||
#include "DeviceHandlerIF.h"
|
||||
#include "DeviceHandlerThermalSet.h"
|
||||
#include "fsfw/action/ActionHelper.h"
|
||||
#include "fsfw/action/HasActionsIF.h"
|
||||
#include "fsfw/datapool/PoolVariableIF.h"
|
||||
#include "fsfw/datapoollocal/HasLocalDataPoolIF.h"
|
||||
#include "fsfw/datapoollocal/LocalDataPoolManager.h"
|
||||
// #include "fsfw/datapool/PoolVariableIF.h"
|
||||
#include "fsfw/datapool/HasDatapoolIF.h"
|
||||
// #include "fsfw/datapoollocal/LocalDataPoolManager.h"
|
||||
#include "fsfw/health/HealthHelper.h"
|
||||
#include "fsfw/ipc/MessageQueueIF.h"
|
||||
#include "fsfw/modes/HasModesIF.h"
|
||||
@ -84,7 +83,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
public HasHealthIF,
|
||||
public HasActionsIF,
|
||||
public ReceivesParameterMessagesIF,
|
||||
public HasLocalDataPoolIF {
|
||||
public HasDatapoolIF {
|
||||
friend void(Factory::setStaticFrameworkObjectIds)();
|
||||
|
||||
public:
|
||||
@ -113,10 +112,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
* @param thermalStatePoolId
|
||||
* @param thermalRequestPoolId
|
||||
*/
|
||||
void setThermalStateRequestPoolIds(
|
||||
lp_id_t thermalStatePoolId = DeviceHandlerIF::DEFAULT_THERMAL_STATE_POOL_ID,
|
||||
lp_id_t thermalRequestPoolId = DeviceHandlerIF::DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID,
|
||||
uint32_t thermalSetId = DeviceHandlerIF::DEFAULT_THERMAL_SET_ID);
|
||||
void setThermalStateRequestPoolIds(); // TODO
|
||||
/**
|
||||
* @brief Helper function to ease device handler development.
|
||||
* This will instruct the transition to MODE_ON immediately
|
||||
@ -200,13 +196,14 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
*/
|
||||
virtual void setParentQueue(MessageQueueId_t parentQueueId);
|
||||
|
||||
/** @brief Implementation required for HasActionIF */
|
||||
ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
|
||||
const uint8_t *data, size_t size) override;
|
||||
/** @brief Implementations required for HasActionIF */
|
||||
ActionHelper *getActionHelper() override;
|
||||
ReturnValue_t executeAction(Action *action) override;
|
||||
|
||||
Mode_t getTransitionSourceMode() const;
|
||||
Submode_t getTransitionSourceSubMode() const;
|
||||
virtual void getMode(Mode_t *mode, Submode_t *submode);
|
||||
void getMode(Mode_t *mode, Submode_t *submode) override;
|
||||
ModeHelper const *getModeHelper() const override;
|
||||
HealthState getHealth();
|
||||
ReturnValue_t setHealth(HealthState health);
|
||||
virtual ReturnValue_t getParameter(uint8_t domainId, uint8_t uniqueId,
|
||||
@ -310,35 +307,10 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
* - Anything else triggers an even with the returnvalue as a parameter
|
||||
*/
|
||||
virtual ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t *id) = 0;
|
||||
/**
|
||||
* @brief Build a device command packet from data supplied by a direct
|
||||
* command (PUS Service 8)
|
||||
* @details
|
||||
* This will be called if an functional command via PUS Service 8 is received and is
|
||||
* the primary interface for functional command instead of #executeAction for users. The
|
||||
* supplied ActionId_t action ID will be converted to a DeviceCommandId_t command ID after
|
||||
* an internal check whether the action ID is a key in the device command map.
|
||||
*
|
||||
* #rawPacket and #rawPacketLen should be set by this method to the packet to be sent.
|
||||
* The existence of the command in the command map and the command size check against 0 are
|
||||
* done by the base class.
|
||||
*
|
||||
* @param deviceCommand The command to build, already checked against deviceCommandMap
|
||||
* @param commandData Pointer to the data from the direct command
|
||||
* @param commandDataLen Length of commandData
|
||||
* @return
|
||||
* - @c returnvalue::OK to send command after #rawPacket and #rawPacketLen
|
||||
* have been set.
|
||||
* - @c HasActionsIF::EXECUTION_COMPLETE to generate a finish reply immediately. This can
|
||||
* be used if no reply is expected
|
||||
* - Anything else triggers an event with the return code as a parameter as well as a
|
||||
* step reply failed with the return code
|
||||
*/
|
||||
virtual ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand,
|
||||
const uint8_t *commandData,
|
||||
size_t commandDataLen) = 0;
|
||||
|
||||
/* Reply handling */
|
||||
// TODO add way to say, not enough data in buffer, try again later
|
||||
// ComIF needs to decide if buffer will be appended or overwritten
|
||||
/**
|
||||
* @brief Scans a buffer for a valid reply.
|
||||
* @details
|
||||
@ -454,7 +426,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
* - @c returnvalue::FAILED else.
|
||||
*/
|
||||
ReturnValue_t insertInCommandAndReplyMap(DeviceCommandId_t deviceCommand, uint16_t maxDelayCycles,
|
||||
LocalPoolDataSetBase *replyDataSet = nullptr,
|
||||
void *replyDataSet = nullptr,
|
||||
size_t replyLen = 0, bool periodic = false,
|
||||
bool hasDifferentReplyId = false,
|
||||
DeviceCommandId_t replyId = 0,
|
||||
@ -475,7 +447,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
* - @c returnvalue::FAILED else.
|
||||
*/
|
||||
ReturnValue_t insertInReplyMap(DeviceCommandId_t deviceCommand, uint16_t maxDelayCycles,
|
||||
LocalPoolDataSetBase *dataSet = nullptr, size_t replyLen = 0,
|
||||
void *dataSet = nullptr, size_t replyLen = 0,
|
||||
bool periodic = false, Countdown *countdown = nullptr);
|
||||
|
||||
/**
|
||||
@ -523,13 +495,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
*/
|
||||
ReturnValue_t updateReplyMapEntry(DeviceCommandId_t deviceReply, uint16_t delayCycles,
|
||||
uint16_t maxDelayCycles, bool periodic = false);
|
||||
/**
|
||||
* @brief Can be used to set the dataset corresponding to a reply ID manually.
|
||||
* @details
|
||||
* Used by the local data pool manager.
|
||||
*/
|
||||
ReturnValue_t setReplyDataset(DeviceCommandId_t replyId, LocalPoolDataSetBase *dataset);
|
||||
|
||||
|
||||
/**
|
||||
* Get the time needed to transit from modeFrom to modeTo.
|
||||
*
|
||||
@ -548,15 +514,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
*/
|
||||
virtual uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) = 0;
|
||||
|
||||
/* Functions used by the local data pool manager */
|
||||
/**
|
||||
* This function is used to initialize the local housekeeping pool
|
||||
* entries. The default implementation leaves the pool empty.
|
||||
* @param localDataPoolMap
|
||||
* @return
|
||||
*/
|
||||
virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
||||
LocalDataPoolManager &poolManager) override;
|
||||
|
||||
/**
|
||||
* @brief Set all datapool variables that are update periodically in
|
||||
* normal mode invalid
|
||||
@ -566,16 +524,11 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
* variables inside the dataset to invalid. The user can override this
|
||||
* method optionally.
|
||||
*/
|
||||
virtual void setNormalDatapoolEntriesInvalid();
|
||||
/**
|
||||
* @brief Get the dataset handle for a given SID.
|
||||
* @details
|
||||
* The default implementation will use the deviceCommandMap to look for the corresponding
|
||||
* dataset handle. The user can override this function if this is not desired.
|
||||
* @param sid
|
||||
* @return
|
||||
*/
|
||||
virtual LocalPoolDataSetBase *getDataSetHandle(sid_t sid) override;
|
||||
virtual void setNormalDatapoolEntriesInvalid(); //TODO
|
||||
|
||||
DatapoolHelper* getDatapoolHelper() override;
|
||||
HousekeepingHelper* getHousekeepingHelper() override;
|
||||
|
||||
|
||||
/* HasModesIF overrides */
|
||||
virtual void startTransition(Mode_t mode, Submode_t submode) override;
|
||||
@ -745,7 +698,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
/* Action helper for HasActionsIF */
|
||||
ActionHelper actionHelper;
|
||||
/* Housekeeping Manager */
|
||||
LocalDataPoolManager poolManager;
|
||||
DatapoolHelper datapoolHelper;
|
||||
|
||||
/**
|
||||
* @brief Information about commands
|
||||
@ -786,7 +739,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
//! The dataset used to access housekeeping data related to the
|
||||
//! respective device reply. Will point to a dataset held by
|
||||
//! the child handler (if one is specified)
|
||||
LocalPoolDataSetBase *dataSet = nullptr;
|
||||
// LocalPoolDataSetBase *dataSet = nullptr; TODO
|
||||
//! The command that expects this reply.
|
||||
DeviceCommandMap::iterator command;
|
||||
//! Instead of using delayCycles to specify the maximum time to wait for the device reply, it
|
||||
@ -818,7 +771,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
//! and to send replies.
|
||||
MessageQueueIF *commandQueue = nullptr;
|
||||
|
||||
DeviceHandlerThermalSet *thermalSet = nullptr;
|
||||
void *thermalSet = nullptr; //TODO
|
||||
|
||||
/**
|
||||
* Optional Error code. Can be set in doStartUp(), doShutDown() and
|
||||
@ -935,12 +888,6 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
*/
|
||||
virtual void doOnActivity();
|
||||
|
||||
/**
|
||||
* Required for HasLocalDataPoolIF, return a handle to the local pool manager.
|
||||
* @return
|
||||
*/
|
||||
LocalDataPoolManager *getHkManagerHandle() override;
|
||||
|
||||
/**
|
||||
* Returns the delay cycle count of a reply.
|
||||
* A count != 0 indicates that the command is already executed.
|
||||
@ -1032,7 +979,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
* by #switches are on
|
||||
* - @c PowerSwitchIF::SWITCH_OFF one of the switches specified by
|
||||
* #switches are off
|
||||
* - @c PowerSwitchIF::returnvalue::FAILED if an error occured
|
||||
* - @c returnvalue::FAILED if an error occured
|
||||
*/
|
||||
ReturnValue_t getStateOfSwitches();
|
||||
|
||||
@ -1307,8 +1254,6 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
|
||||
ReturnValue_t handleDeviceHandlerMessage(CommandMessage *message);
|
||||
|
||||
virtual dur_millis_t getPeriodicOperationFrequency() const override;
|
||||
|
||||
void parseReply(const uint8_t *receivedData, size_t receivedDataLen);
|
||||
|
||||
void handleTransitionToOnMode(Mode_t commandedMode, Submode_t commandedSubmode);
|
||||
|
@ -4,7 +4,8 @@
|
||||
#include "fsfw/health/HealthTableIF.h"
|
||||
#include "fsfw/modes/HasModesIF.h"
|
||||
#include "fsfw/objectmanager/ObjectManager.h"
|
||||
#include "fsfw/power/Fuse.h"
|
||||
//#include "fsfw/power/Fuse.h"
|
||||
#include <fsfw/power/PowerSwitchIF.h>
|
||||
#include "fsfw/serviceinterface/ServiceInterfaceStream.h"
|
||||
#include "fsfw/thermal/ThermalComponentIF.h"
|
||||
|
||||
@ -77,16 +78,16 @@ ReturnValue_t DeviceHandlerFailureIsolation::eventReceived(EventMessage* event)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Fuse::FUSE_WENT_OFF:
|
||||
// Not so good, because PCDU reacted.
|
||||
case Fuse::POWER_ABOVE_HIGH_LIMIT:
|
||||
// Better, because software detected over-current.
|
||||
setFaulty(event->getEvent());
|
||||
break;
|
||||
case Fuse::POWER_BELOW_LOW_LIMIT:
|
||||
// Device might got stuck during boot, retry.
|
||||
handleRecovery(event->getEvent());
|
||||
break;
|
||||
// case Fuse::FUSE_WENT_OFF:
|
||||
// // Not so good, because PCDU reacted.
|
||||
// case Fuse::POWER_ABOVE_HIGH_LIMIT:
|
||||
// // Better, because software detected over-current.
|
||||
// setFaulty(event->getEvent());
|
||||
// break;
|
||||
// case Fuse::POWER_BELOW_LOW_LIMIT:
|
||||
// // Device might got stuck during boot, retry.
|
||||
// handleRecovery(event->getEvent());
|
||||
// break;
|
||||
//****Thermal*****
|
||||
case ThermalComponentIF::COMPONENT_TEMP_LOW:
|
||||
case ThermalComponentIF::COMPONENT_TEMP_HIGH:
|
||||
|
@ -2,8 +2,8 @@
|
||||
#define FSFW_DEVICEHANDLERS_DEVICEHANDLERIF_H_
|
||||
|
||||
#include "../action/HasActionsIF.h"
|
||||
#include "../datapoollocal/localPoolDefinitions.h"
|
||||
#include "../events/Event.h"
|
||||
#include "../introspection/ClasslessEnum.h"
|
||||
#include "../ipc/MessageQueueSenderIF.h"
|
||||
#include "../modes/HasModesIF.h"
|
||||
#include "DeviceHandlerMessage.h"
|
||||
@ -37,23 +37,30 @@ class DeviceHandlerIF {
|
||||
* The mode of the device itself is transparent to the user but related to the mode of the
|
||||
* handler. MODE_ON and MODE_OFF are included in hasModesIF.h
|
||||
*/
|
||||
FSFW_CLASSLESS_ENUM(
|
||||
DeviceHandlerMode, Mode_t,
|
||||
//! The device is powered and ready to perform operations. In this mode, no
|
||||
//! commands are sent by the device handler itself, but direct commands can be
|
||||
//! commanded and will be executed/forwarded to the device
|
||||
//! This is an alias of MODE_ON to have the FSFW_ENUM complete for introspection
|
||||
((DEVICEHANDLER_MODE_ON, HasModesIF::MODE_ON, "On"))
|
||||
//! The device is powered off. The only command accepted in this
|
||||
//! mode is a mode change to on.
|
||||
//! This is an alias of MODE_OFF to have the FSFW_ENUM complete for introspection
|
||||
((DEVICEHANDLER_MODE_OFF, HasModesIF::MODE_OFF, "Off"))
|
||||
//! The device is powered on and the device handler periodically sends
|
||||
//! commands. The commands to be sent are selected by the handler
|
||||
//! according to the submode.
|
||||
((MODE_NORMAL, 2, "Normal"))
|
||||
//! The device is powered on and ready to perform operations. In this mode,
|
||||
//! raw commands can be sent. The device handler will send all replies
|
||||
//! received from the command back to the commanding object as raw TM
|
||||
((MODE_RAW, 3, "Raw"))
|
||||
//! The device is shut down but the switch could not be turned off, so the
|
||||
//! device still is powered. In this mode, only a mode change to @c MODE_OFF
|
||||
//! can be commanded, which tries to switch off the device again.
|
||||
((MODE_ERROR_ON, 4, "Error")))
|
||||
|
||||
// MODE_ON = 0, //!< The device is powered and ready to perform operations. In this mode, no
|
||||
// commands are sent by the device handler itself, but direct commands van be commanded and will
|
||||
// be interpreted MODE_OFF = 1, //!< The device is powered off. The only command accepted in this
|
||||
// mode is a mode change to on.
|
||||
//! The device is powered on and the device handler periodically sends
|
||||
//! commands. The commands to be sent are selected by the handler
|
||||
//! according to the submode.
|
||||
static const Mode_t MODE_NORMAL = 2;
|
||||
//! The device is powered on and ready to perform operations. In this mode,
|
||||
//! raw commands can be sent. The device handler will send all replies
|
||||
//! received from the command back to the commanding object.
|
||||
static const Mode_t MODE_RAW = 3;
|
||||
//! The device is shut down but the switch could not be turned off, so the
|
||||
//! device still is powered. In this mode, only a mode change to @c MODE_OFF
|
||||
//! can be commanded, which tries to switch off the device again.
|
||||
static const Mode_t MODE_ERROR_ON = 4;
|
||||
//! This is a transitional state which can not be commanded. The device
|
||||
//! handler performs all commands to get the device in a state ready to
|
||||
//! perform commands. When this is completed, the mode changes to @c MODE_ON.
|
||||
@ -162,10 +169,10 @@ class DeviceHandlerIF {
|
||||
NOTHING //!< Do nothing.
|
||||
};
|
||||
|
||||
static constexpr uint32_t DEFAULT_THERMAL_SET_ID = sid_t::INVALID_SET_ID - 1;
|
||||
static constexpr uint32_t DEFAULT_THERMAL_SET_ID = 0; // TODO sid_t::INVALID_SET_ID - 1;
|
||||
|
||||
static constexpr lp_id_t DEFAULT_THERMAL_STATE_POOL_ID = localpool::INVALID_LPID - 2;
|
||||
static constexpr lp_id_t DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID = localpool::INVALID_LPID - 1;
|
||||
static constexpr int DEFAULT_THERMAL_STATE_POOL_ID = 0; // TODOlocalpool::INVALID_LPID - 2;
|
||||
static constexpr int DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID = 0; // TODOlocalpool::INVALID_LPID - 1;
|
||||
|
||||
/**
|
||||
* Default Destructor
|
||||
|
@ -15,14 +15,16 @@ const LocalPool::LocalPoolConfig EventManager::poolConfig = {
|
||||
{fsfwconfig::FSFW_EVENTMGMT_EVENTIDMATCHERS, sizeof(EventIdRangeMatcher)},
|
||||
{fsfwconfig::FSFW_EVENTMGMR_RANGEMATCHERS, sizeof(ReporterRangeMatcher)}};
|
||||
|
||||
EventManager::EventManager(object_id_t setObjectId)
|
||||
EventManager::EventManager(object_id_t setObjectId, uint32_t eventQueueDepth)
|
||||
: SystemObject(setObjectId), factoryBackend(0, poolConfig, false, true) {
|
||||
mutex = MutexFactory::instance()->createMutex();
|
||||
eventReportQueue = QueueFactory::instance()->createMessageQueue(MAX_EVENTS_PER_CYCLE,
|
||||
EventMessage::EVENT_MESSAGE_SIZE);
|
||||
auto mqArgs = MqArgs(setObjectId, static_cast<void*>(this));
|
||||
eventReportQueue = QueueFactory::instance()->createMessageQueue(
|
||||
eventQueueDepth, EventMessage::EVENT_MESSAGE_SIZE, &mqArgs);
|
||||
}
|
||||
|
||||
EventManager::~EventManager() {
|
||||
listenerList.clear();
|
||||
QueueFactory::instance()->deleteMessageQueue(eventReportQueue);
|
||||
MutexFactory::instance()->deleteMutex(mutex);
|
||||
}
|
||||
@ -37,6 +39,16 @@ ReturnValue_t EventManager::performOperation(uint8_t opCode) {
|
||||
if (result == returnvalue::OK) {
|
||||
#if FSFW_OBJ_EVENT_TRANSLATION == 1
|
||||
printEvent(&message);
|
||||
#else
|
||||
//TODO
|
||||
sif::debug << "EventManager: ";
|
||||
sif::debug << "0x" << std::hex << std::setw(8) << std::setfill('0') << message.getReporter()
|
||||
<< std::setfill(' ') << std::dec;
|
||||
sif::debug << " reported event with ID " << message.getEventId() << std::endl;
|
||||
sif::debug << std::hex << "P1 Hex: 0x"
|
||||
<< message.getParameter1() << " | P1 Dec: " << std::dec << message.getParameter1()
|
||||
<< std::hex << " | P2 Hex: 0x" << message.getParameter2()
|
||||
<< " | P2 Dec: " << std::dec << message.getParameter2() << std::endl;
|
||||
#endif
|
||||
notifyListeners(&message);
|
||||
}
|
||||
@ -46,9 +58,20 @@ ReturnValue_t EventManager::performOperation(uint8_t opCode) {
|
||||
|
||||
void EventManager::notifyListeners(EventMessage* message) {
|
||||
lockMutex();
|
||||
for (auto iter = listenerList.begin(); iter != listenerList.end(); ++iter) {
|
||||
if (iter->second.match(message)) {
|
||||
MessageQueueSenderIF::sendMessage(iter->first, message, message->getSender());
|
||||
for (auto& listener : listenerList) {
|
||||
if (listener.second.match(message)) {
|
||||
ReturnValue_t result =
|
||||
MessageQueueSenderIF::sendMessage(listener.first, message, message->getSender());
|
||||
if (result != returnvalue::OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << std::hex << "EventManager::notifyListeners: MSG to 0x" << std::setfill('0')
|
||||
<< std::setw(8) << listener.first << " failed with result 0x" << std::setw(4)
|
||||
<< result << std::setfill(' ') << std::endl;
|
||||
#else
|
||||
sif::printError("Sending message to listener 0x%08x failed with result %04x\n",
|
||||
listener.first, result);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
unlockMutex();
|
||||
@ -61,9 +84,14 @@ ReturnValue_t EventManager::registerListener(MessageQueueId_t listener,
|
||||
if (!result.second) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t EventManager::unregisterListener(MessageQueueId_t listener) {
|
||||
return listenerList.erase(listener) == 1 ? returnvalue::OK : returnvalue::FAILED;
|
||||
}
|
||||
|
||||
ReturnValue_t EventManager::subscribeToEvent(MessageQueueId_t listener, EventId_t event) {
|
||||
return subscribeToEventRange(listener, event);
|
||||
}
|
||||
@ -194,4 +222,19 @@ void EventManager::printUtility(sif::OutputTypes printType, EventMessage* messag
|
||||
}
|
||||
}
|
||||
|
||||
void EventManager::printListeners() {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::info << "Event manager listener MQ IDs:" << std::setfill('0') << std::hex << std::endl;
|
||||
for (auto& listener : listenerList) {
|
||||
sif::info << "0x" << std::setw(8) << listener.first << std::endl;
|
||||
}
|
||||
sif::info << std::dec << std::setfill(' ');
|
||||
#else
|
||||
sif::printInfo("Event manager listener MQ IDs:\n");
|
||||
for (auto& listener : listenerList) {
|
||||
sif::printInfo("0x%08x\n", listener.first);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* FSFW_OBJ_EVENT_TRANSLATION == 1 */
|
||||
|
@ -21,9 +21,7 @@ extern const char* translateEvents(Event event);
|
||||
|
||||
class EventManager : public EventManagerIF, public ExecutableObjectIF, public SystemObject {
|
||||
public:
|
||||
static const uint16_t MAX_EVENTS_PER_CYCLE = 80;
|
||||
|
||||
EventManager(object_id_t setObjectId);
|
||||
EventManager(object_id_t setObjectId, uint32_t eventQueueDepth);
|
||||
virtual ~EventManager();
|
||||
|
||||
void setMutexTimeout(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs);
|
||||
@ -31,6 +29,7 @@ class EventManager : public EventManagerIF, public ExecutableObjectIF, public Sy
|
||||
MessageQueueId_t getEventReportQueue();
|
||||
|
||||
ReturnValue_t registerListener(MessageQueueId_t listener, bool forwardAllButSelected = false);
|
||||
ReturnValue_t unregisterListener(MessageQueueId_t listener) override;
|
||||
ReturnValue_t subscribeToEvent(MessageQueueId_t listener, EventId_t event);
|
||||
ReturnValue_t subscribeToAllEventsFrom(MessageQueueId_t listener, object_id_t object);
|
||||
ReturnValue_t subscribeToEventRange(MessageQueueId_t listener, EventId_t idFrom = 0,
|
||||
@ -43,6 +42,7 @@ class EventManager : public EventManagerIF, public ExecutableObjectIF, public Sy
|
||||
object_id_t reporterFrom = 0, object_id_t reporterTo = 0,
|
||||
bool reporterInverted = false);
|
||||
ReturnValue_t performOperation(uint8_t opCode);
|
||||
void printListeners();
|
||||
|
||||
protected:
|
||||
MessageQueueIF* eventReportQueue = nullptr;
|
||||
|
@ -18,6 +18,7 @@ class EventManagerIF {
|
||||
|
||||
virtual ReturnValue_t registerListener(MessageQueueId_t listener,
|
||||
bool forwardAllButSelected = false) = 0;
|
||||
virtual ReturnValue_t unregisterListener(MessageQueueId_t listener) = 0;
|
||||
virtual ReturnValue_t subscribeToEvent(MessageQueueId_t listener, EventId_t event) = 0;
|
||||
virtual ReturnValue_t subscribeToAllEventsFrom(MessageQueueId_t listener, object_id_t object) = 0;
|
||||
virtual ReturnValue_t unsubscribeFromAllEvents(MessageQueueId_t listener, object_id_t object) = 0;
|
||||
|
@ -10,7 +10,7 @@ enum : uint8_t {
|
||||
CDH = 28,
|
||||
TCS_1 = 59,
|
||||
PCDU_1 = 42,
|
||||
PCDU_2 = 43,
|
||||
POWER_SWITCH_IF = 43,
|
||||
HEATER = 50,
|
||||
T_SENSORS = 52,
|
||||
FDIR = 70,
|
||||
@ -33,6 +33,7 @@ enum : uint8_t {
|
||||
PUS_SERVICE_23 = 103,
|
||||
MGM_LIS3MDL = 106,
|
||||
MGM_RM3100 = 107,
|
||||
CFDP = 108,
|
||||
|
||||
FW_SUBSYSTEM_ID_RANGE
|
||||
};
|
||||
|
@ -23,7 +23,7 @@ FailureIsolationBase::~FailureIsolationBase() {
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
manager->unsubscribeFromAllEvents(eventQueue->getId(), ownerId);
|
||||
manager->unregisterListener(eventQueue->getId());
|
||||
QueueFactory::instance()->deleteMessageQueue(eventQueue);
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ target_sources(
|
||||
AsciiConverter.cpp
|
||||
CRC.cpp
|
||||
DleEncoder.cpp
|
||||
DleParser.cpp
|
||||
PeriodicOperationDivider.cpp
|
||||
timevalOperations.cpp
|
||||
Type.cpp
|
||||
|
173
src/fsfw/globalfunctions/DleParser.cpp
Normal file
173
src/fsfw/globalfunctions/DleParser.cpp
Normal file
@ -0,0 +1,173 @@
|
||||
#include "DleParser.h"
|
||||
|
||||
#include <fsfw/serviceinterface/ServiceInterface.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
DleParser::DleParser(SimpleRingBuffer& decodeRingBuf, DleEncoder& decoder, BufPair encodedBuf,
|
||||
BufPair decodedBuf)
|
||||
: decodeRingBuf(decodeRingBuf),
|
||||
decoder(decoder),
|
||||
encodedBuf(encodedBuf),
|
||||
decodedBuf(decodedBuf) {}
|
||||
|
||||
ReturnValue_t DleParser::passData(const uint8_t* data, size_t len) {
|
||||
if (data == nullptr or len == 0) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
return decodeRingBuf.writeData(data, len);
|
||||
}
|
||||
|
||||
ReturnValue_t DleParser::parseRingBuf(size_t& readSize) {
|
||||
ctx.setType(DleParser::ContextType::NONE);
|
||||
size_t availableData = decodeRingBuf.getAvailableReadData();
|
||||
if (availableData == 0) {
|
||||
return NO_PACKET_FOUND;
|
||||
}
|
||||
if (availableData > encodedBuf.second) {
|
||||
ErrorInfo info;
|
||||
info.len = decodeRingBuf.getAvailableReadData();
|
||||
setErrorContext(ErrorTypes::DECODING_BUF_TOO_SMALL, info);
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
ReturnValue_t result = decodeRingBuf.readData(encodedBuf.first, availableData);
|
||||
if (result != returnvalue::OK) {
|
||||
ErrorInfo info;
|
||||
info.res = result;
|
||||
setErrorContext(ErrorTypes::RING_BUF_ERROR, info);
|
||||
return result;
|
||||
}
|
||||
bool stxFound = false;
|
||||
size_t stxIdx = 0;
|
||||
for (size_t vectorIdx = 0; vectorIdx < availableData; vectorIdx++) {
|
||||
// handle STX char
|
||||
if (encodedBuf.first[vectorIdx] == DleEncoder::STX_CHAR) {
|
||||
if (not stxFound) {
|
||||
stxFound = true;
|
||||
stxIdx = vectorIdx;
|
||||
} else {
|
||||
// might be lost packet, so we should advance the read pointer
|
||||
// without skipping the STX
|
||||
readSize = vectorIdx;
|
||||
ErrorInfo info;
|
||||
setErrorContext(ErrorTypes::CONSECUTIVE_STX_CHARS, info);
|
||||
return POSSIBLE_PACKET_LOSS;
|
||||
}
|
||||
}
|
||||
// handle ETX char
|
||||
if (encodedBuf.first[vectorIdx] == DleEncoder::ETX_CHAR) {
|
||||
if (stxFound) {
|
||||
// This is propably a packet, so we decode it.
|
||||
size_t decodedLen = 0;
|
||||
size_t dummy = 0;
|
||||
|
||||
ReturnValue_t result =
|
||||
decoder.decode(&encodedBuf.first[stxIdx], availableData - stxIdx, &dummy,
|
||||
decodedBuf.first, decodedBuf.second, &decodedLen);
|
||||
if (result == returnvalue::OK) {
|
||||
ctx.setType(ContextType::PACKET_FOUND);
|
||||
ctx.decodedPacket.first = decodedBuf.first;
|
||||
ctx.decodedPacket.second = decodedLen;
|
||||
readSize = ++vectorIdx;
|
||||
return returnvalue::OK;
|
||||
} else {
|
||||
// invalid packet, skip.
|
||||
readSize = ++vectorIdx;
|
||||
ErrorInfo info;
|
||||
info.res = result;
|
||||
setErrorContext(ErrorTypes::DECODE_ERROR, info);
|
||||
return POSSIBLE_PACKET_LOSS;
|
||||
}
|
||||
} else {
|
||||
// might be lost packet, so we should advance the read pointer
|
||||
readSize = ++vectorIdx;
|
||||
ErrorInfo info;
|
||||
info.len = 0;
|
||||
setErrorContext(ErrorTypes::CONSECUTIVE_ETX_CHARS, info);
|
||||
return POSSIBLE_PACKET_LOSS;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO_PACKET_FOUND;
|
||||
}
|
||||
|
||||
void DleParser::defaultFoundPacketHandler(uint8_t* packet, size_t len, void* args) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::info << "DleParserBase::handleFoundPacket: Detected DLE packet with " << len << " bytes"
|
||||
<< std::endl;
|
||||
#else
|
||||
sif::printInfo("DleParserBase::handleFoundPacket: Detected DLE packet with %d bytes\n", len);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void DleParser::defaultErrorHandler() {
|
||||
if (ctx.getType() != DleParser::ContextType::ERROR) {
|
||||
errorPrinter("No error");
|
||||
return;
|
||||
}
|
||||
switch (ctx.error.first) {
|
||||
case (ErrorTypes::NONE): {
|
||||
errorPrinter("No error");
|
||||
break;
|
||||
}
|
||||
case (ErrorTypes::DECODE_ERROR): {
|
||||
errorPrinter("Decode Error");
|
||||
break;
|
||||
}
|
||||
case (ErrorTypes::RING_BUF_ERROR): {
|
||||
errorPrinter("Ring Buffer Error");
|
||||
break;
|
||||
}
|
||||
case (ErrorTypes::ENCODED_BUF_TOO_SMALL):
|
||||
case (ErrorTypes::DECODING_BUF_TOO_SMALL): {
|
||||
char opt[64];
|
||||
snprintf(opt, sizeof(opt), ": Too small for packet with length %zu",
|
||||
ctx.decodedPacket.second);
|
||||
if (ctx.error.first == ErrorTypes::ENCODED_BUF_TOO_SMALL) {
|
||||
errorPrinter("Encoded buf too small", opt);
|
||||
} else {
|
||||
errorPrinter("Decoding buf too small", opt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case (ErrorTypes::CONSECUTIVE_STX_CHARS): {
|
||||
errorPrinter("Consecutive STX chars detected");
|
||||
break;
|
||||
}
|
||||
case (ErrorTypes::CONSECUTIVE_ETX_CHARS): {
|
||||
errorPrinter("Consecutive ETX chars detected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DleParser::errorPrinter(const char* str, const char* opt) {
|
||||
if (opt == nullptr) {
|
||||
opt = "";
|
||||
}
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::info << "DleParserBase::handleParseError: " << str << opt << std::endl;
|
||||
#else
|
||||
sif::printInfo("DleParserBase::handleParseError: %s%s\n", str, opt);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void DleParser::setErrorContext(ErrorTypes err, ErrorInfo info) {
|
||||
ctx.setType(ContextType::ERROR);
|
||||
ctx.error.first = err;
|
||||
ctx.error.second = info;
|
||||
}
|
||||
|
||||
ReturnValue_t DleParser::confirmBytesRead(size_t bytesRead) {
|
||||
return decodeRingBuf.deleteData(bytesRead);
|
||||
}
|
||||
|
||||
const DleParser::Context& DleParser::getContext() { return ctx; }
|
||||
|
||||
void DleParser::reset() { decodeRingBuf.clear(); }
|
127
src/fsfw/globalfunctions/DleParser.h
Normal file
127
src/fsfw/globalfunctions/DleParser.h
Normal file
@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
|
||||
#include <fsfw/container/SimpleRingBuffer.h>
|
||||
#include <fsfw/globalfunctions/DleEncoder.h>
|
||||
#include <fsfw/returnvalues/returnvalue.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
/**
|
||||
* @brief This base helper class can be used to extract DLE encoded packets from a data stream
|
||||
* @details
|
||||
* The core API of the parser takes received packets which can contains DLE packets. The parser
|
||||
* can deal with DLE packets split across multiple packets. It does so by using a dedicated
|
||||
* decoding ring buffer. The user can process received packets and detect errors by
|
||||
* overriding two provided virtual methods. This also allows detecting multiple DLE packets
|
||||
* inside one passed packet.
|
||||
*/
|
||||
class DleParser {
|
||||
public:
|
||||
static constexpr ReturnValue_t NO_PACKET_FOUND = returnvalue::makeCode(1, 1);
|
||||
static constexpr ReturnValue_t POSSIBLE_PACKET_LOSS = returnvalue::makeCode(1, 2);
|
||||
using BufPair = std::pair<uint8_t*, size_t>;
|
||||
|
||||
enum class ContextType { NONE, PACKET_FOUND, ERROR };
|
||||
|
||||
enum class ErrorTypes {
|
||||
NONE,
|
||||
ENCODED_BUF_TOO_SMALL,
|
||||
DECODING_BUF_TOO_SMALL,
|
||||
DECODE_ERROR,
|
||||
RING_BUF_ERROR,
|
||||
CONSECUTIVE_STX_CHARS,
|
||||
CONSECUTIVE_ETX_CHARS
|
||||
};
|
||||
|
||||
union ErrorInfo {
|
||||
size_t len;
|
||||
ReturnValue_t res;
|
||||
};
|
||||
|
||||
using ErrorPair = std::pair<ErrorTypes, ErrorInfo>;
|
||||
|
||||
struct Context {
|
||||
public:
|
||||
Context() { setType(ContextType::PACKET_FOUND); }
|
||||
|
||||
void setType(ContextType type) {
|
||||
this->type = type;
|
||||
if (type == ContextType::PACKET_FOUND) {
|
||||
error.first = ErrorTypes::NONE;
|
||||
error.second.len = 0;
|
||||
} else {
|
||||
decodedPacket.first = nullptr;
|
||||
decodedPacket.second = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ContextType getType() const { return type; }
|
||||
|
||||
BufPair decodedPacket = {};
|
||||
ErrorPair error;
|
||||
|
||||
private:
|
||||
ContextType type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class constructor
|
||||
* @param decodeRingBuf Ring buffer used to store multiple packets to allow detecting DLE packets
|
||||
* split across multiple packets
|
||||
* @param decoder Decoder instance
|
||||
* @param encodedBuf Buffer used to store encoded packets. It has to be large enough to hold
|
||||
* the largest expected encoded DLE packet size
|
||||
* @param decodedBuf Buffer used to store decoded packets. It has to be large enough to hold the
|
||||
* largest expected decoded DLE packet size
|
||||
* @param handler Function which will be called on a found packet
|
||||
* @param args Arbitrary user argument
|
||||
*/
|
||||
DleParser(SimpleRingBuffer& decodeRingBuf, DleEncoder& decoder, BufPair encodedBuf,
|
||||
BufPair decodedBuf);
|
||||
|
||||
/**
|
||||
* This function allows to pass new data into the parser. It then scans for DLE packets
|
||||
* automatically and inserts (part of) the packet into a ring buffer if necessary.
|
||||
* @param data
|
||||
* @param len
|
||||
* @return
|
||||
*/
|
||||
ReturnValue_t passData(const uint8_t* data, size_t len);
|
||||
|
||||
ReturnValue_t parseRingBuf(size_t& bytesRead);
|
||||
|
||||
ReturnValue_t confirmBytesRead(size_t bytesRead);
|
||||
|
||||
const Context& getContext();
|
||||
/**
|
||||
* Example found packet handler
|
||||
* function call
|
||||
* @param packet Decoded packet
|
||||
* @param len Length of detected packet
|
||||
*/
|
||||
void defaultFoundPacketHandler(uint8_t* packet, size_t len, void* args);
|
||||
/**
|
||||
* Will be called if an error occured in the #passData call
|
||||
* @param err
|
||||
* @param ctx Context information depending on the error type
|
||||
* - For buffer length errors, will be set to the detected packet length which is too large
|
||||
* - For decode or ring buffer errors, will be set to the result returned from the failed call
|
||||
*/
|
||||
void defaultErrorHandler();
|
||||
|
||||
static void errorPrinter(const char* str, const char* opt = nullptr);
|
||||
|
||||
void setErrorContext(ErrorTypes err, ErrorInfo ctx);
|
||||
/**
|
||||
* Resets the parser by resetting the internal states and clearing the decoding ring buffer
|
||||
*/
|
||||
void reset();
|
||||
|
||||
private:
|
||||
SimpleRingBuffer& decodeRingBuf;
|
||||
DleEncoder& decoder;
|
||||
BufPair encodedBuf;
|
||||
BufPair decodedBuf;
|
||||
Context ctx;
|
||||
};
|
@ -44,13 +44,13 @@ class Type : public SerializeIF {
|
||||
|
||||
static ActualType_t getActualType(uint8_t ptc, uint8_t pfc);
|
||||
|
||||
virtual ReturnValue_t serialize(uint8_t **buffer, size_t *size, size_t maxSize,
|
||||
Endianness streamEndianness) const override;
|
||||
ReturnValue_t serialize(uint8_t **buffer, size_t *size, size_t maxSize,
|
||||
Endianness streamEndianness) const override;
|
||||
|
||||
virtual size_t getSerializedSize() const override;
|
||||
size_t getSerializedSize() const override;
|
||||
|
||||
virtual ReturnValue_t deSerialize(const uint8_t **buffer, size_t *size,
|
||||
Endianness streamEndianness) override;
|
||||
ReturnValue_t deSerialize(const uint8_t **buffer, size_t *size,
|
||||
Endianness streamEndianness) override;
|
||||
|
||||
private:
|
||||
ActualType_t actualType;
|
||||
|
@ -24,7 +24,7 @@ class MatchTree : public SerializeableMatcherIF<T>, public BinaryTree<Serializea
|
||||
MatchTree(iterator root, uint8_t maxDepth = -1)
|
||||
: BinaryTree<SerializeableMatcherIF<T>>(root.element), maxDepth(maxDepth) {}
|
||||
MatchTree() : BinaryTree<SerializeableMatcherIF<T>>(), maxDepth(-1) {}
|
||||
virtual ~MatchTree() {}
|
||||
virtual ~MatchTree() { clear(); }
|
||||
virtual bool match(T number) override { return matchesTree(number); }
|
||||
bool matchesTree(T number) {
|
||||
iterator iter = this->begin();
|
||||
@ -176,6 +176,45 @@ class MatchTree : public SerializeableMatcherIF<T>, public BinaryTree<Serializea
|
||||
return cleanUpElement(position);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
Node* localRoot = BinaryTree<SerializeableMatcherIF<T>>::rootNode;
|
||||
|
||||
if (localRoot == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Node* node = localRoot->left;
|
||||
|
||||
while (true) {
|
||||
if (node->left != nullptr) {
|
||||
node = node->left;
|
||||
continue;
|
||||
}
|
||||
if (node->right != nullptr) {
|
||||
node = node->right;
|
||||
continue;
|
||||
}
|
||||
if (node->parent == nullptr) {
|
||||
// this is the root node with no children
|
||||
if (node->value != nullptr) {
|
||||
cleanUpElement(iterator(node));
|
||||
}
|
||||
return;
|
||||
}
|
||||
// leaf
|
||||
{
|
||||
Node* parent = node->parent;
|
||||
if (parent->left == node) {
|
||||
parent->left = nullptr;
|
||||
} else {
|
||||
parent->right = nullptr;
|
||||
}
|
||||
cleanUpElement(iterator(node));
|
||||
node = parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual ReturnValue_t cleanUpElement(iterator position) { return returnvalue::OK; }
|
||||
|
||||
bool matchSubtree(iterator iter, T number) {
|
||||
|
@ -1,8 +1,8 @@
|
||||
#include "fsfw/globalfunctions/timevalOperations.h"
|
||||
|
||||
timeval& operator+=(timeval& lhs, const timeval& rhs) {
|
||||
int64_t sum = lhs.tv_sec * 1000000. + lhs.tv_usec;
|
||||
sum += rhs.tv_sec * 1000000. + rhs.tv_usec;
|
||||
int64_t sum = static_cast<int64_t>(lhs.tv_sec) * 1000000. + lhs.tv_usec;
|
||||
sum += static_cast<int64_t>(rhs.tv_sec) * 1000000. + rhs.tv_usec;
|
||||
lhs.tv_sec = sum / 1000000;
|
||||
lhs.tv_usec = sum - lhs.tv_sec * 1000000;
|
||||
return lhs;
|
||||
|
@ -16,26 +16,24 @@ class HasHealthIF {
|
||||
};
|
||||
|
||||
static const uint8_t INTERFACE_ID = CLASS_ID::HAS_HEALTH_IF;
|
||||
static const ReturnValue_t OBJECT_NOT_HEALTHY = MAKE_RETURN_CODE(1);
|
||||
static const ReturnValue_t INVALID_HEALTH_STATE = MAKE_RETURN_CODE(2);
|
||||
static constexpr ReturnValue_t OBJECT_NOT_HEALTHY = returnvalue::makeCode(INTERFACE_ID, 1);
|
||||
static constexpr ReturnValue_t INVALID_HEALTH_STATE = returnvalue::makeCode(INTERFACE_ID, 2);
|
||||
static constexpr ReturnValue_t IS_EXTERNALLY_CONTROLLED = returnvalue::makeCode(INTERFACE_ID, 3);
|
||||
|
||||
static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::SYSTEM_MANAGER_1;
|
||||
//! P1: New Health, P2: Old Health
|
||||
static const Event HEALTH_INFO = MAKE_EVENT(6, severity::INFO);
|
||||
static const Event CHILD_CHANGED_HEALTH = MAKE_EVENT(7, severity::INFO);
|
||||
static const Event CHILD_PROBLEMS = MAKE_EVENT(8, severity::LOW);
|
||||
static const Event OVERWRITING_HEALTH =
|
||||
MAKE_EVENT(9, severity::LOW); //!< Assembly overwrites health information of children to keep
|
||||
//!< satellite alive.
|
||||
static const Event TRYING_RECOVERY =
|
||||
MAKE_EVENT(10, severity::MEDIUM); //!< Someone starts a recovery of a component (typically
|
||||
//!< power-cycle). No parameters.
|
||||
static const Event RECOVERY_STEP =
|
||||
MAKE_EVENT(11, severity::MEDIUM); //!< Recovery is ongoing. Comes twice during recovery. P1:
|
||||
//!< 0 for the first, 1 for the second event. P2: 0
|
||||
static const Event RECOVERY_DONE = MAKE_EVENT(
|
||||
12,
|
||||
severity::MEDIUM); //!< Recovery was completed. Not necessarily successful. No parameters.
|
||||
|
||||
//! Assembly overwrites health information of children to keep satellite alive.
|
||||
static const Event OVERWRITING_HEALTH = MAKE_EVENT(9, severity::LOW);
|
||||
//! Someone starts a recovery of a component (typically power-cycle). No parameters.
|
||||
static const Event TRYING_RECOVERY = MAKE_EVENT(10, severity::MEDIUM);
|
||||
//! Recovery is ongoing. Comes twice during recovery.
|
||||
//! P1: 0 for the first, 1 for the second event. P2: 0
|
||||
static const Event RECOVERY_STEP = MAKE_EVENT(11, severity::MEDIUM);
|
||||
//! Recovery was completed. Not necessarily successful. No parameters.
|
||||
static const Event RECOVERY_DONE = MAKE_EVENT(12, severity::MEDIUM);
|
||||
virtual ~HasHealthIF() {}
|
||||
|
||||
virtual MessageQueueId_t getCommandQueue() const = 0;
|
||||
|
@ -5,7 +5,11 @@
|
||||
HealthHelper::HealthHelper(HasHealthIF* owner, object_id_t objectId)
|
||||
: objectId(objectId), owner(owner) {}
|
||||
|
||||
HealthHelper::~HealthHelper() { healthTable->removeObject(objectId); }
|
||||
HealthHelper::~HealthHelper() {
|
||||
if (healthTable != nullptr) {
|
||||
healthTable->removeObject(objectId);
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t HealthHelper::handleHealthCommand(CommandMessage* message) {
|
||||
switch (message->getCommand()) {
|
||||
|
@ -6,8 +6,6 @@
|
||||
|
||||
HealthTable::HealthTable(object_id_t objectid) : SystemObject(objectid) {
|
||||
mutex = MutexFactory::instance()->createMutex();
|
||||
;
|
||||
|
||||
mapIterator = healthMap.begin();
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include "HealthTableIF.h"
|
||||
|
||||
class HealthTable : public HealthTableIF, public SystemObject {
|
||||
friend class CServiceHealthCommanding;
|
||||
|
||||
public:
|
||||
explicit HealthTable(object_id_t objectid);
|
||||
~HealthTable() override;
|
||||
|
@ -1,2 +1,2 @@
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE HousekeepingMessage.cpp
|
||||
PeriodicHousekeepingHelper.cpp)
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE HousekeepingHelper.cpp
|
||||
HousekeepingSet.cpp )
|
||||
|
16
src/fsfw/housekeeping/GeneratesHousekeepingIF.h
Normal file
16
src/fsfw/housekeeping/GeneratesHousekeepingIF.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <fsfw/tmtc/FsfwProtocolHeader.h>
|
||||
|
||||
#include "HousekeepingHelper.h"
|
||||
|
||||
class GeneratesHousekeepingIF {
|
||||
public:
|
||||
static const uint8_t INTERFACE_ID = CLASS_ID::GENERATES_HOUSEKEEPING;
|
||||
|
||||
enum Functions : FsfwProtocolHeader::FunctionType_t { REPORT };
|
||||
|
||||
virtual ~GeneratesHousekeepingIF() = default;
|
||||
|
||||
virtual HousekeepingHelper* getHousekeepingHelper() = 0;
|
||||
};
|
46
src/fsfw/housekeeping/HousekeepingHelper.cpp
Normal file
46
src/fsfw/housekeeping/HousekeepingHelper.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include "HousekeepingHelper.h"
|
||||
|
||||
#include <fsfw/objectmanager/ObjectManager.h>
|
||||
|
||||
#include "GeneratesHousekeepingIF.h"
|
||||
|
||||
HousekeepingHelper::HousekeepingHelper(GeneratesHousekeepingIF* owner) : owner(owner) {}
|
||||
|
||||
ReturnValue_t HousekeepingHelper::initialize() {
|
||||
tmManager = ObjectManager::instance()->get<TmManager>(objects::TM_MANAGER);
|
||||
if (tmManager == nullptr) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
const HousekeepingSet* HousekeepingHelper::getHousekeepingSet(HousekeepingSetId_t id) {
|
||||
auto iter = housekeepingSets.find(id);
|
||||
if (iter == housekeepingSets.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
void HousekeepingHelper::registerSet(HousekeepingSet* set) {
|
||||
auto id = set->getId();
|
||||
housekeepingSets.emplace(id, set);
|
||||
}
|
||||
|
||||
ReturnValue_t HousekeepingHelper::reportHousekeeping(HousekeepingSet* set, const Action* action) {
|
||||
SystemObjectIF* ownerAsObject = dynamic_cast<SystemObjectIF*>(owner);
|
||||
if (ownerAsObject == nullptr) {
|
||||
sif::error << "Duuuuuuuude, what the hell?" << std::endl;
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
store_address_t tc;
|
||||
size_t tcOffset = 0;
|
||||
if (action != nullptr) {
|
||||
tc = action->tc;
|
||||
tcOffset = action->tcOffset;
|
||||
}
|
||||
|
||||
return tmManager->sendTmPacket(ownerAsObject->getObjectId(),
|
||||
GeneratesHousekeepingIF::INTERFACE_ID,
|
||||
GeneratesHousekeepingIF::Functions::REPORT, set, tc, tcOffset);
|
||||
}
|
36
src/fsfw/housekeeping/HousekeepingHelper.h
Normal file
36
src/fsfw/housekeeping/HousekeepingHelper.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <fsfw/returnvalues/returnvalue.h>
|
||||
#include <fsfw/serialize/SerializeIF.h>
|
||||
#include <fsfw/tmtc/TmManager.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "HousekeepingSet.h"
|
||||
|
||||
class HousekeepingHelper {
|
||||
friend class HousekeepingSet;
|
||||
|
||||
public:
|
||||
HousekeepingHelper(GeneratesHousekeepingIF* owner);
|
||||
~HousekeepingHelper() = default;
|
||||
|
||||
const HousekeepingSet* getHousekeepingSet(HousekeepingSetId_t id);
|
||||
|
||||
const std::map<HousekeepingSetId_t, HousekeepingSet*>* getHousekeepingSets() const {
|
||||
return &housekeepingSets;
|
||||
}
|
||||
|
||||
ReturnValue_t initialize();
|
||||
|
||||
protected:
|
||||
GeneratesHousekeepingIF* owner;
|
||||
TmManager* tmManager = nullptr;
|
||||
|
||||
void registerSet(HousekeepingSet* set);
|
||||
|
||||
ReturnValue_t reportHousekeeping(HousekeepingSet* set, const Action* action = nullptr);
|
||||
|
||||
private:
|
||||
std::map<HousekeepingSetId_t, HousekeepingSet*> housekeepingSets;
|
||||
};
|
146
src/fsfw/housekeeping/HousekeepingSet.cpp
Normal file
146
src/fsfw/housekeeping/HousekeepingSet.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
#include "HousekeepingSet.h"
|
||||
|
||||
#include <fsfw/datapool/DatasetEntryIF.h>
|
||||
|
||||
#include "GeneratesHousekeepingIF.h"
|
||||
|
||||
#ifdef FSFW_INTROSPECTION
|
||||
HousekeepingSet::HousekeepingSet(GeneratesHousekeepingIF* owner, const EnumIF &id) {
|
||||
if (owner != nullptr) {
|
||||
helper = owner->getHousekeepingHelper();
|
||||
helper->registerSet(this);
|
||||
}
|
||||
this->id = id.getValue();
|
||||
description = id.getDescription();
|
||||
}
|
||||
|
||||
const char* HousekeepingSet::getDescription() const { return description; }
|
||||
#else
|
||||
HousekeepingSet::HousekeepingSet(GeneratesHousekeepingIF* owner, HousekeepingSetId_t id) : id(id) {
|
||||
if (owner != nullptr) {
|
||||
helper = owner->getHousekeepingHelper();
|
||||
helper->registerSet(this);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
HousekeepingSet::~HousekeepingSet() {}
|
||||
|
||||
void HousekeepingSet::report(const Action* action) {
|
||||
if (helper != nullptr) {
|
||||
helper->reportHousekeeping(this, action);
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t HousekeepingSet::serialize(uint8_t** buffer, size_t* size, size_t maxSize,
|
||||
Endianness streamEndianness) const {
|
||||
ReturnValue_t result = SerializeAdapter::serialize(&id, buffer, size, maxSize, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
for (auto parameter : parameterList) {
|
||||
result = parameter->serialize(buffer, size, maxSize, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill valid flags
|
||||
size_t index = 0;
|
||||
for (auto parameter : parameterList) {
|
||||
size_t byteIndex = index / 8;
|
||||
size_t bitIndex = index % 8;
|
||||
if (*size + byteIndex > maxSize) { // TODO audition me
|
||||
return SerializeIF::STREAM_TOO_SHORT;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if the parameter is a datasetParameter which knows its validity
|
||||
* If not, just set to valid
|
||||
*/
|
||||
DatasetEntryIF* dataSetParameter = dynamic_cast<DatasetEntryIF*>(parameter);
|
||||
if (dataSetParameter != nullptr) {
|
||||
if (dataSetParameter->getValid()) {
|
||||
(*buffer)[byteIndex] |= (1 << bitIndex);
|
||||
} else {
|
||||
(*buffer)[byteIndex] &= ~(1 << bitIndex);
|
||||
}
|
||||
} else {
|
||||
(*buffer)[byteIndex] |= (1 << bitIndex);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
*size += (parameterList.size() - 1) / 8 + 1;
|
||||
if (*size > maxSize) {
|
||||
// how did we end up here
|
||||
return SerializeIF::STREAM_TOO_SHORT;
|
||||
}
|
||||
*buffer += (parameterList.size() - 1) / 8 + 1;
|
||||
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
size_t HousekeepingSet::getSerializedSize() const {
|
||||
size_t size = SerializeAdapter::getSerializedSize(&id);
|
||||
for (auto parameter : parameterList) {
|
||||
size += parameter->getSerializedSize();
|
||||
}
|
||||
const size_t validBytes = (parameterList.size() - 1) / 8 + 1;
|
||||
size += validBytes;
|
||||
return size;
|
||||
}
|
||||
|
||||
ReturnValue_t HousekeepingSet::deSerialize(const uint8_t** buffer, size_t* size,
|
||||
Endianness streamEndianness) {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
|
||||
/**
|
||||
* When deserializing, the id needs to be deserialized first, to find the correct class to
|
||||
* deserialize with. Consequentely, it is assumed, that the pointer was already advanced
|
||||
*
|
||||
* SerializeAdapter::deSerialize(&id, buffer, size, streamEndianness); if (result !=
|
||||
*/
|
||||
for (auto parameter : *getParameters()) {
|
||||
result = parameter->deSerialize(buffer, size, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// read valid flags
|
||||
size_t index = 0;
|
||||
for (auto parameter : parameterList) {
|
||||
size_t byteIndex = index / 8;
|
||||
size_t bitIndex = index % 8;
|
||||
if (byteIndex > *size) { // TODO audition me
|
||||
return SerializeIF::BUFFER_TOO_SHORT;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if the parameter is a datasetParameter which knows its validity
|
||||
* If not, just set to valid
|
||||
*/
|
||||
DatasetEntryIF* dataSetParameter = dynamic_cast<DatasetEntryIF*>(parameter);
|
||||
if (dataSetParameter != nullptr) {
|
||||
bool valid = ((*buffer)[byteIndex] & (1 << bitIndex)) != 0;
|
||||
dataSetParameter->setValid(valid);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
if (*size < (parameterList.size() - 1) / 8 + 1) {
|
||||
// how did we end up here
|
||||
return SerializeIF::BUFFER_TOO_SHORT;
|
||||
}
|
||||
*size -= (parameterList.size() - 1) / 8 + 1;
|
||||
*buffer += (parameterList.size() - 1) / 8 + 1;
|
||||
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
void HousekeepingSet::registerParameter(ParameterIF* parameter) {
|
||||
parameterList.push_back(parameter);
|
||||
}
|
||||
|
||||
std::vector<ParameterIF*> const* HousekeepingSet::getParameters() const { return ¶meterList; }
|
54
src/fsfw/housekeeping/HousekeepingSet.h
Normal file
54
src/fsfw/housekeeping/HousekeepingSet.h
Normal file
@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <fsfw/action/Action.h>
|
||||
#include <fsfw/introspection/Enum.h>
|
||||
#include <fsfw/introspection/HasTmTcParametersIF.h>
|
||||
#include <fsfw/serialize/SerializeIF.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class HousekeepingHelper;
|
||||
class GeneratesHousekeepingIF;
|
||||
|
||||
using HousekeepingSetId_t = uint32_t;
|
||||
|
||||
class HousekeepingSet : public HasTmTcParametersIF, public SerializeIF {
|
||||
friend class ParameterIF;
|
||||
|
||||
public:
|
||||
#ifdef FSFW_INTROSPECTION
|
||||
HousekeepingSet(GeneratesHousekeepingIF* owner, const EnumIF &id);
|
||||
#else
|
||||
HousekeepingSet(GeneratesHousekeepingIF* owner, HousekeepingSetId_t id);
|
||||
#endif
|
||||
|
||||
virtual ~HousekeepingSet();
|
||||
|
||||
HousekeepingSetId_t getId() const { return id; }
|
||||
|
||||
virtual void report(const Action* action = nullptr);
|
||||
|
||||
std::vector<ParameterIF*> const* getParameters() const override;
|
||||
|
||||
#ifdef FSFW_INTROSPECTION
|
||||
const char* getDescription() const;
|
||||
#endif
|
||||
|
||||
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
|
||||
Endianness streamEndianness) const override;
|
||||
|
||||
size_t getSerializedSize() const override;
|
||||
|
||||
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
|
||||
Endianness streamEndianness) override;
|
||||
|
||||
protected:
|
||||
HousekeepingHelper* helper = nullptr;
|
||||
HousekeepingSetId_t id;
|
||||
#ifdef FSFW_INTROSPECTION
|
||||
const char* description;
|
||||
#endif
|
||||
std::vector<ParameterIF*> parameterList;
|
||||
|
||||
void registerParameter(ParameterIF* parameter) override;
|
||||
};
|
@ -54,8 +54,8 @@ class HousekeepingSnapshot : public SerializeIF {
|
||||
HousekeepingSnapshot(uint8_t* timeStamp, size_t timeStampSize, LocalPoolObjectBase* dataSetPtr)
|
||||
: timeStamp(timeStamp), timeStampSize(timeStampSize), updateData(dataSetPtr){};
|
||||
|
||||
virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
|
||||
Endianness streamEndianness) const {
|
||||
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
|
||||
Endianness streamEndianness) const {
|
||||
if (timeStamp != nullptr) {
|
||||
/* Endianness will always be MACHINE, so we can simply use memcpy
|
||||
here. */
|
||||
@ -70,15 +70,15 @@ class HousekeepingSnapshot : public SerializeIF {
|
||||
return updateData->serialize(buffer, size, maxSize, streamEndianness);
|
||||
}
|
||||
|
||||
virtual size_t getSerializedSize() const {
|
||||
size_t getSerializedSize() const {
|
||||
if (updateData == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return timeStampSize + updateData->getSerializedSize();
|
||||
}
|
||||
|
||||
virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
|
||||
SerializeIF::Endianness streamEndianness) override {
|
||||
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
|
||||
SerializeIF::Endianness streamEndianness) override {
|
||||
if (*size < timeStampSize) {
|
||||
return SerializeIF::STREAM_TOO_SHORT;
|
||||
}
|
||||
|
@ -1,22 +1,22 @@
|
||||
#ifndef FSFW_INTERNALERROR_INTERNALERRORDATASET_H_
|
||||
#define FSFW_INTERNALERROR_INTERNALERRORDATASET_H_
|
||||
|
||||
#include <fsfw/datapoollocal/LocalPoolVariable.h>
|
||||
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
|
||||
#include <cstdint>
|
||||
#include <fsfw/objectmanager/SystemObjectIF.h>
|
||||
|
||||
enum errorPoolIds { TM_HITS, QUEUE_HITS, STORE_HITS };
|
||||
|
||||
class InternalErrorDataset : public StaticLocalDataSet<3 * sizeof(uint32_t)> {
|
||||
class InternalErrorDataset {
|
||||
public:
|
||||
static constexpr uint8_t ERROR_SET_ID = 0;
|
||||
|
||||
InternalErrorDataset(HasLocalDataPoolIF* owner) : StaticLocalDataSet(owner, ERROR_SET_ID) {}
|
||||
InternalErrorDataset(void* owner) {}
|
||||
|
||||
InternalErrorDataset(object_id_t objectId) : StaticLocalDataSet(sid_t(objectId, ERROR_SET_ID)) {}
|
||||
InternalErrorDataset(object_id_t objectId) {}
|
||||
|
||||
lp_var_t<uint32_t> tmHits = lp_var_t<uint32_t>(sid.objectId, TM_HITS, this);
|
||||
lp_var_t<uint32_t> queueHits = lp_var_t<uint32_t>(sid.objectId, QUEUE_HITS, this);
|
||||
lp_var_t<uint32_t> storeHits = lp_var_t<uint32_t>(sid.objectId, STORE_HITS, this);
|
||||
// lp_var_t<uint32_t> tmHits = lp_var_t<uint32_t>(sid.objectId, TM_HITS, this);
|
||||
// lp_var_t<uint32_t> queueHits = lp_var_t<uint32_t>(sid.objectId, QUEUE_HITS, this);
|
||||
// lp_var_t<uint32_t> storeHits = lp_var_t<uint32_t>(sid.objectId, STORE_HITS, this);
|
||||
};
|
||||
|
||||
#endif /* FSFW_INTERNALERROR_INTERNALERRORDATASET_H_ */
|
||||
|
@ -1,32 +1,24 @@
|
||||
#include "fsfw/internalerror/InternalErrorReporter.h"
|
||||
|
||||
#include "fsfw/datapool/PoolReadGuard.h"
|
||||
#include "fsfw/ipc/MutexFactory.h"
|
||||
#include "fsfw/ipc/QueueFactory.h"
|
||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||
|
||||
InternalErrorReporter::InternalErrorReporter(object_id_t setObjectId, uint32_t messageQueueDepth)
|
||||
: SystemObject(setObjectId),
|
||||
commandQueue(QueueFactory::instance()->createMessageQueue(messageQueueDepth)),
|
||||
poolManager(this, commandQueue),
|
||||
internalErrorSid(setObjectId, InternalErrorDataset::ERROR_SET_ID),
|
||||
internalErrorDataset(this) {
|
||||
mutex = MutexFactory::instance()->createMutex();
|
||||
}
|
||||
|
||||
InternalErrorReporter::~InternalErrorReporter() { MutexFactory::instance()->deleteMutex(mutex); }
|
||||
InternalErrorReporter::~InternalErrorReporter() {
|
||||
MutexFactory::instance()->deleteMutex(mutex);
|
||||
}
|
||||
|
||||
void InternalErrorReporter::setDiagnosticPrintout(bool enable) {
|
||||
this->diagnosticPrintout = enable;
|
||||
}
|
||||
|
||||
ReturnValue_t InternalErrorReporter::performOperation(uint8_t opCode) {
|
||||
CommandMessage message;
|
||||
ReturnValue_t result = commandQueue->receiveMessage(&message);
|
||||
if (result != MessageQueueIF::EMPTY) {
|
||||
poolManager.handleHousekeepingMessage(&message);
|
||||
}
|
||||
|
||||
uint32_t newQueueHits = getAndResetQueueHits();
|
||||
uint32_t newTmHits = getAndResetTmHits();
|
||||
uint32_t newStoreHits = getAndResetStoreHits();
|
||||
@ -50,20 +42,19 @@ ReturnValue_t InternalErrorReporter::performOperation(uint8_t opCode) {
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
PoolReadGuard readGuard(&internalErrorDataset);
|
||||
if (readGuard.getReadResult() == returnvalue::OK) {
|
||||
internalErrorDataset.queueHits.value += newQueueHits;
|
||||
internalErrorDataset.storeHits.value += newStoreHits;
|
||||
internalErrorDataset.tmHits.value += newTmHits;
|
||||
internalErrorDataset.setValidity(true, true);
|
||||
if ((newQueueHits != 0) or (newStoreHits != 0) or (newTmHits != 0)) {
|
||||
internalErrorDataset.setChanged(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
// {
|
||||
// PoolReadGuard readGuard(&internalErrorDataset);
|
||||
// if (readGuard.getReadResult() == returnvalue::OK) {
|
||||
// internalErrorDataset.queueHits.value += newQueueHits;
|
||||
// internalErrorDataset.storeHits.value += newStoreHits;
|
||||
// internalErrorDataset.tmHits.value += newTmHits;
|
||||
// internalErrorDataset.setValidity(true, true);
|
||||
// if ((newQueueHits != 0) or (newStoreHits != 0) or (newTmHits != 0)) {
|
||||
// internalErrorDataset.setChanged(true);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
poolManager.performHkOperation();
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
@ -120,47 +111,15 @@ void InternalErrorReporter::incrementStoreHits() {
|
||||
|
||||
object_id_t InternalErrorReporter::getObjectId() const { return SystemObject::getObjectId(); }
|
||||
|
||||
MessageQueueId_t InternalErrorReporter::getCommandQueue() const {
|
||||
return this->commandQueue->getId();
|
||||
}
|
||||
|
||||
ReturnValue_t InternalErrorReporter::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
||||
LocalDataPoolManager &poolManager) {
|
||||
localDataPoolMap.emplace(errorPoolIds::TM_HITS, &tmHitsEntry);
|
||||
localDataPoolMap.emplace(errorPoolIds::QUEUE_HITS, &queueHitsEntry);
|
||||
localDataPoolMap.emplace(errorPoolIds::STORE_HITS, &storeHitsEntry);
|
||||
poolManager.subscribeForDiagPeriodicPacket(subdp::DiagnosticsHkPeriodicParams(
|
||||
internalErrorSid, false,
|
||||
static_cast<float>(getPeriodicOperationFrequency()) / static_cast<float>(1000.0)));
|
||||
internalErrorDataset.setValidity(true, true);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
dur_millis_t InternalErrorReporter::getPeriodicOperationFrequency() const {
|
||||
return this->executingTask->getPeriodMs();
|
||||
}
|
||||
|
||||
LocalPoolDataSetBase *InternalErrorReporter::getDataSetHandle(sid_t sid) {
|
||||
return &internalErrorDataset;
|
||||
}
|
||||
|
||||
void InternalErrorReporter::setTaskIF(PeriodicTaskIF *task) { this->executingTask = task; }
|
||||
// MessageQueueId_t InternalErrorReporter::getCommandQueue() const {
|
||||
// return this->commandQueue->getId();
|
||||
// }
|
||||
|
||||
ReturnValue_t InternalErrorReporter::initialize() {
|
||||
ReturnValue_t result = poolManager.initialize(commandQueue);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
return SystemObject::initialize();
|
||||
}
|
||||
|
||||
ReturnValue_t InternalErrorReporter::initializeAfterTaskCreation() {
|
||||
return poolManager.initializeAfterTaskCreation();
|
||||
}
|
||||
|
||||
void InternalErrorReporter::setMutexTimeout(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
|
||||
this->timeoutType = timeoutType;
|
||||
this->timeoutMs = timeoutMs;
|
||||
}
|
||||
|
||||
LocalDataPoolManager *InternalErrorReporter::getHkManagerHandle() { return &poolManager; }
|
||||
|
@ -2,7 +2,7 @@
|
||||
#define FSFW_INTERNALERROR_INTERNALERRORREPORTER_H_
|
||||
|
||||
#include "InternalErrorReporterIF.h"
|
||||
#include "fsfw/datapoollocal/LocalDataPoolManager.h"
|
||||
// #include "fsfw/datapoollocal/LocalDataPoolManager.h"
|
||||
#include "fsfw/internalerror/InternalErrorDataset.h"
|
||||
#include "fsfw/ipc/MutexIF.h"
|
||||
#include "fsfw/objectmanager/SystemObject.h"
|
||||
@ -18,8 +18,7 @@
|
||||
*/
|
||||
class InternalErrorReporter : public SystemObject,
|
||||
public ExecutableObjectIF,
|
||||
public InternalErrorReporterIF,
|
||||
public HasLocalDataPoolIF {
|
||||
public InternalErrorReporterIF {
|
||||
public:
|
||||
InternalErrorReporter(object_id_t setObjectId, uint32_t messageQueueDepth = 5);
|
||||
|
||||
@ -35,15 +34,10 @@ class InternalErrorReporter : public SystemObject,
|
||||
virtual ~InternalErrorReporter();
|
||||
|
||||
virtual object_id_t getObjectId() const override;
|
||||
virtual MessageQueueId_t getCommandQueue() const override;
|
||||
virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
|
||||
LocalDataPoolManager& poolManager) override;
|
||||
virtual dur_millis_t getPeriodicOperationFrequency() const override;
|
||||
virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override;
|
||||
LocalDataPoolManager* getHkManagerHandle() override;
|
||||
//virtual MessageQueueId_t getCommandQueue() const override;
|
||||
|
||||
virtual ReturnValue_t initialize() override;
|
||||
virtual ReturnValue_t initializeAfterTaskCreation() override;
|
||||
//virtual ReturnValue_t initializeAfterTaskCreation() override;
|
||||
virtual ReturnValue_t performOperation(uint8_t opCode) override;
|
||||
|
||||
virtual void queueMessageNotSent() override;
|
||||
@ -52,19 +46,13 @@ class InternalErrorReporter : public SystemObject,
|
||||
|
||||
virtual void storeFull() override;
|
||||
|
||||
virtual void setTaskIF(PeriodicTaskIF* task) override;
|
||||
|
||||
protected:
|
||||
MessageQueueIF* commandQueue;
|
||||
LocalDataPoolManager poolManager;
|
||||
|
||||
PeriodicTaskIF* executingTask = nullptr;
|
||||
// MessageQueueIF* commandQueue;
|
||||
|
||||
MutexIF* mutex = nullptr;
|
||||
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
|
||||
uint32_t timeoutMs = 20;
|
||||
|
||||
sid_t internalErrorSid;
|
||||
InternalErrorDataset internalErrorDataset;
|
||||
|
||||
bool diagnosticPrintout = true;
|
||||
@ -72,9 +60,6 @@ class InternalErrorReporter : public SystemObject,
|
||||
uint32_t queueHits = 0;
|
||||
uint32_t tmHits = 0;
|
||||
uint32_t storeHits = 0;
|
||||
PoolEntry<uint32_t> tmHitsEntry = PoolEntry<uint32_t>();
|
||||
PoolEntry<uint32_t> storeHitsEntry = PoolEntry<uint32_t>();
|
||||
PoolEntry<uint32_t> queueHitsEntry = PoolEntry<uint32_t>();
|
||||
|
||||
uint32_t getAndResetQueueHits();
|
||||
void incrementQueueHits();
|
||||
|
1
src/fsfw/introspection/CMakeLists.txt
Normal file
1
src/fsfw/introspection/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE ParameterTypeSelector.cpp)
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user