Compare commits
317 Commits
mueller/ua
...
mohr_intro
Author | SHA1 | Date | |
---|---|---|---|
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 | |||
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*
|
||||
|
195
CHANGELOG.md
195
CHANGELOG.md
@ -8,32 +8,160 @@ 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
|
||||
|
||||
- DHB TM handler `handleDeviceTM` renamed to `handleDeviceTm` and now takes
|
||||
`util::DataWrapper` as the data input argument. This allows more flexibility in the possible
|
||||
types of telemetry.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/669
|
||||
- Add `util::DataWrapper` class inside the `util` module. This is a tagged union which allows
|
||||
to specify raw data either as a classic C-style raw pointer and size or as a `SerializeIF`
|
||||
pointer.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/668
|
||||
- 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)`
|
||||
@ -66,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
|
||||
|
||||
@ -89,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
|
||||
|
||||
@ -222,6 +377,7 @@ https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/593
|
||||
- https://gitlab.kitware.com/cmake/cmake/-/issues/21696
|
||||
Easiest solution for now: Keep this option OFF by default.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/616
|
||||
- Linux HAL: Add wiretapping option for I2C. Enabled with `FSFW_HAL_I2C_WIRETAPPING` defined to 1
|
||||
- Dedicated Version class and constant `fsfw::FSFW_VERSION` containing version information
|
||||
inside `fsfw/version.h`
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/559
|
||||
@ -236,17 +392,6 @@ https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/593
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/590
|
||||
- `Subsystem`: New API to add table and sequence entries
|
||||
|
||||
## HAL
|
||||
|
||||
- SPI: Cache the SPI device in the communication interface. Architecturally, this makes a
|
||||
lot more sense because each ComIF should be responsible for one SPI bus.
|
||||
- SPI: Move the empty transfer to update the line polarity to separate function. This means
|
||||
it is not automatically called when calling the setter function for SPI speed and mode.
|
||||
The user should call this function after locking the CS mutex if multiple SPI devices with
|
||||
differing speeds and modes are attached to one bus.
|
||||
- SPI: Getter functions for SPI speed and mode.
|
||||
- I2C: Add wiretapping option for I2C. Enabled with `FSFW_HAL_I2C_WIRETAPPING` defined to 1.
|
||||
|
||||
## Fixed
|
||||
|
||||
- TCP TMTC Server: `MutexGuard` was not created properly in
|
||||
|
@ -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)
|
||||
|
||||
@ -195,13 +196,13 @@ message(
|
||||
)
|
||||
|
||||
# Check whether the user has already installed ETL first
|
||||
find_package(${FSFW_ETL_LIB_NAME} ${FSFW_ETL_LIB_MAJOR_VERSION} CONFIG QUIET)
|
||||
find_package(${FSFW_ETL_LIB_NAME} ${FSFW_ETL_LIB_MAJOR_VERSION} QUIET)
|
||||
# Not installed, so use FetchContent to download and provide etl
|
||||
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".
|
||||
|
@ -7,14 +7,13 @@ 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 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;
|
||||
|
@ -16,8 +16,8 @@ class CommandActionHelper {
|
||||
public:
|
||||
explicit CommandActionHelper(CommandsActionsIF* owner);
|
||||
virtual ~CommandActionHelper();
|
||||
ReturnValue_t commandAction(object_id_t commandTo, ActionId_t actionId,
|
||||
const uint8_t* data = nullptr, uint32_t size = 0);
|
||||
ReturnValue_t commandAction(object_id_t commandTo, ActionId_t actionId, const uint8_t* data,
|
||||
uint32_t size);
|
||||
ReturnValue_t commandAction(object_id_t commandTo, ActionId_t actionId, SerializeIF* data);
|
||||
ReturnValue_t initialize();
|
||||
ReturnValue_t handleReply(CommandMessage* reply);
|
||||
|
@ -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;
|
||||
};
|
@ -2,10 +2,7 @@
|
||||
#define FSFW_CFDP_H
|
||||
|
||||
#include "cfdp/definitions.h"
|
||||
#include "cfdp/handler/CfdpHandler.h"
|
||||
#include "cfdp/handler/DestHandler.h"
|
||||
#include "cfdp/handler/FaultHandlerBase.h"
|
||||
#include "cfdp/helpers.h"
|
||||
#include "cfdp/tlv/Lv.h"
|
||||
#include "cfdp/tlv/StringLv.h"
|
||||
#include "cfdp/tlv/Tlv.h"
|
||||
|
@ -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;
|
||||
|
||||
|
@ -1,3 +1 @@
|
||||
target_sources(
|
||||
${LIB_FSFW_NAME} PRIVATE SourceHandler.cpp DestHandler.cpp
|
||||
FaultHandlerBase.cpp UserBase.cpp CfdpHandler.cpp)
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE FaultHandlerBase.cpp UserBase.cpp)
|
||||
|
@ -1,134 +0,0 @@
|
||||
#include "CfdpHandler.h"
|
||||
|
||||
#include "fsfw/cfdp/pdu/AckPduReader.h"
|
||||
#include "fsfw/cfdp/pdu/PduHeaderReader.h"
|
||||
#include "fsfw/globalfunctions/arrayprinter.h"
|
||||
#include "fsfw/ipc/QueueFactory.h"
|
||||
#include "fsfw/tmtcservices/TmTcMessage.h"
|
||||
|
||||
using namespace returnvalue;
|
||||
using namespace cfdp;
|
||||
|
||||
CfdpHandler::CfdpHandler(const FsfwHandlerParams& fsfwParams, const CfdpHandlerCfg& cfdpCfg)
|
||||
: SystemObject(fsfwParams.objectId),
|
||||
msgQueue(fsfwParams.msgQueue),
|
||||
destHandler(
|
||||
DestHandlerParams(LocalEntityCfg(cfdpCfg.id, cfdpCfg.indicCfg, cfdpCfg.faultHandler),
|
||||
cfdpCfg.userHandler, cfdpCfg.remoteCfgProvider, cfdpCfg.packetInfoList,
|
||||
cfdpCfg.lostSegmentsList),
|
||||
FsfwParams(fsfwParams.packetDest, nullptr, this, fsfwParams.tcStore,
|
||||
fsfwParams.tmStore)) {
|
||||
destHandler.setMsgQueue(msgQueue);
|
||||
}
|
||||
|
||||
[[nodiscard]] const char* CfdpHandler::getName() const { return "CFDP Handler"; }
|
||||
|
||||
[[nodiscard]] uint32_t CfdpHandler::getIdentifier() const {
|
||||
return destHandler.getDestHandlerParams().cfg.localId.getValue();
|
||||
}
|
||||
|
||||
[[nodiscard]] MessageQueueId_t CfdpHandler::getRequestQueue() const { return msgQueue.getId(); }
|
||||
|
||||
ReturnValue_t CfdpHandler::initialize() {
|
||||
ReturnValue_t result = destHandler.initialize();
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
tcStore = destHandler.getTcStore();
|
||||
tmStore = destHandler.getTmStore();
|
||||
|
||||
return SystemObject::initialize();
|
||||
}
|
||||
|
||||
ReturnValue_t CfdpHandler::performOperation(uint8_t operationCode) {
|
||||
// TODO: Receive TC packets and route them to source and dest handler, depending on which is
|
||||
// correct or more appropriate
|
||||
ReturnValue_t status;
|
||||
ReturnValue_t result = OK;
|
||||
TmTcMessage tmtcMsg;
|
||||
for (status = msgQueue.receiveMessage(&tmtcMsg); status == returnvalue::OK;
|
||||
status = msgQueue.receiveMessage(&tmtcMsg)) {
|
||||
result = handleCfdpPacket(tmtcMsg);
|
||||
if (result != OK) {
|
||||
status = result;
|
||||
}
|
||||
}
|
||||
auto& fsmRes = destHandler.performStateMachine();
|
||||
// TODO: Error handling?
|
||||
while (fsmRes.callStatus == CallStatus::CALL_AGAIN) {
|
||||
destHandler.performStateMachine();
|
||||
// TODO: Error handling?
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
ReturnValue_t CfdpHandler::handleCfdpPacket(TmTcMessage& msg) {
|
||||
auto accessorPair = tcStore->getData(msg.getStorageId());
|
||||
if (accessorPair.first != OK) {
|
||||
return accessorPair.first;
|
||||
}
|
||||
PduHeaderReader reader(accessorPair.second.data(), accessorPair.second.size());
|
||||
ReturnValue_t result = reader.parseData();
|
||||
if (result != returnvalue::OK) {
|
||||
return INVALID_PDU_FORMAT;
|
||||
}
|
||||
// The CFDP distributor should have taken care of ensuring the destination ID is correct
|
||||
PduType type = reader.getPduType();
|
||||
// Only the destination handler can process these PDUs
|
||||
if (type == PduType::FILE_DATA) {
|
||||
// Disable auto-deletion of packet
|
||||
accessorPair.second.release();
|
||||
PacketInfo info(type, msg.getStorageId());
|
||||
result = destHandler.passPacket(info);
|
||||
} else {
|
||||
// Route depending on PDU type and directive type if applicable. It retrieves directive type
|
||||
// from the raw stream for better performance (with sanity and directive code check).
|
||||
// The routing is based on section 4.5 of the CFDP standard which specifies the PDU forwarding
|
||||
// procedure.
|
||||
|
||||
// PDU header only. Invalid supplied data. A directive packet should have a valid data field
|
||||
// with at least one byte being the directive code
|
||||
const uint8_t* pduDataField = reader.getPduDataField();
|
||||
if (pduDataField == nullptr) {
|
||||
return INVALID_PDU_FORMAT;
|
||||
}
|
||||
if (not FileDirectiveReader::checkFileDirective(pduDataField[0])) {
|
||||
return INVALID_DIRECTIVE_FIELD;
|
||||
}
|
||||
auto directive = static_cast<FileDirective>(pduDataField[0]);
|
||||
|
||||
auto passToDestHandler = [&]() {
|
||||
accessorPair.second.release();
|
||||
PacketInfo info(type, msg.getStorageId(), directive);
|
||||
result = destHandler.passPacket(info);
|
||||
};
|
||||
auto passToSourceHandler = [&]() {
|
||||
|
||||
};
|
||||
if (directive == FileDirective::METADATA or directive == FileDirective::EOF_DIRECTIVE or
|
||||
directive == FileDirective::PROMPT) {
|
||||
// Section b) of 4.5.3: These PDUs should always be targeted towards the file receiver a.k.a.
|
||||
// the destination handler
|
||||
passToDestHandler();
|
||||
} else if (directive == FileDirective::FINISH or directive == FileDirective::NAK or
|
||||
directive == FileDirective::KEEP_ALIVE) {
|
||||
// Section c) of 4.5.3: These PDUs should always be targeted towards the file sender a.k.a.
|
||||
// the source handler
|
||||
passToSourceHandler();
|
||||
} else if (directive == FileDirective::ACK) {
|
||||
// Section a): Recipient depends of the type of PDU that is being acknowledged. We can simply
|
||||
// extract the PDU type from the raw stream. If it is an EOF PDU, this packet is passed to
|
||||
// the source handler, for a Finished PDU, it is passed to the destination handler.
|
||||
FileDirective ackedDirective;
|
||||
if (not AckPduReader::checkAckedDirectiveField(pduDataField[1], ackedDirective)) {
|
||||
return INVALID_ACK_DIRECTIVE_FIELDS;
|
||||
}
|
||||
if (ackedDirective == FileDirective::EOF_DIRECTIVE) {
|
||||
passToSourceHandler();
|
||||
} else if (ackedDirective == FileDirective::FINISH) {
|
||||
passToDestHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
#ifndef FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H
|
||||
#define FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "fsfw/cfdp/handler/DestHandler.h"
|
||||
#include "fsfw/objectmanager/SystemObject.h"
|
||||
#include "fsfw/tasks/ExecutableObjectIF.h"
|
||||
#include "fsfw/tmtcservices/AcceptsTelecommandsIF.h"
|
||||
#include "fsfw/tmtcservices/TmTcMessage.h"
|
||||
|
||||
struct FsfwHandlerParams {
|
||||
FsfwHandlerParams(object_id_t objectId, HasFileSystemIF& vfs, AcceptsTelemetryIF& packetDest,
|
||||
StorageManagerIF& tcStore, StorageManagerIF& tmStore, MessageQueueIF& msgQueue)
|
||||
: objectId(objectId),
|
||||
vfs(vfs),
|
||||
packetDest(packetDest),
|
||||
tcStore(tcStore),
|
||||
tmStore(tmStore),
|
||||
msgQueue(msgQueue) {}
|
||||
object_id_t objectId{};
|
||||
HasFileSystemIF& vfs;
|
||||
AcceptsTelemetryIF& packetDest;
|
||||
StorageManagerIF& tcStore;
|
||||
StorageManagerIF& tmStore;
|
||||
MessageQueueIF& msgQueue;
|
||||
};
|
||||
|
||||
struct CfdpHandlerCfg {
|
||||
CfdpHandlerCfg(cfdp::EntityId localId, cfdp::IndicationCfg indicationCfg,
|
||||
cfdp::UserBase& userHandler, cfdp::FaultHandlerBase& userFaultHandler,
|
||||
cfdp::PacketInfoListBase& packetInfo, cfdp::LostSegmentsListBase& lostSegmentsList,
|
||||
cfdp::RemoteConfigTableIF& remoteCfgProvider)
|
||||
: id(std::move(localId)),
|
||||
indicCfg(indicationCfg),
|
||||
packetInfoList(packetInfo),
|
||||
lostSegmentsList(lostSegmentsList),
|
||||
remoteCfgProvider(remoteCfgProvider),
|
||||
userHandler(userHandler),
|
||||
faultHandler(userFaultHandler) {}
|
||||
|
||||
cfdp::EntityId id;
|
||||
cfdp::IndicationCfg indicCfg;
|
||||
cfdp::PacketInfoListBase& packetInfoList;
|
||||
cfdp::LostSegmentsListBase& lostSegmentsList;
|
||||
cfdp::RemoteConfigTableIF& remoteCfgProvider;
|
||||
cfdp::UserBase& userHandler;
|
||||
cfdp::FaultHandlerBase& faultHandler;
|
||||
};
|
||||
|
||||
class CfdpHandler : public SystemObject, public ExecutableObjectIF, public AcceptsTelecommandsIF {
|
||||
public:
|
||||
explicit CfdpHandler(const FsfwHandlerParams& fsfwParams, const CfdpHandlerCfg& cfdpCfg);
|
||||
|
||||
[[nodiscard]] const char* getName() const override;
|
||||
[[nodiscard]] uint32_t getIdentifier() const override;
|
||||
[[nodiscard]] MessageQueueId_t getRequestQueue() const override;
|
||||
|
||||
ReturnValue_t initialize() override;
|
||||
ReturnValue_t performOperation(uint8_t operationCode) override;
|
||||
|
||||
private:
|
||||
MessageQueueIF& msgQueue;
|
||||
cfdp::DestHandler destHandler;
|
||||
StorageManagerIF* tcStore = nullptr;
|
||||
StorageManagerIF* tmStore = nullptr;
|
||||
|
||||
ReturnValue_t handleCfdpPacket(TmTcMessage& msg);
|
||||
};
|
||||
|
||||
#endif // FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H
|
@ -1,480 +0,0 @@
|
||||
#include "DestHandler.h"
|
||||
|
||||
#include <etl/crc32.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "fsfw/FSFW.h"
|
||||
#include "fsfw/cfdp/pdu/EofPduReader.h"
|
||||
#include "fsfw/cfdp/pdu/FileDataReader.h"
|
||||
#include "fsfw/cfdp/pdu/FinishedPduCreator.h"
|
||||
#include "fsfw/cfdp/pdu/PduHeaderReader.h"
|
||||
#include "fsfw/objectmanager.h"
|
||||
#include "fsfw/tmtcservices/TmTcMessage.h"
|
||||
|
||||
using namespace returnvalue;
|
||||
|
||||
cfdp::DestHandler::DestHandler(DestHandlerParams params, FsfwParams fsfwParams)
|
||||
: tlvVec(params.maxTlvsInOnePdu),
|
||||
userTlvVec(params.maxTlvsInOnePdu),
|
||||
dp(std::move(params)),
|
||||
fp(fsfwParams),
|
||||
tp(params.maxFilenameLen) {
|
||||
tp.pduConf.direction = cfdp::Direction::TOWARDS_SENDER;
|
||||
}
|
||||
|
||||
const cfdp::DestHandler::FsmResult& cfdp::DestHandler::performStateMachine() {
|
||||
ReturnValue_t result;
|
||||
uint8_t errorIdx = 0;
|
||||
fsmRes.resetOfIteration();
|
||||
if (fsmRes.step == TransactionStep::IDLE) {
|
||||
for (auto infoIter = dp.packetListRef.begin(); infoIter != dp.packetListRef.end();) {
|
||||
if (infoIter->pduType == PduType::FILE_DIRECTIVE and
|
||||
infoIter->directiveType == FileDirective::METADATA) {
|
||||
result = handleMetadataPdu(*infoIter);
|
||||
checkAndHandleError(result, errorIdx);
|
||||
// Store data was deleted in PDU handler because a store guard is used
|
||||
dp.packetListRef.erase(infoIter++);
|
||||
} else {
|
||||
infoIter++;
|
||||
}
|
||||
}
|
||||
if (fsmRes.step == TransactionStep::IDLE) {
|
||||
// To decrease the already high complexity of the software, all packets arriving before
|
||||
// a metadata PDU are deleted.
|
||||
for (auto infoIter = dp.packetListRef.begin(); infoIter != dp.packetListRef.end();) {
|
||||
fp.tcStore->deleteData(infoIter->storeId);
|
||||
infoIter++;
|
||||
}
|
||||
dp.packetListRef.clear();
|
||||
}
|
||||
|
||||
if (fsmRes.step != TransactionStep::IDLE) {
|
||||
fsmRes.callStatus = CallStatus::CALL_AGAIN;
|
||||
}
|
||||
return updateFsmRes(errorIdx);
|
||||
}
|
||||
if (fsmRes.state == CfdpStates::BUSY_CLASS_1_NACKED) {
|
||||
if (fsmRes.step == TransactionStep::RECEIVING_FILE_DATA_PDUS) {
|
||||
for (auto infoIter = dp.packetListRef.begin(); infoIter != dp.packetListRef.end();) {
|
||||
if (infoIter->pduType == PduType::FILE_DATA) {
|
||||
result = handleFileDataPdu(*infoIter);
|
||||
checkAndHandleError(result, errorIdx);
|
||||
// Store data was deleted in PDU handler because a store guard is used
|
||||
dp.packetListRef.erase(infoIter++);
|
||||
} else if (infoIter->pduType == PduType::FILE_DIRECTIVE and
|
||||
infoIter->directiveType == FileDirective::EOF_DIRECTIVE) {
|
||||
// TODO: Support for check timer missing
|
||||
result = handleEofPdu(*infoIter);
|
||||
checkAndHandleError(result, errorIdx);
|
||||
// Store data was deleted in PDU handler because a store guard is used
|
||||
dp.packetListRef.erase(infoIter++);
|
||||
} else {
|
||||
infoIter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fsmRes.step == TransactionStep::TRANSFER_COMPLETION) {
|
||||
result = handleTransferCompletion();
|
||||
checkAndHandleError(result, errorIdx);
|
||||
}
|
||||
if (fsmRes.step == TransactionStep::SENDING_FINISHED_PDU) {
|
||||
result = sendFinishedPdu();
|
||||
checkAndHandleError(result, errorIdx);
|
||||
finish();
|
||||
}
|
||||
return updateFsmRes(errorIdx);
|
||||
}
|
||||
if (fsmRes.state == CfdpStates::BUSY_CLASS_2_ACKED) {
|
||||
// TODO: Will be implemented at a later stage
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "CFDP state machine for acknowledged mode not implemented yet" << std::endl;
|
||||
#endif
|
||||
}
|
||||
return updateFsmRes(errorIdx);
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::passPacket(PacketInfo packet) {
|
||||
if (dp.packetListRef.full()) {
|
||||
return FAILED;
|
||||
}
|
||||
dp.packetListRef.push_back(packet);
|
||||
return OK;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::initialize() {
|
||||
if (fp.tmStore == nullptr) {
|
||||
fp.tmStore = ObjectManager::instance()->get<StorageManagerIF>(objects::TM_STORE);
|
||||
if (fp.tmStore == nullptr) {
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
if (fp.tcStore == nullptr) {
|
||||
fp.tcStore = ObjectManager::instance()->get<StorageManagerIF>(objects::TC_STORE);
|
||||
if (fp.tcStore == nullptr) {
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
if (fp.msgQueue == nullptr) {
|
||||
return FAILED;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::handleMetadataPdu(const PacketInfo& info) {
|
||||
// Process metadata PDU
|
||||
auto constAccessorPair = fp.tcStore->getData(info.storeId);
|
||||
if (constAccessorPair.first != OK) {
|
||||
// TODO: This is not a CFDP error. Event and/or warning?
|
||||
return constAccessorPair.first;
|
||||
}
|
||||
cfdp::StringLv sourceFileName;
|
||||
cfdp::StringLv destFileName;
|
||||
MetadataInfo metadataInfo(tp.fileSize, sourceFileName, destFileName);
|
||||
cfdp::Tlv* tlvArrayAsPtr = tlvVec.data();
|
||||
metadataInfo.setOptionsArray(&tlvArrayAsPtr, std::nullopt, tlvVec.size());
|
||||
MetadataPduReader reader(constAccessorPair.second.data(), constAccessorPair.second.size(),
|
||||
metadataInfo);
|
||||
ReturnValue_t result = reader.parseData();
|
||||
// TODO: The standard does not really specify what happens if this kind of error happens
|
||||
// I think it might be a good idea to cache some sort of error code, which
|
||||
// is translated into a warning and/or event by an upper layer
|
||||
if (result != OK) {
|
||||
return handleMetadataParseError(result, constAccessorPair.second.data(),
|
||||
constAccessorPair.second.size());
|
||||
}
|
||||
return startTransaction(reader, metadataInfo);
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::handleFileDataPdu(const cfdp::PacketInfo& info) {
|
||||
// Process file data PDU
|
||||
auto constAccessorPair = fp.tcStore->getData(info.storeId);
|
||||
if (constAccessorPair.first != OK) {
|
||||
// TODO: This is not a CFDP error. Event and/or warning?
|
||||
return constAccessorPair.first;
|
||||
}
|
||||
cfdp::FileSize offset;
|
||||
FileDataInfo fdInfo(offset);
|
||||
FileDataReader reader(constAccessorPair.second.data(), constAccessorPair.second.size(), fdInfo);
|
||||
ReturnValue_t result = reader.parseData();
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
size_t fileSegmentLen = 0;
|
||||
const uint8_t* fileData = fdInfo.getFileData(&fileSegmentLen);
|
||||
FileOpParams fileOpParams(tp.destName.data(), fileSegmentLen);
|
||||
fileOpParams.offset = offset.value();
|
||||
if (dp.cfg.indicCfg.fileSegmentRecvIndicRequired) {
|
||||
FileSegmentRecvdParams segParams;
|
||||
segParams.offset = offset.value();
|
||||
segParams.id = tp.transactionId;
|
||||
segParams.length = fileSegmentLen;
|
||||
segParams.recContState = fdInfo.getRecordContinuationState();
|
||||
size_t segmentMetadatLen = 0;
|
||||
auto* segMetadata = fdInfo.getSegmentMetadata(&segmentMetadatLen);
|
||||
segParams.segmentMetadata = {segMetadata, segmentMetadatLen};
|
||||
dp.user.fileSegmentRecvdIndication(segParams);
|
||||
}
|
||||
result = dp.user.vfs.writeToFile(fileOpParams, fileData);
|
||||
if (offset.value() + fileSegmentLen > tp.progress) {
|
||||
tp.progress = offset.value() + fileSegmentLen;
|
||||
}
|
||||
if (result != returnvalue::OK) {
|
||||
// TODO: Proper Error handling
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "File write error" << std::endl;
|
||||
#endif
|
||||
} else {
|
||||
tp.deliveryStatus = FileDeliveryStatus::RETAINED_IN_FILESTORE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::handleEofPdu(const cfdp::PacketInfo& info) {
|
||||
// Process EOF PDU
|
||||
auto constAccessorPair = fp.tcStore->getData(info.storeId);
|
||||
if (constAccessorPair.first != OK) {
|
||||
// TODO: This is not a CFDP error. Event and/or warning?
|
||||
return constAccessorPair.first;
|
||||
}
|
||||
EofInfo eofInfo(nullptr);
|
||||
EofPduReader reader(constAccessorPair.second.data(), constAccessorPair.second.size(), eofInfo);
|
||||
ReturnValue_t result = reader.parseData();
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
// TODO: Error handling
|
||||
if (eofInfo.getConditionCode() == ConditionCode::NO_ERROR) {
|
||||
tp.crc = eofInfo.getChecksum();
|
||||
uint64_t fileSizeFromEof = eofInfo.getFileSize().value();
|
||||
// CFDP 4.6.1.2.9: Declare file size error if progress exceeds file size
|
||||
if (fileSizeFromEof > tp.progress) {
|
||||
// TODO: File size error
|
||||
}
|
||||
tp.fileSize.setFileSize(fileSizeFromEof, std::nullopt);
|
||||
}
|
||||
if (dp.cfg.indicCfg.eofRecvIndicRequired) {
|
||||
dp.user.eofRecvIndication(getTransactionId());
|
||||
}
|
||||
if (fsmRes.step == TransactionStep::RECEIVING_FILE_DATA_PDUS) {
|
||||
if (fsmRes.state == CfdpStates::BUSY_CLASS_1_NACKED) {
|
||||
fsmRes.step = TransactionStep::TRANSFER_COMPLETION;
|
||||
} else if (fsmRes.state == CfdpStates::BUSY_CLASS_2_ACKED) {
|
||||
fsmRes.step = TransactionStep::SENDING_ACK_PDU;
|
||||
}
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::handleMetadataParseError(ReturnValue_t result,
|
||||
const uint8_t* rawData, size_t maxSize) {
|
||||
// TODO: try to extract destination ID for error
|
||||
// TODO: Invalid metadata PDU.
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "Parsing Metadata PDU failed with code " << result << std::endl;
|
||||
#else
|
||||
#endif
|
||||
PduHeaderReader headerReader(rawData, maxSize);
|
||||
result = headerReader.parseData();
|
||||
if (result != OK) {
|
||||
// TODO: Now this really should not happen. Warning or error,
|
||||
// yield or cache appropriate returnvalue
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "Parsing Header failed" << std::endl;
|
||||
#else
|
||||
#endif
|
||||
// TODO: Trigger appropriate event
|
||||
return result;
|
||||
}
|
||||
cfdp::EntityId destId;
|
||||
headerReader.getDestId(destId);
|
||||
RemoteEntityCfg* remoteCfg;
|
||||
if (not dp.remoteCfgTable.getRemoteCfg(destId, &remoteCfg)) {
|
||||
// TODO: No remote config for dest ID. I consider this a configuration error, which is not
|
||||
// covered by the standard.
|
||||
// Warning or error, yield or cache appropriate returnvalue
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "No remote config exists for destination ID" << std::endl;
|
||||
#else
|
||||
#endif
|
||||
// TODO: Trigger appropriate event
|
||||
}
|
||||
// TODO: Appropriate returnvalue?
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::startTransaction(MetadataPduReader& reader, MetadataInfo& info) {
|
||||
if (fsmRes.state != CfdpStates::IDLE) {
|
||||
// According to standard, discard metadata PDU if we are busy
|
||||
return OK;
|
||||
}
|
||||
ReturnValue_t result = OK;
|
||||
fsmRes.step = TransactionStep::TRANSACTION_START;
|
||||
if (reader.getTransmissionMode() == TransmissionMode::UNACKNOWLEDGED) {
|
||||
fsmRes.state = CfdpStates::BUSY_CLASS_1_NACKED;
|
||||
} else if (reader.getTransmissionMode() == TransmissionMode::ACKNOWLEDGED) {
|
||||
fsmRes.state = CfdpStates::BUSY_CLASS_2_ACKED;
|
||||
}
|
||||
tp.checksumType = info.getChecksumType();
|
||||
tp.closureRequested = info.isClosureRequested();
|
||||
size_t sourceNameSize = 0;
|
||||
const uint8_t* sourceNamePtr = info.getSourceFileName().getValue(&sourceNameSize);
|
||||
if (sourceNameSize > tp.sourceName.size()) {
|
||||
// TODO: Warning, event etc.
|
||||
return FAILED;
|
||||
}
|
||||
std::memcpy(tp.sourceName.data(), sourceNamePtr, sourceNameSize);
|
||||
tp.sourceName[sourceNameSize] = '\0';
|
||||
size_t destNameSize = 0;
|
||||
const uint8_t* destNamePtr = info.getDestFileName().getValue(&destNameSize);
|
||||
if (destNameSize > tp.destName.size()) {
|
||||
// TODO: Warning, event etc.
|
||||
return FAILED;
|
||||
}
|
||||
std::memcpy(tp.destName.data(), destNamePtr, destNameSize);
|
||||
tp.destName[destNameSize] = '\0';
|
||||
reader.fillConfig(tp.pduConf);
|
||||
tp.pduConf.direction = Direction::TOWARDS_SENDER;
|
||||
tp.transactionId.entityId = tp.pduConf.sourceId;
|
||||
tp.transactionId.seqNum = tp.pduConf.seqNum;
|
||||
if (not dp.remoteCfgTable.getRemoteCfg(tp.pduConf.sourceId, &tp.remoteCfg)) {
|
||||
// TODO: Warning, event etc.
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "cfdp::DestHandler" << __func__
|
||||
<< ": No remote configuration found for destination ID "
|
||||
<< tp.pduConf.sourceId.getValue() << std::endl;
|
||||
#endif
|
||||
return FAILED;
|
||||
}
|
||||
// If both dest name size and source name size are 0, we are dealing with a metadata only PDU,
|
||||
// so there is no need to create a file or truncate an existing file
|
||||
if (destNameSize > 0 and sourceNameSize > 0) {
|
||||
FilesystemParams fparams(tp.destName.data());
|
||||
// TODO: Filesystem errors?
|
||||
if (dp.user.vfs.fileExists(fparams)) {
|
||||
dp.user.vfs.truncateFile(fparams);
|
||||
} else {
|
||||
result = dp.user.vfs.createFile(fparams);
|
||||
if (result != OK) {
|
||||
// TODO: Handle FS error. This is probably a case for the filestore rejection mechanism of
|
||||
// CFDP.
|
||||
// In any case, it does not really make sense to continue here
|
||||
}
|
||||
}
|
||||
}
|
||||
fsmRes.step = TransactionStep::RECEIVING_FILE_DATA_PDUS;
|
||||
MetadataRecvdParams params(tp.transactionId, tp.pduConf.sourceId);
|
||||
params.fileSize = tp.fileSize.getSize();
|
||||
params.destFileName = tp.destName.data();
|
||||
params.sourceFileName = tp.sourceName.data();
|
||||
params.msgsToUserArray = dynamic_cast<MessageToUserTlv*>(userTlvVec.data());
|
||||
params.msgsToUserLen = info.getOptionsLen();
|
||||
dp.user.metadataRecvdIndication(params);
|
||||
return result;
|
||||
}
|
||||
|
||||
cfdp::CfdpStates cfdp::DestHandler::getCfdpState() const { return fsmRes.state; }
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::handleTransferCompletion() {
|
||||
ReturnValue_t result;
|
||||
if (tp.checksumType != ChecksumType::NULL_CHECKSUM) {
|
||||
result = checksumVerification();
|
||||
if (result != OK) {
|
||||
// TODO: Warning / error handling?
|
||||
}
|
||||
} else {
|
||||
tp.conditionCode = ConditionCode::NO_ERROR;
|
||||
}
|
||||
result = noticeOfCompletion();
|
||||
if (result != OK) {
|
||||
}
|
||||
if (fsmRes.state == CfdpStates::BUSY_CLASS_1_NACKED) {
|
||||
if (tp.closureRequested) {
|
||||
fsmRes.step = TransactionStep::SENDING_FINISHED_PDU;
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
} else if (fsmRes.state == CfdpStates::BUSY_CLASS_2_ACKED) {
|
||||
fsmRes.step = TransactionStep::SENDING_FINISHED_PDU;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void cfdp::DestHandler::finish() {
|
||||
tp.reset();
|
||||
dp.packetListRef.clear();
|
||||
fsmRes.state = CfdpStates::IDLE;
|
||||
fsmRes.step = TransactionStep::IDLE;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::checksumVerification() {
|
||||
std::array<uint8_t, 1024> buf{};
|
||||
// TODO: Checksum verification and notice of completion
|
||||
etl::crc32 crcCalc;
|
||||
uint64_t currentOffset = 0;
|
||||
FileOpParams params(tp.destName.data(), tp.fileSize.value());
|
||||
while (currentOffset < tp.fileSize.value()) {
|
||||
uint64_t readLen;
|
||||
if (currentOffset + buf.size() > tp.fileSize.value()) {
|
||||
readLen = tp.fileSize.value() - currentOffset;
|
||||
} else {
|
||||
readLen = buf.size();
|
||||
}
|
||||
if (readLen > 0) {
|
||||
params.offset = currentOffset;
|
||||
params.size = readLen;
|
||||
auto result = dp.user.vfs.readFromFile(params, buf.data(), buf.size());
|
||||
if (result != OK) {
|
||||
// TODO: I think this is a case for a filestore rejection, but it might sense to print
|
||||
// a warning or trigger an event because this should generally not happen
|
||||
return FAILED;
|
||||
}
|
||||
crcCalc.add(buf.begin(), buf.begin() + readLen);
|
||||
}
|
||||
currentOffset += readLen;
|
||||
}
|
||||
|
||||
uint32_t value = crcCalc.value();
|
||||
if (value == tp.crc) {
|
||||
tp.conditionCode = ConditionCode::NO_ERROR;
|
||||
tp.deliveryCode = FileDeliveryCode::DATA_COMPLETE;
|
||||
} else {
|
||||
// TODO: Proper error handling
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "CRC check for file " << tp.destName.data() << " failed" << std::endl;
|
||||
#endif
|
||||
tp.conditionCode = ConditionCode::FILE_CHECKSUM_FAILURE;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::noticeOfCompletion() {
|
||||
if (dp.cfg.indicCfg.transactionFinishedIndicRequired) {
|
||||
TransactionFinishedParams params(tp.transactionId, tp.conditionCode, tp.deliveryCode,
|
||||
tp.deliveryStatus);
|
||||
dp.user.transactionFinishedIndication(params);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::sendFinishedPdu() {
|
||||
FinishedInfo info(tp.conditionCode, tp.deliveryCode, tp.deliveryStatus);
|
||||
FinishPduCreator finishedPdu(tp.pduConf, info);
|
||||
store_address_t storeId;
|
||||
uint8_t* dataPtr = nullptr;
|
||||
ReturnValue_t result =
|
||||
fp.tcStore->getFreeElement(&storeId, finishedPdu.getSerializedSize(), &dataPtr);
|
||||
if (result != OK) {
|
||||
// TODO: Error handling and event, this is a non CFDP specific error (most likely store is full)
|
||||
return result;
|
||||
}
|
||||
size_t serLen = 0;
|
||||
result = finishedPdu.serialize(dataPtr, serLen, finishedPdu.getSerializedSize());
|
||||
if (result != OK) {
|
||||
// TODO: Error printout, this really should not happen
|
||||
return result;
|
||||
}
|
||||
TmTcMessage msg(storeId);
|
||||
result = fp.msgQueue->sendMessage(fp.packetDest.getReportReceptionQueue(), &msg);
|
||||
if (result != OK) {
|
||||
// TODO: Error handling and event, this is a non CFDP specific error (most likely store is full)
|
||||
return result;
|
||||
}
|
||||
fsmRes.packetsSent++;
|
||||
return OK;
|
||||
}
|
||||
|
||||
cfdp::DestHandler::TransactionStep cfdp::DestHandler::getTransactionStep() const {
|
||||
return fsmRes.step;
|
||||
}
|
||||
|
||||
const cfdp::DestHandler::FsmResult& cfdp::DestHandler::updateFsmRes(uint8_t errors) {
|
||||
fsmRes.errors = errors;
|
||||
fsmRes.result = OK;
|
||||
if (fsmRes.errors > 0) {
|
||||
fsmRes.result = FAILED;
|
||||
}
|
||||
return fsmRes;
|
||||
}
|
||||
|
||||
const cfdp::TransactionId& cfdp::DestHandler::getTransactionId() const { return tp.transactionId; }
|
||||
|
||||
void cfdp::DestHandler::checkAndHandleError(ReturnValue_t result, uint8_t& errorIdx) {
|
||||
if (result != OK and errorIdx < 3) {
|
||||
fsmRes.errorCodes[errorIdx] = result;
|
||||
errorIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
void cfdp::DestHandler::setMsgQueue(MessageQueueIF& queue) { fp.msgQueue = &queue; }
|
||||
|
||||
void cfdp::DestHandler::setEventReporter(EventReportingProxyIF& reporter) {
|
||||
fp.eventReporter = &reporter;
|
||||
}
|
||||
|
||||
const cfdp::DestHandlerParams& cfdp::DestHandler::getDestHandlerParams() const { return dp; }
|
||||
|
||||
StorageManagerIF* cfdp::DestHandler::getTmStore() const { return fp.tmStore; }
|
||||
StorageManagerIF* cfdp::DestHandler::getTcStore() const { return fp.tcStore; }
|
@ -1,202 +0,0 @@
|
||||
#ifndef FSFW_CFDP_CFDPDESTHANDLER_H
|
||||
#define FSFW_CFDP_CFDPDESTHANDLER_H
|
||||
|
||||
#include <etl/list.h>
|
||||
#include <etl/set.h>
|
||||
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#include "RemoteConfigTableIF.h"
|
||||
#include "UserBase.h"
|
||||
#include "defs.h"
|
||||
#include "fsfw/cfdp/handler/mib.h"
|
||||
#include "fsfw/cfdp/pdu/MetadataPduReader.h"
|
||||
#include "fsfw/cfdp/pdu/PduConfig.h"
|
||||
#include "fsfw/container/DynamicFIFO.h"
|
||||
#include "fsfw/storagemanager/StorageManagerIF.h"
|
||||
#include "fsfw/storagemanager/storeAddress.h"
|
||||
#include "fsfw/tmtcservices/AcceptsTelemetryIF.h"
|
||||
|
||||
namespace cfdp {
|
||||
|
||||
struct PacketInfo {
|
||||
PacketInfo(PduType type, store_address_t storeId,
|
||||
std::optional<FileDirective> directive = std::nullopt)
|
||||
: pduType(type), directiveType(directive), storeId(storeId) {}
|
||||
|
||||
PduType pduType = PduType::FILE_DATA;
|
||||
std::optional<FileDirective> directiveType = FileDirective::INVALID_DIRECTIVE;
|
||||
store_address_t storeId = store_address_t::invalid();
|
||||
PacketInfo() = default;
|
||||
};
|
||||
|
||||
template <size_t SIZE>
|
||||
using LostSegmentsList = etl::set<etl::pair<uint64_t, uint64_t>, SIZE>;
|
||||
template <size_t SIZE>
|
||||
using PacketInfoList = etl::list<PacketInfo, SIZE>;
|
||||
using LostSegmentsListBase = etl::iset<etl::pair<uint64_t, uint64_t>>;
|
||||
using PacketInfoListBase = etl::ilist<PacketInfo>;
|
||||
|
||||
struct DestHandlerParams {
|
||||
DestHandlerParams(LocalEntityCfg cfg, UserBase& user, RemoteConfigTableIF& remoteCfgTable,
|
||||
PacketInfoListBase& packetList,
|
||||
// TODO: This container can potentially take tons of space. For a better
|
||||
// memory efficient implementation, an additional abstraction could be
|
||||
// be used so users can use uint32_t as the pair type
|
||||
LostSegmentsListBase& lostSegmentsContainer)
|
||||
: cfg(std::move(cfg)),
|
||||
user(user),
|
||||
remoteCfgTable(remoteCfgTable),
|
||||
packetListRef(packetList),
|
||||
lostSegmentsContainer(lostSegmentsContainer) {}
|
||||
|
||||
LocalEntityCfg cfg;
|
||||
UserBase& user;
|
||||
RemoteConfigTableIF& remoteCfgTable;
|
||||
|
||||
PacketInfoListBase& packetListRef;
|
||||
LostSegmentsListBase& lostSegmentsContainer;
|
||||
uint8_t maxTlvsInOnePdu = 10;
|
||||
size_t maxFilenameLen = 255;
|
||||
};
|
||||
|
||||
struct FsfwParams {
|
||||
FsfwParams(AcceptsTelemetryIF& packetDest, MessageQueueIF* msgQueue,
|
||||
EventReportingProxyIF* eventReporter, StorageManagerIF& tcStore,
|
||||
StorageManagerIF& tmStore)
|
||||
: FsfwParams(packetDest, msgQueue, eventReporter) {
|
||||
this->tcStore = &tcStore;
|
||||
this->tmStore = &tmStore;
|
||||
}
|
||||
|
||||
FsfwParams(AcceptsTelemetryIF& packetDest, MessageQueueIF* msgQueue,
|
||||
EventReportingProxyIF* eventReporter)
|
||||
: packetDest(packetDest), msgQueue(msgQueue), eventReporter(eventReporter) {}
|
||||
AcceptsTelemetryIF& packetDest;
|
||||
MessageQueueIF* msgQueue;
|
||||
EventReportingProxyIF* eventReporter = nullptr;
|
||||
StorageManagerIF* tcStore = nullptr;
|
||||
StorageManagerIF* tmStore = nullptr;
|
||||
};
|
||||
|
||||
enum class CallStatus { DONE, CALL_AFTER_DELAY, CALL_AGAIN };
|
||||
|
||||
class DestHandler {
|
||||
public:
|
||||
enum class TransactionStep {
|
||||
IDLE = 0,
|
||||
TRANSACTION_START = 1,
|
||||
RECEIVING_FILE_DATA_PDUS = 2,
|
||||
SENDING_ACK_PDU = 3,
|
||||
TRANSFER_COMPLETION = 4,
|
||||
SENDING_FINISHED_PDU = 5
|
||||
};
|
||||
|
||||
struct FsmResult {
|
||||
public:
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
CallStatus callStatus = CallStatus::CALL_AFTER_DELAY;
|
||||
TransactionStep step = TransactionStep::IDLE;
|
||||
CfdpStates state = CfdpStates::IDLE;
|
||||
uint32_t packetsSent = 0;
|
||||
uint8_t errors = 0;
|
||||
std::array<ReturnValue_t, 3> errorCodes = {};
|
||||
void resetOfIteration() {
|
||||
result = returnvalue::OK;
|
||||
callStatus = CallStatus::CALL_AFTER_DELAY;
|
||||
packetsSent = 0;
|
||||
errors = 0;
|
||||
errorCodes.fill(returnvalue::OK);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Will be returned if it is advisable to call the state machine operation call again
|
||||
*/
|
||||
ReturnValue_t PARTIAL_SUCCESS = returnvalue::makeCode(0, 2);
|
||||
ReturnValue_t FAILURE = returnvalue::makeCode(0, 3);
|
||||
explicit DestHandler(DestHandlerParams handlerParams, FsfwParams fsfwParams);
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
* - @c returnvalue::OK State machine OK for this execution cycle
|
||||
* - @c CALL_FSM_AGAIN State machine should be called again.
|
||||
*/
|
||||
const FsmResult& performStateMachine();
|
||||
void setMsgQueue(MessageQueueIF& queue);
|
||||
void setEventReporter(EventReportingProxyIF& reporter);
|
||||
|
||||
ReturnValue_t passPacket(PacketInfo packet);
|
||||
|
||||
ReturnValue_t initialize();
|
||||
|
||||
[[nodiscard]] CfdpStates getCfdpState() const;
|
||||
[[nodiscard]] TransactionStep getTransactionStep() const;
|
||||
[[nodiscard]] const TransactionId& getTransactionId() const;
|
||||
[[nodiscard]] const DestHandlerParams& getDestHandlerParams() const;
|
||||
[[nodiscard]] StorageManagerIF* getTcStore() const;
|
||||
[[nodiscard]] StorageManagerIF* getTmStore() const;
|
||||
|
||||
private:
|
||||
struct TransactionParams {
|
||||
// Initialize char vectors with length + 1 for 0 termination
|
||||
explicit TransactionParams(size_t maxFileNameLen)
|
||||
: sourceName(maxFileNameLen + 1), destName(maxFileNameLen + 1) {}
|
||||
|
||||
void reset() {
|
||||
pduConf = PduConfig();
|
||||
transactionId = TransactionId();
|
||||
std::fill(sourceName.begin(), sourceName.end(), '\0');
|
||||
std::fill(destName.begin(), destName.end(), '\0');
|
||||
fileSize.setFileSize(0, false);
|
||||
conditionCode = ConditionCode::NO_ERROR;
|
||||
deliveryCode = FileDeliveryCode::DATA_INCOMPLETE;
|
||||
deliveryStatus = FileDeliveryStatus::DISCARDED_DELIBERATELY;
|
||||
crc = 0;
|
||||
progress = 0;
|
||||
remoteCfg = nullptr;
|
||||
closureRequested = false;
|
||||
checksumType = ChecksumType::NULL_CHECKSUM;
|
||||
}
|
||||
|
||||
ChecksumType checksumType = ChecksumType::NULL_CHECKSUM;
|
||||
bool closureRequested = false;
|
||||
std::vector<char> sourceName;
|
||||
std::vector<char> destName;
|
||||
cfdp::FileSize fileSize;
|
||||
TransactionId transactionId;
|
||||
PduConfig pduConf;
|
||||
ConditionCode conditionCode = ConditionCode::NO_ERROR;
|
||||
FileDeliveryCode deliveryCode = FileDeliveryCode::DATA_INCOMPLETE;
|
||||
FileDeliveryStatus deliveryStatus = FileDeliveryStatus::DISCARDED_DELIBERATELY;
|
||||
uint32_t crc = 0;
|
||||
uint64_t progress = 0;
|
||||
RemoteEntityCfg* remoteCfg = nullptr;
|
||||
};
|
||||
|
||||
std::vector<cfdp::Tlv> tlvVec;
|
||||
std::vector<cfdp::Tlv> userTlvVec;
|
||||
DestHandlerParams dp;
|
||||
FsfwParams fp;
|
||||
TransactionParams tp;
|
||||
FsmResult fsmRes;
|
||||
|
||||
ReturnValue_t startTransaction(MetadataPduReader& reader, MetadataInfo& info);
|
||||
ReturnValue_t handleMetadataPdu(const PacketInfo& info);
|
||||
ReturnValue_t handleFileDataPdu(const PacketInfo& info);
|
||||
ReturnValue_t handleEofPdu(const PacketInfo& info);
|
||||
ReturnValue_t handleMetadataParseError(ReturnValue_t result, const uint8_t* rawData,
|
||||
size_t maxSize);
|
||||
ReturnValue_t handleTransferCompletion();
|
||||
ReturnValue_t sendFinishedPdu();
|
||||
ReturnValue_t noticeOfCompletion();
|
||||
ReturnValue_t checksumVerification();
|
||||
const FsmResult& updateFsmRes(uint8_t errors);
|
||||
void checkAndHandleError(ReturnValue_t result, uint8_t& errorIdx);
|
||||
void finish();
|
||||
};
|
||||
|
||||
} // namespace cfdp
|
||||
|
||||
#endif // FSFW_CFDP_CFDPDESTHANDLER_H
|
@ -1 +0,0 @@
|
||||
#include "SourceHandler.h"
|
@ -1,6 +0,0 @@
|
||||
#ifndef FSFW_CFDP_CFDPSOURCEHANDLER_H
|
||||
#define FSFW_CFDP_CFDPSOURCEHANDLER_H
|
||||
|
||||
class SourceHandler {};
|
||||
|
||||
#endif // FSFW_CFDP_CFDPSOURCEHANDLER_H
|
@ -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,10 +12,11 @@ ControllerBase::ControllerBase(object_id_t setObjectId, object_id_t parentId,
|
||||
mode(MODE_OFF),
|
||||
submode(SUBMODE_NONE),
|
||||
modeHelper(this),
|
||||
healthHelper(this, setObjectId) {
|
||||
auto mqArgs = MqArgs(setObjectId, static_cast<void*>(this));
|
||||
commandQueue = QueueFactory::instance()->createMessageQueue(
|
||||
commandQueueDepth, MessageQueueMessage::MAX_MESSAGE_SIZE, &mqArgs);
|
||||
healthHelper(this, setObjectId),
|
||||
actionHelper(this, commandQueue),
|
||||
datapoolHelper(this),
|
||||
housekeepingHelper(this) {
|
||||
commandQueue = QueueFactory::instance()->createMessageQueue(commandQueueDepth);
|
||||
}
|
||||
|
||||
ControllerBase::~ControllerBase() { QueueFactory::instance()->deleteMessageQueue(commandQueue); }
|
||||
@ -47,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;
|
||||
}
|
||||
|
||||
@ -66,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;
|
||||
@ -101,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_ */
|
@ -26,7 +26,11 @@ void AssemblyBase::performChildOperation() {
|
||||
|
||||
void AssemblyBase::startTransition(Mode_t mode, Submode_t submode) {
|
||||
doStartTransition(mode, submode);
|
||||
triggerModeHelperEvents(mode, submode);
|
||||
if (modeHelper.isForced()) {
|
||||
triggerEvent(FORCING_MODE, mode, submode);
|
||||
} else {
|
||||
triggerEvent(CHANGING_MODE, mode, submode);
|
||||
}
|
||||
}
|
||||
|
||||
void AssemblyBase::doStartTransition(Mode_t mode, Submode_t submode) {
|
||||
@ -73,10 +77,9 @@ bool AssemblyBase::handleChildrenChangedHealth() {
|
||||
}
|
||||
HealthState healthState = healthHelper.healthTable->getHealth(iter->first);
|
||||
if (healthState == HasHealthIF::NEEDS_RECOVERY) {
|
||||
triggerEvent(TRYING_RECOVERY, iter->first, 0);
|
||||
triggerEvent(TRYING_RECOVERY);
|
||||
recoveryState = RECOVERY_STARTED;
|
||||
recoveringDevice = iter;
|
||||
// The user needs to take care of commanding the children off in commandChildren
|
||||
doStartTransition(targetMode, targetSubmode);
|
||||
} else {
|
||||
triggerEvent(CHILD_CHANGED_HEALTH);
|
||||
@ -225,9 +228,6 @@ ReturnValue_t AssemblyBase::handleHealthReply(CommandMessage* message) {
|
||||
bool AssemblyBase::checkAndHandleRecovery() {
|
||||
switch (recoveryState) {
|
||||
case RECOVERY_STARTED:
|
||||
// The recovery was already start in #handleChildrenChangedHealth and we just need
|
||||
// to wait for an off time period.
|
||||
// TODO: make time period configurable
|
||||
recoveryState = RECOVERY_WAIT;
|
||||
recoveryOffTimer.resetTimer();
|
||||
return true;
|
||||
@ -266,11 +266,3 @@ void AssemblyBase::overwriteDeviceHealth(object_id_t objectId, HasHealthIF::Heal
|
||||
modeHelper.setForced(true);
|
||||
sendHealthCommand(childrenMap[objectId].commandQueue, EXTERNAL_CONTROL);
|
||||
}
|
||||
|
||||
void AssemblyBase::triggerModeHelperEvents(Mode_t mode, Submode_t submode) {
|
||||
if (modeHelper.isForced()) {
|
||||
triggerEvent(FORCING_MODE, mode, submode);
|
||||
} else {
|
||||
triggerEvent(CHANGING_MODE, mode, submode);
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,7 @@
|
||||
* Documentation: Dissertation Baetz p.156, 157.
|
||||
*
|
||||
* This class reduces the complexity of controller components which would
|
||||
* otherwise be needed for the handling of redundant devices. However, it can also be used to
|
||||
* manage the mode keeping and recovery of non-redundant devices
|
||||
* otherwise be needed for the handling of redundant devices.
|
||||
*
|
||||
* The template class monitors mode and health state of its children
|
||||
* and checks availability of devices on every detected change.
|
||||
@ -27,9 +26,11 @@
|
||||
*
|
||||
* Important:
|
||||
*
|
||||
* The implementation must call #registerChild for all commanded children during initialization.
|
||||
* The implementation must call registerChild(object_id_t child)
|
||||
* for all commanded children during initialization.
|
||||
* The implementation must call the initialization function of the base class.
|
||||
* (This will call the function in SubsystemBase)
|
||||
*
|
||||
*/
|
||||
class AssemblyBase : public SubsystemBase {
|
||||
public:
|
||||
@ -46,10 +47,9 @@ class AssemblyBase : public SubsystemBase {
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Command children to reach [mode,submode] combination. Can be done by setting
|
||||
* #commandsOutstanding correctly, or using #executeTable. In case of an FDIR recovery,
|
||||
* the user needs to ensure that the target devices are healthy. If a device is not healthy,
|
||||
* a recovery might be on-going and the device needs to be commanded to off first.
|
||||
* Command children to reach [mode,submode] combination
|
||||
* Can be done by setting #commandsOutstanding correctly,
|
||||
* or using executeTable()
|
||||
* @param mode
|
||||
* @param submode
|
||||
* @return
|
||||
@ -120,19 +120,8 @@ class AssemblyBase : public SubsystemBase {
|
||||
|
||||
virtual ReturnValue_t handleHealthReply(CommandMessage *message);
|
||||
|
||||
/**
|
||||
* @brief Default periodic handler
|
||||
* @details
|
||||
* This is the default periodic handler which will be called by the SubsystemBase
|
||||
* performOperation. It performs the child transitions or reacts to changed health/mode states
|
||||
* of children objects
|
||||
*/
|
||||
virtual void performChildOperation() override;
|
||||
virtual void performChildOperation();
|
||||
|
||||
/**
|
||||
* This function handles changed mode or health states of children
|
||||
* @return
|
||||
*/
|
||||
bool handleChildrenChanged();
|
||||
|
||||
/**
|
||||
@ -145,37 +134,12 @@ class AssemblyBase : public SubsystemBase {
|
||||
|
||||
bool handleChildrenChangedHealth();
|
||||
|
||||
/**
|
||||
* Core transition handler. The default implementation will only do something if
|
||||
* #commandsOutstanding is smaller or equal to zero, which means that all mode commands
|
||||
* from the #doPerformTransition call were executed successfully.
|
||||
*
|
||||
* Unless a second step was requested, the function will then use #checkChildrenState to
|
||||
* determine whether the target mode was reached.
|
||||
*
|
||||
* There is some special handling for certain (internal) modes:
|
||||
* - A second step is necessary. #commandChildren will be performed again
|
||||
* - The device health was overwritten. #commandChildren will be called
|
||||
* - A recovery is ongoing. #checkAndHandleRecovery will be called.
|
||||
*/
|
||||
virtual void handleChildrenTransition();
|
||||
|
||||
ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, uint32_t *msToReachTheMode);
|
||||
|
||||
/**
|
||||
* Calls #doStartTransition and triggers an informative event as well that the mode will
|
||||
* change
|
||||
* @param mode
|
||||
* @param submode
|
||||
*/
|
||||
virtual void startTransition(Mode_t mode, Submode_t submode);
|
||||
|
||||
/**
|
||||
* This function starts the transition by setting the internal #targetSubmode and #targetMode
|
||||
* variables and then calling the #commandChildren function.
|
||||
* @param mode
|
||||
* @param submode
|
||||
*/
|
||||
virtual void doStartTransition(Mode_t mode, Submode_t submode);
|
||||
|
||||
virtual bool isInTransition();
|
||||
@ -196,7 +160,7 @@ class AssemblyBase : public SubsystemBase {
|
||||
* Manages recovery of a device
|
||||
* @return true if recovery is still ongoing, false else.
|
||||
*/
|
||||
virtual bool checkAndHandleRecovery();
|
||||
bool checkAndHandleRecovery();
|
||||
|
||||
/**
|
||||
* Helper method to overwrite health state of one of the children.
|
||||
@ -204,8 +168,6 @@ class AssemblyBase : public SubsystemBase {
|
||||
* @param objectId Must be a registered child.
|
||||
*/
|
||||
void overwriteDeviceHealth(object_id_t objectId, HasHealthIF::HealthState oldHealth);
|
||||
|
||||
void triggerModeHelperEvents(Mode_t mode, Submode_t submode);
|
||||
};
|
||||
|
||||
#endif /* FSFW_DEVICEHANDLERS_ASSEMBLYBASE_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),
|
||||
@ -40,9 +39,8 @@ DeviceHandlerBase::DeviceHandlerBase(object_id_t setObjectId, object_id_t device
|
||||
childTransitionDelay(5000),
|
||||
transitionSourceMode(_MODE_POWER_DOWN),
|
||||
transitionSourceSubMode(SUBMODE_NONE) {
|
||||
auto mqArgs = MqArgs(setObjectId, static_cast<void*>(this));
|
||||
commandQueue = QueueFactory::instance()->createMessageQueue(
|
||||
cmdQueueSize, MessageQueueMessage::MAX_MESSAGE_SIZE, &mqArgs);
|
||||
cmdQueueSize, MessageQueueMessage::MAX_MESSAGE_SIZE);
|
||||
insertInCommandMap(RAW_COMMAND_ID);
|
||||
cookieInfo.state = COOKIE_UNUSED;
|
||||
cookieInfo.pendingCommand = deviceCommandMap.end();
|
||||
@ -50,18 +48,16 @@ DeviceHandlerBase::DeviceHandlerBase(object_id_t setObjectId, object_id_t device
|
||||
printWarningOrError(sif::OutputTypes::OUT_ERROR, "DeviceHandlerBase", returnvalue::FAILED,
|
||||
"Invalid cookie");
|
||||
}
|
||||
if (this->fdirInstance == nullptr) {
|
||||
this->fdirInstance = new DeviceHandlerFailureIsolation(setObjectId, defaultFdirParentId);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -112,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;
|
||||
@ -127,18 +120,6 @@ ReturnValue_t DeviceHandlerBase::initialize() {
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
if (this->fdirInstance == nullptr) {
|
||||
this->fdirInstance =
|
||||
new DeviceHandlerFailureIsolation(this->getObjectId(), defaultFdirParentId);
|
||||
}
|
||||
|
||||
if (this->parent != objects::NO_OBJECT) {
|
||||
HasModesIF* modeIF = ObjectManager::instance()->get<HasModesIF>(this->parent);
|
||||
HasHealthIF* healthIF = ObjectManager::instance()->get<HasHealthIF>(this->parent);
|
||||
if (modeIF != nullptr and healthIF != nullptr) {
|
||||
setParentQueue(modeIF->getCommandQueue());
|
||||
}
|
||||
}
|
||||
|
||||
communicationInterface =
|
||||
ObjectManager::instance()->get<DeviceCommunicationIF>(deviceCommunicationId);
|
||||
@ -215,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;
|
||||
@ -225,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;
|
||||
}
|
||||
@ -300,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) {
|
||||
@ -369,17 +354,20 @@ 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();
|
||||
}
|
||||
} break;
|
||||
case _MODE_WAIT_OFF: {
|
||||
uint32_t currentUptime;
|
||||
Clock::getUptime(¤tUptime);
|
||||
|
||||
if (powerSwitcher == nullptr) {
|
||||
setMode(MODE_OFF);
|
||||
break;
|
||||
}
|
||||
uint32_t currentUptime;
|
||||
Clock::getUptime(¤tUptime);
|
||||
if (currentUptime - timeoutStart >= powerSwitcher->getSwitchDelayMs()) {
|
||||
triggerEvent(MODE_TRANSITION_FAILED, PowerSwitchIF::SWITCH_TIMEOUT, 0);
|
||||
setMode(MODE_ERROR_ON);
|
||||
@ -427,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.
|
||||
@ -442,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);
|
||||
@ -467,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) {
|
||||
@ -542,15 +530,15 @@ ReturnValue_t DeviceHandlerBase::updatePeriodicReply(bool enable, DeviceCommandI
|
||||
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) {
|
||||
@ -580,24 +568,21 @@ void DeviceHandlerBase::setMode(Mode_t newMode, uint8_t newSubmode) {
|
||||
mode = newMode;
|
||||
modeChanged();
|
||||
setNormalDatapoolEntriesInvalid();
|
||||
if (newMode == MODE_OFF) {
|
||||
disableCommandsAndReplies();
|
||||
}
|
||||
if (!isTransitionalMode()) {
|
||||
modeHelper.modeChanged(newMode, newSubmode);
|
||||
announceMode(false);
|
||||
}
|
||||
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);
|
||||
}
|
||||
@ -1086,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);
|
||||
@ -1145,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);
|
||||
}
|
||||
@ -1288,7 +1273,6 @@ void DeviceHandlerBase::handleDeviceTm(const SerializeIF& dataSet, DeviceCommand
|
||||
if (iter->second.command != deviceCommandMap.end()) {
|
||||
MessageQueueId_t queueId = iter->second.command->second.sendReplyTo;
|
||||
|
||||
// This may fail, but we'll ignore the fault.
|
||||
if (queueId != NO_COMMANDER) {
|
||||
// This may fail, but we'll ignore the fault.
|
||||
actionHelper.reportData(queueId, replyId, const_cast<SerializeIF*>(&dataSet));
|
||||
@ -1324,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;
|
||||
@ -1467,50 +1452,25 @@ void DeviceHandlerBase::setTaskIF(PeriodicTaskIF* task) { executingTask = task;
|
||||
void DeviceHandlerBase::debugInterface(uint8_t positionTracker, object_id_t objectId,
|
||||
uint32_t parameter) {}
|
||||
|
||||
Submode_t DeviceHandlerBase::getInitialSubmode() { return SUBMODE_NONE; }
|
||||
|
||||
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, getInitialSubmode());
|
||||
startTransition(MODE_ON, SUBMODE_NONE);
|
||||
}
|
||||
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;
|
||||
@ -1519,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) {
|
||||
@ -1566,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()) {
|
||||
@ -1576,28 +1540,4 @@ MessageQueueId_t DeviceHandlerBase::getCommanderQueueId(DeviceCommandId_t replyI
|
||||
return commandIter->second.sendReplyTo;
|
||||
}
|
||||
|
||||
void DeviceHandlerBase::setCustomFdir(FailureIsolationBase* fdir) { this->fdirInstance = fdir; }
|
||||
|
||||
void DeviceHandlerBase::setParent(object_id_t parent) { this->parent = parent; }
|
||||
|
||||
void DeviceHandlerBase::setPowerSwitcher(PowerSwitchIF* switcher) {
|
||||
this->powerSwitcher = switcher;
|
||||
}
|
||||
|
||||
void DeviceHandlerBase::disableCommandsAndReplies() {
|
||||
for (auto& command : deviceCommandMap) {
|
||||
if (command.second.isExecuting) {
|
||||
command.second.isExecuting = false;
|
||||
}
|
||||
}
|
||||
for (auto& reply : deviceReplyMap) {
|
||||
if (!reply.second.periodic) {
|
||||
if (reply.second.countdown != nullptr) {
|
||||
reply.second.countdown->timeOut();
|
||||
} else {
|
||||
reply.second.delayCycles = 0;
|
||||
}
|
||||
reply.second.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
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"
|
||||
@ -23,7 +22,6 @@
|
||||
#include "fsfw/serviceinterface/serviceInterfaceDefintions.h"
|
||||
#include "fsfw/tasks/ExecutableObjectIF.h"
|
||||
#include "fsfw/tasks/PeriodicTaskIF.h"
|
||||
#include "fsfw/util/dataWrapper.h"
|
||||
|
||||
namespace Factory {
|
||||
void setStaticFrameworkObjectIds();
|
||||
@ -85,7 +83,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
public HasHealthIF,
|
||||
public HasActionsIF,
|
||||
public ReceivesParameterMessagesIF,
|
||||
public HasLocalDataPoolIF {
|
||||
public HasDatapoolIF {
|
||||
friend void(Factory::setStaticFrameworkObjectIds)();
|
||||
|
||||
public:
|
||||
@ -103,9 +101,6 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
DeviceHandlerBase(object_id_t setObjectId, object_id_t deviceCommunication, CookieIF *comCookie,
|
||||
FailureIsolationBase *fdirInstance = nullptr, size_t cmdQueueSize = 20);
|
||||
|
||||
void setCustomFdir(FailureIsolationBase *fdir);
|
||||
void setParent(object_id_t parent);
|
||||
void setPowerSwitcher(PowerSwitchIF *switcher);
|
||||
void setHkDestination(object_id_t hkDestination);
|
||||
|
||||
/**
|
||||
@ -117,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
|
||||
@ -204,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,
|
||||
@ -314,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
|
||||
@ -458,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,
|
||||
@ -467,19 +435,19 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
* @brief This is a helper method to insert replies in the reply map.
|
||||
* @param deviceCommand Identifier of the reply to add.
|
||||
* @param maxDelayCycles The maximum number of delay cycles the reply waits
|
||||
* until it times out.
|
||||
* until it times out.
|
||||
* @param periodic Indicates if the command is periodic (i.e. it is sent
|
||||
* by the device repeatedly without request) or not. Default is aperiodic (0).
|
||||
* Please note that periodic replies are disabled by default. You can enable them with
|
||||
* #updatePeriodicReply
|
||||
* by the device repeatedly without request) or not. Default is aperiodic (0).
|
||||
* Please note that periodic replies are disabled by default. You can enable them with
|
||||
* #updatePeriodicReply
|
||||
* @param countdown Instead of using maxDelayCycles to timeout a device reply it is also possible
|
||||
* to provide a pointer to a Countdown object which will signal the timeout
|
||||
* when expired
|
||||
* to provide a pointer to a Countdown object which will signal the timeout
|
||||
* when expired
|
||||
* @return - @c returnvalue::OK when the command was successfully inserted,
|
||||
* - @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);
|
||||
|
||||
/**
|
||||
@ -527,12 +495,6 @@ 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.
|
||||
@ -552,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
|
||||
@ -570,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;
|
||||
@ -659,12 +608,6 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
virtual void debugInterface(uint8_t positionTracker = 0, object_id_t objectId = 0,
|
||||
uint32_t parameter = 0);
|
||||
|
||||
/**
|
||||
* @brief Can be overwritten by a child to specify the initial submode when device has been set
|
||||
* to startup immediately.
|
||||
*/
|
||||
virtual Submode_t getInitialSubmode();
|
||||
|
||||
protected:
|
||||
static const uint8_t INTERFACE_ID = CLASS_ID::DEVICE_HANDLER_BASE;
|
||||
|
||||
@ -755,7 +698,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
/* Action helper for HasActionsIF */
|
||||
ActionHelper actionHelper;
|
||||
/* Housekeeping Manager */
|
||||
LocalDataPoolManager poolManager;
|
||||
DatapoolHelper datapoolHelper;
|
||||
|
||||
/**
|
||||
* @brief Information about commands
|
||||
@ -783,18 +726,11 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
* This is used to keep track of pending replies.
|
||||
*/
|
||||
struct DeviceReplyInfo {
|
||||
//! For Command-Reply combinations:
|
||||
//! The maximum number of cycles the handler should wait for a reply
|
||||
//! to this command.
|
||||
//!
|
||||
//! Reply Only:
|
||||
//! For periodic replies, this variable will be the number of delay cycles between the replies.
|
||||
//! For the non-periodic variant, this variable is not used as there is no meaningful
|
||||
//! definition for delay
|
||||
uint16_t maxDelayCycles;
|
||||
//! This variable will be set to #maxDelayCycles if a reply is expected.
|
||||
//! For non-periodic replies without a command, this variable is unused.
|
||||
//! A runtime value of 0 means there is no reply is currently expected.
|
||||
//! The currently remaining cycles the handler should wait for a reply,
|
||||
//! 0 means there is no reply expected
|
||||
uint16_t delayCycles;
|
||||
size_t replyLen = 0; //!< Expected size of the reply.
|
||||
//! if this is !=0, the delayCycles will not be reset to 0 but to
|
||||
@ -803,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
|
||||
@ -835,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
|
||||
@ -850,7 +786,6 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
/** Pointer to the used FDIR instance. If not provided by child,
|
||||
* default class is instantiated. */
|
||||
FailureIsolationBase *fdirInstance;
|
||||
object_id_t parent = objects::NO_OBJECT;
|
||||
|
||||
//! To correctly delete the default instance.
|
||||
bool defaultFDIRUsed;
|
||||
@ -953,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.
|
||||
@ -1050,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();
|
||||
|
||||
@ -1325,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);
|
||||
@ -1341,11 +1268,6 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
void printWarningOrError(sif::OutputTypes errorType, const char *functionName,
|
||||
ReturnValue_t errorCode = returnvalue::FAILED,
|
||||
const char *errorPrint = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Disables all commands and replies when device is set to MODE_OFF
|
||||
*/
|
||||
void disableCommandsAndReplies();
|
||||
};
|
||||
|
||||
#endif /* FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ */
|
||||
|
@ -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"
|
||||
|
||||
@ -29,7 +30,6 @@ ReturnValue_t DeviceHandlerFailureIsolation::eventReceived(EventMessage* event)
|
||||
switch (event->getEvent()) {
|
||||
case HasModesIF::MODE_TRANSITION_FAILED:
|
||||
case HasModesIF::OBJECT_IN_INVALID_MODE:
|
||||
case DeviceHandlerIF::DEVICE_WANTS_HARD_REBOOT:
|
||||
// We'll try a recovery as long as defined in MAX_REBOOT.
|
||||
// Might cause some AssemblyBase cycles, so keep number low.
|
||||
handleRecovery(event->getEvent());
|
||||
@ -78,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.
|
||||
@ -109,7 +116,6 @@ class DeviceHandlerIF {
|
||||
static const Event INVALID_DEVICE_COMMAND = MAKE_EVENT(8, severity::LOW);
|
||||
static const Event MONITORING_LIMIT_EXCEEDED = MAKE_EVENT(9, severity::LOW);
|
||||
static const Event MONITORING_AMBIGUOUS = MAKE_EVENT(10, severity::HIGH);
|
||||
static const Event DEVICE_WANTS_HARD_REBOOT = MAKE_EVENT(11, severity::HIGH);
|
||||
|
||||
static const uint8_t INTERFACE_ID = CLASS_ID::DEVICE_HANDLER_IF;
|
||||
|
||||
@ -163,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
|
||||
|
@ -1,10 +1,9 @@
|
||||
#ifndef FSFW_DEVICEHANDLERS_DEVICETMREPORTINGWRAPPER_H_
|
||||
#define FSFW_DEVICEHANDLERS_DEVICETMREPORTINGWRAPPER_H_
|
||||
|
||||
#include "fsfw/action/HasActionsIF.h"
|
||||
#include "fsfw/objectmanager/SystemObjectIF.h"
|
||||
#include "fsfw/serialize/SerializeIF.h"
|
||||
#include "fsfw/util/dataWrapper.h"
|
||||
#include "../action/HasActionsIF.h"
|
||||
#include "../objectmanager/SystemObjectIF.h"
|
||||
#include "../serialize/SerializeIF.h"
|
||||
|
||||
class DeviceTmReportingWrapper : public SerializeIF {
|
||||
public:
|
||||
|
@ -8,9 +8,7 @@ HealthDevice::HealthDevice(object_id_t setObjectId, MessageQueueId_t parentQueue
|
||||
parentQueue(parentQueue),
|
||||
commandQueue(),
|
||||
healthHelper(this, setObjectId) {
|
||||
auto mqArgs = MqArgs(setObjectId, static_cast<void*>(this));
|
||||
commandQueue = QueueFactory::instance()->createMessageQueue(
|
||||
3, MessageQueueMessage::MAX_MESSAGE_SIZE, &mqArgs);
|
||||
commandQueue = QueueFactory::instance()->createMessageQueue(3);
|
||||
}
|
||||
|
||||
HealthDevice::~HealthDevice() { QueueFactory::instance()->deleteMessageQueue(commandQueue); }
|
||||
|
@ -15,15 +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();
|
||||
auto mqArgs = MqArgs(setObjectId, static_cast<void*>(this));
|
||||
eventReportQueue = QueueFactory::instance()->createMessageQueue(
|
||||
MAX_EVENTS_PER_CYCLE, EventMessage::EVENT_MESSAGE_SIZE, &mqArgs);
|
||||
eventQueueDepth, EventMessage::EVENT_MESSAGE_SIZE, &mqArgs);
|
||||
}
|
||||
|
||||
EventManager::~EventManager() {
|
||||
listenerList.clear();
|
||||
QueueFactory::instance()->deleteMessageQueue(eventReportQueue);
|
||||
MutexFactory::instance()->deleteMutex(mutex);
|
||||
}
|
||||
@ -38,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);
|
||||
}
|
||||
@ -73,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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -9,9 +9,8 @@
|
||||
FailureIsolationBase::FailureIsolationBase(object_id_t owner, object_id_t parent,
|
||||
uint8_t messageDepth, uint8_t parameterDomainBase)
|
||||
: ownerId(owner), faultTreeParent(parent), parameterDomainBase(parameterDomainBase) {
|
||||
auto mqArgs = MqArgs(owner, static_cast<void*>(this));
|
||||
eventQueue = QueueFactory::instance()->createMessageQueue(
|
||||
messageDepth, EventMessage::EVENT_MESSAGE_SIZE, &mqArgs);
|
||||
eventQueue =
|
||||
QueueFactory::instance()->createMessageQueue(messageDepth, EventMessage::EVENT_MESSAGE_SIZE);
|
||||
}
|
||||
|
||||
FailureIsolationBase::~FailureIsolationBase() {
|
||||
@ -24,7 +23,7 @@ FailureIsolationBase::~FailureIsolationBase() {
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
manager->unsubscribeFromAllEvents(eventQueue->getId(), ownerId);
|
||||
manager->unregisterListener(eventQueue->getId());
|
||||
QueueFactory::instance()->deleteMessageQueue(eventQueue);
|
||||
}
|
||||
|
||||
@ -62,12 +61,11 @@ ReturnValue_t FailureIsolationBase::initialize() {
|
||||
ObjectManager::instance()->get<ConfirmsFailuresIF>(faultTreeParent);
|
||||
if (parentIF == nullptr) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "FailureIsolationBase::intialize: Parent object "
|
||||
<< "invalid" << std::endl;
|
||||
sif::error << "Make sure it implements ConfirmsFailuresIF" << std::endl;
|
||||
#else
|
||||
sif::printError("FailureIsolationBase::intialize: Parent object invalid\n");
|
||||
sif::printError("Make sure it implements ConfirmsFailuresIF\n");
|
||||
sif::error << "FailureIsolationBase::intialize: Parent object"
|
||||
<< "invalid." << std::endl;
|
||||
#endif
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "Make sure it implements ConfirmsFailuresIF." << std::endl;
|
||||
#endif
|
||||
return ObjectManagerIF::CHILD_INIT_FAILED;
|
||||
return returnvalue::FAILED;
|
||||
|
@ -12,12 +12,13 @@
|
||||
class FailureIsolationBase : public ConfirmsFailuresIF, public HasParametersIF {
|
||||
public:
|
||||
static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::FDIR_1;
|
||||
//! FDIR has an internal state, which changed from par2 (oldState) to par1 (newState).
|
||||
static const Event FDIR_CHANGED_STATE = MAKE_EVENT(1, severity::INFO);
|
||||
//! FDIR tries to restart device. Par1: event that caused recovery.
|
||||
static const Event FDIR_STARTS_RECOVERY = MAKE_EVENT(2, severity::MEDIUM);
|
||||
//! FDIR turns off device. Par1: event that caused recovery.
|
||||
static const Event FDIR_TURNS_OFF_DEVICE = MAKE_EVENT(3, severity::MEDIUM);
|
||||
static const Event FDIR_CHANGED_STATE =
|
||||
MAKE_EVENT(1, severity::INFO); //!< FDIR has an internal state, which changed from par2
|
||||
//!< (oldState) to par1 (newState).
|
||||
static const Event FDIR_STARTS_RECOVERY = MAKE_EVENT(
|
||||
2, severity::MEDIUM); //!< FDIR tries to restart device. Par1: event that caused recovery.
|
||||
static const Event FDIR_TURNS_OFF_DEVICE = MAKE_EVENT(
|
||||
3, severity::MEDIUM); //!< FDIR turns off device. Par1: event that caused recovery.
|
||||
|
||||
FailureIsolationBase(object_id_t owner, object_id_t parent = objects::NO_OBJECT,
|
||||
uint8_t messageDepth = 10, uint8_t parameterDomainBase = 0xF0);
|
||||
|
@ -3,159 +3,94 @@
|
||||
#include <fsfw/serviceinterface/ServiceInterface.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
DleParser::DleParser(SimpleRingBuffer& decodeRingBuf, DleEncoder& decoder, BufPair encodedBuf,
|
||||
BufPair decodedBuf, UserHandler handler, void* args)
|
||||
BufPair decodedBuf)
|
||||
: decodeRingBuf(decodeRingBuf),
|
||||
decoder(decoder),
|
||||
encodedBuf(encodedBuf),
|
||||
decodedBuf(decodedBuf),
|
||||
handler(handler),
|
||||
ctx(args) {
|
||||
if (handler == nullptr) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "DleParser::DleParser: Invalid user handler" << std::endl;
|
||||
#else
|
||||
sif::printError("DleParser::DleParser: Invalid user handler\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
decodedBuf(decodedBuf) {}
|
||||
|
||||
ReturnValue_t DleParser::passData(uint8_t* data, size_t len) {
|
||||
if (data == nullptr or len == 0 or handler == nullptr) {
|
||||
ReturnValue_t DleParser::passData(const uint8_t* data, size_t len) {
|
||||
if (data == nullptr or len == 0) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
size_t copyIntoRingBufFromHere = 0;
|
||||
size_t copyAmount = len;
|
||||
size_t startIdx = 0;
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
bool startFoundInThisPacket = false;
|
||||
for (size_t idx = 0; idx < len; idx++) {
|
||||
if (data[idx] == DleEncoder::STX_CHAR) {
|
||||
if (not startFound and not startFoundInThisPacket) {
|
||||
startIdx = idx;
|
||||
copyIntoRingBufFromHere = idx;
|
||||
copyAmount = len - idx;
|
||||
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 {
|
||||
// Maybe print warning, should not happen
|
||||
decodeRingBuf.clear();
|
||||
// might be lost packet, so we should advance the read pointer
|
||||
// without skipping the STX
|
||||
readSize = vectorIdx;
|
||||
ErrorInfo info;
|
||||
info.len = idx;
|
||||
prepareErrorContext(ErrorTypes::CONSECUTIVE_STX_CHARS, info);
|
||||
handler(ctx);
|
||||
copyIntoRingBufFromHere = idx;
|
||||
copyAmount = len - idx;
|
||||
setErrorContext(ErrorTypes::CONSECUTIVE_STX_CHARS, info);
|
||||
return POSSIBLE_PACKET_LOSS;
|
||||
}
|
||||
startFound = true;
|
||||
startFoundInThisPacket = true;
|
||||
} else if (data[idx] == DleEncoder::ETX_CHAR) {
|
||||
if (startFoundInThisPacket) {
|
||||
size_t readLen = 0;
|
||||
}
|
||||
// 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;
|
||||
result = decoder.decode(data + startIdx, idx + 1 - startIdx, &readLen, decodedBuf.first,
|
||||
decodedBuf.second, &decodedLen);
|
||||
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;
|
||||
this->handler(ctx);
|
||||
} else if (result == DleEncoder::STREAM_TOO_SHORT) {
|
||||
readSize = ++vectorIdx;
|
||||
return returnvalue::OK;
|
||||
} else {
|
||||
// invalid packet, skip.
|
||||
readSize = ++vectorIdx;
|
||||
ErrorInfo info;
|
||||
info.res = result;
|
||||
prepareErrorContext(ErrorTypes::DECODING_BUF_TOO_SMALL, info);
|
||||
handler(ctx);
|
||||
} else {
|
||||
ErrorInfo info;
|
||||
info.res = result;
|
||||
prepareErrorContext(ErrorTypes::DECODING_BUF_TOO_SMALL, info);
|
||||
handler(ctx);
|
||||
}
|
||||
decodeRingBuf.clear();
|
||||
if ((idx + 1) < len) {
|
||||
copyIntoRingBufFromHere = idx + 1;
|
||||
copyAmount = len - idx - 1;
|
||||
} else {
|
||||
copyAmount = 0;
|
||||
}
|
||||
} else if (startFound) {
|
||||
// ETX found but STX was found in another mini packet. Reconstruct the full packet
|
||||
// to decode it
|
||||
result = decodeRingBuf.writeData(data, idx + 1);
|
||||
if (result != returnvalue::OK) {
|
||||
ErrorInfo info;
|
||||
info.res = result;
|
||||
prepareErrorContext(ErrorTypes::RING_BUF_ERROR, info);
|
||||
handler(ctx);
|
||||
}
|
||||
size_t fullEncodedLen = decodeRingBuf.getAvailableReadData();
|
||||
if (fullEncodedLen > encodedBuf.second) {
|
||||
ErrorInfo info;
|
||||
info.len = fullEncodedLen;
|
||||
prepareErrorContext(ErrorTypes::ENCODED_BUF_TOO_SMALL, info);
|
||||
handler(ctx);
|
||||
decodeRingBuf.clear();
|
||||
} else {
|
||||
size_t decodedLen = 0;
|
||||
size_t readLen = 0;
|
||||
decodeRingBuf.readData(encodedBuf.first, fullEncodedLen, true);
|
||||
result = decoder.decode(encodedBuf.first, fullEncodedLen, &readLen, decodedBuf.first,
|
||||
decodedBuf.second, &decodedLen);
|
||||
if (result == returnvalue::OK) {
|
||||
if (this->handler != nullptr) {
|
||||
ctx.setType(ContextType::PACKET_FOUND);
|
||||
ctx.decodedPacket.first = decodedBuf.first;
|
||||
ctx.decodedPacket.second = decodedLen;
|
||||
this->handler(ctx);
|
||||
}
|
||||
} else if (result == DleEncoder::STREAM_TOO_SHORT) {
|
||||
ErrorInfo info;
|
||||
info.res = result;
|
||||
prepareErrorContext(ErrorTypes::DECODING_BUF_TOO_SMALL, info);
|
||||
handler(ctx);
|
||||
} else {
|
||||
ErrorInfo info;
|
||||
info.res = result;
|
||||
prepareErrorContext(ErrorTypes::DECODE_ERROR, info);
|
||||
handler(ctx);
|
||||
}
|
||||
decodeRingBuf.clear();
|
||||
startFound = false;
|
||||
startFoundInThisPacket = false;
|
||||
if ((idx + 1) < len) {
|
||||
copyIntoRingBufFromHere = idx + 1;
|
||||
copyAmount = len - idx - 1;
|
||||
} else {
|
||||
copyAmount = 0;
|
||||
}
|
||||
setErrorContext(ErrorTypes::DECODE_ERROR, info);
|
||||
return POSSIBLE_PACKET_LOSS;
|
||||
}
|
||||
} else {
|
||||
// End data without preceeding STX
|
||||
// might be lost packet, so we should advance the read pointer
|
||||
readSize = ++vectorIdx;
|
||||
ErrorInfo info;
|
||||
info.len = idx + 1;
|
||||
prepareErrorContext(ErrorTypes::CONSECUTIVE_ETX_CHARS, info);
|
||||
handler(ctx);
|
||||
decodeRingBuf.clear();
|
||||
if ((idx + 1) < len) {
|
||||
copyIntoRingBufFromHere = idx + 1;
|
||||
copyAmount = len - idx - 1;
|
||||
} else {
|
||||
copyAmount = 0;
|
||||
}
|
||||
info.len = 0;
|
||||
setErrorContext(ErrorTypes::CONSECUTIVE_ETX_CHARS, info);
|
||||
return POSSIBLE_PACKET_LOSS;
|
||||
}
|
||||
startFoundInThisPacket = false;
|
||||
startFound = false;
|
||||
}
|
||||
}
|
||||
if (copyAmount > 0) {
|
||||
result = decodeRingBuf.writeData(data + copyIntoRingBufFromHere, copyAmount);
|
||||
if (result != returnvalue::OK) {
|
||||
ErrorInfo info;
|
||||
info.res = result;
|
||||
prepareErrorContext(ErrorTypes::RING_BUF_ERROR, info);
|
||||
handler(ctx);
|
||||
}
|
||||
}
|
||||
return returnvalue::OK;
|
||||
return NO_PACKET_FOUND;
|
||||
}
|
||||
|
||||
void DleParser::defaultFoundPacketHandler(uint8_t* packet, size_t len, void* args) {
|
||||
@ -169,8 +104,12 @@ void DleParser::defaultFoundPacketHandler(uint8_t* packet, size_t len, void* arg
|
||||
#endif
|
||||
}
|
||||
|
||||
void DleParser::defaultErrorHandler(ErrorTypes err, ErrorInfo ctx) {
|
||||
switch (err) {
|
||||
void DleParser::defaultErrorHandler() {
|
||||
if (ctx.getType() != DleParser::ContextType::ERROR) {
|
||||
errorPrinter("No error");
|
||||
return;
|
||||
}
|
||||
switch (ctx.error.first) {
|
||||
case (ErrorTypes::NONE): {
|
||||
errorPrinter("No error");
|
||||
break;
|
||||
@ -186,8 +125,9 @@ void DleParser::defaultErrorHandler(ErrorTypes err, ErrorInfo ctx) {
|
||||
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.len);
|
||||
if (err == ErrorTypes::ENCODED_BUF_TOO_SMALL) {
|
||||
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);
|
||||
@ -218,13 +158,16 @@ void DleParser::errorPrinter(const char* str, const char* opt) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void DleParser::prepareErrorContext(ErrorTypes err, ErrorInfo info) {
|
||||
void DleParser::setErrorContext(ErrorTypes err, ErrorInfo info) {
|
||||
ctx.setType(ContextType::ERROR);
|
||||
ctx.error.first = err;
|
||||
ctx.error.second = info;
|
||||
}
|
||||
|
||||
void DleParser::reset() {
|
||||
startFound = false;
|
||||
decodeRingBuf.clear();
|
||||
ReturnValue_t DleParser::confirmBytesRead(size_t bytesRead) {
|
||||
return decodeRingBuf.deleteData(bytesRead);
|
||||
}
|
||||
|
||||
const DleParser::Context& DleParser::getContext() { return ctx; }
|
||||
|
||||
void DleParser::reset() { decodeRingBuf.clear(); }
|
||||
|
@ -18,9 +18,11 @@
|
||||
*/
|
||||
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 { PACKET_FOUND, ERROR };
|
||||
enum class ContextType { NONE, PACKET_FOUND, ERROR };
|
||||
|
||||
enum class ErrorTypes {
|
||||
NONE,
|
||||
@ -41,7 +43,7 @@ class DleParser {
|
||||
|
||||
struct Context {
|
||||
public:
|
||||
Context(void* args) : userArgs(args) { setType(ContextType::PACKET_FOUND); }
|
||||
Context() { setType(ContextType::PACKET_FOUND); }
|
||||
|
||||
void setType(ContextType type) {
|
||||
this->type = type;
|
||||
@ -58,14 +60,11 @@ class DleParser {
|
||||
|
||||
BufPair decodedPacket = {};
|
||||
ErrorPair error;
|
||||
void* userArgs;
|
||||
|
||||
private:
|
||||
ContextType type;
|
||||
};
|
||||
|
||||
using UserHandler = void (*)(const Context& ctx);
|
||||
|
||||
/**
|
||||
* Base class constructor
|
||||
* @param decodeRingBuf Ring buffer used to store multiple packets to allow detecting DLE packets
|
||||
@ -79,7 +78,7 @@ class DleParser {
|
||||
* @param args Arbitrary user argument
|
||||
*/
|
||||
DleParser(SimpleRingBuffer& decodeRingBuf, DleEncoder& decoder, BufPair encodedBuf,
|
||||
BufPair decodedBuf, UserHandler handler, void* args);
|
||||
BufPair decodedBuf);
|
||||
|
||||
/**
|
||||
* This function allows to pass new data into the parser. It then scans for DLE packets
|
||||
@ -88,8 +87,13 @@ class DleParser {
|
||||
* @param len
|
||||
* @return
|
||||
*/
|
||||
ReturnValue_t passData(uint8_t* data, size_t len);
|
||||
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
|
||||
@ -104,11 +108,11 @@ class DleParser {
|
||||
* - 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
|
||||
*/
|
||||
static void defaultErrorHandler(ErrorTypes err, ErrorInfo ctx);
|
||||
void defaultErrorHandler();
|
||||
|
||||
static void errorPrinter(const char* str, const char* opt = nullptr);
|
||||
|
||||
void prepareErrorContext(ErrorTypes err, ErrorInfo ctx);
|
||||
void setErrorContext(ErrorTypes err, ErrorInfo ctx);
|
||||
/**
|
||||
* Resets the parser by resetting the internal states and clearing the decoding ring buffer
|
||||
*/
|
||||
@ -119,7 +123,5 @@ class DleParser {
|
||||
DleEncoder& decoder;
|
||||
BufPair encodedBuf;
|
||||
BufPair decodedBuf;
|
||||
UserHandler handler = nullptr;
|
||||
Context ctx;
|
||||
bool startFound = false;
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user