Compare commits

...

247 Commits

Author SHA1 Message Date
b0b75b85a9 Merge branch 'meier/sequenceCount' into meier/develop 2022-05-11 16:54:06 +02:00
bc994595da sequence count init value 2022-05-11 14:31:49 +02:00
de2d4da161 Merge branch 'eive/develop' into meier/develop 2022-05-05 12:28:34 +02:00
19bd26d998 Merge branch 'meier/develop' of https://egit.irs.uni-stuttgart.de/eive/fsfw into meier/develop 2022-05-05 09:15:23 +02:00
80cb0e682f Merge pull request 'Update GPIO API' (#76) from mueller/update-gpio-api into eive/develop
Reviewed-on: #76
2022-05-04 14:03:28 +02:00
8ee26f81f9 dedicated returnvalue for line get failure 2022-05-04 10:36:32 +02:00
3556eca8e8 error check on line getter 2022-05-04 10:33:55 +02:00
a9041b84a3 update read gpio API 2022-05-04 10:27:20 +02:00
83d9dbc052 Merge pull request 'improved i2c error printout' (#75) from mueller/i2c-error-handling-improvement into eive/develop
Reviewed-on: #75
2022-05-03 16:50:48 +02:00
2220120d54 improved i2c error printout 2022-05-03 16:43:15 +02:00
15eb22f9ee Merge remote-tracking branch 'origin/eive/develop' into meier/develop 2022-05-03 13:03:44 +02:00
7f6c8b8b12 Merge remote-tracking branch 'upstream/development' into eive/develop 2022-05-02 16:15:27 +02:00
1a07864a5f Merge pull request 'CCSDS Time CUC Tests' (#593) from gaisser/fsfw:gaisser_cuc_tests into development
Reviewed-on: fsfw/fsfw#593
2022-05-02 15:29:48 +02:00
3e9d6bdbb9 Merge branch 'development' into gaisser_cuc_tests 2022-05-02 15:24:32 +02:00
c295539c79 Merge pull request 'Fixes to allow compilation on MacOS' (#611) from mueller/mac-os into development
Reviewed-on: fsfw/fsfw#611
2022-05-02 15:22:19 +02:00
cddf16f941 Merge branch 'development' into mueller/mac-os 2022-05-02 15:15:53 +02:00
a3dee05fe3 Merge pull request 'space packet bug fix' (#607) from meier/spacePacketBugFix into development
Reviewed-on: fsfw/fsfw#607
2022-05-02 15:15:28 +02:00
8edf4c3c8d Merge branch 'development' into meier/spacePacketBugFix 2022-05-02 15:10:32 +02:00
8cc94a55ab Merge pull request 'Bump Catch2 dependency & Catch2 pre-installed for CI/CD' (#605) from mueller/bump-catch2 into development
Reviewed-on: fsfw/fsfw#605
2022-05-02 15:03:08 +02:00
daffb6b666 Merge branch 'development' into gaisser_cuc_tests 2022-05-02 14:45:33 +02:00
789668ae50 Merge branch 'eive/develop' into meier/develop 2022-05-02 14:45:23 +02:00
7cfb1e6076 Merge branch 'development' into mueller/bump-catch2 2022-05-02 14:36:44 +02:00
cc36baff78 Merge pull request 'Hotfix CMake & ETL' (#604) from mueller/hotfix-etl into development
Reviewed-on: fsfw/fsfw#604
2022-05-02 14:36:29 +02:00
4c65109ac0 Merge branch 'development' into meier/spacePacketBugFix 2022-05-02 14:36:24 +02:00
861bd15eda Merge branch 'development' into mueller/hotfix-etl 2022-05-02 14:20:40 +02:00
7b979eadff Merge pull request 'fix compiler warnings and auto-formatting' (#598) from eive/fsfw:mueller/compiler-warning-fixes-upstream-2 into development
Reviewed-on: fsfw/fsfw#598
2022-05-02 14:17:00 +02:00
16714ceb40 Merge branch 'development' into gaisser_cuc_tests 2022-05-02 13:58:17 +02:00
fea301bcc9 Merge remote-tracking branch 'origin/mueller/hotfix-etl' into mueller/mac-os 2022-05-02 09:35:28 +02:00
77450eb4b7 removed flag which does not exist 2022-05-02 09:09:41 +02:00
28015c4735 it compiles and runs 2022-05-01 17:48:49 +02:00
7d61e67d20 more macos changes 2022-04-30 19:02:41 +02:00
afcbc8be0a changes for MacOS 2022-04-30 18:40:22 +02:00
7760b3063e Merge pull request 'New Subsystem adder functions' (#73) from mueller/new-ss-adder-functions-eive into eive/develop
Reviewed-on: #73
2022-04-30 17:00:34 +02:00
d04f88bee0 Merge branch 'eive/develop' into mueller/new-ss-adder-functions-eive 2022-04-30 16:59:48 +02:00
e867d09111 Merge pull request 'Update from upstream' (#72) from mueller/update-from-upstream into eive/develop
Reviewed-on: #72
2022-04-30 16:55:35 +02:00
7a2269262b Merge branch 'development' into meier/spacePacketBugFix 2022-04-29 08:45:10 +02:00
9731dc1e61 space packet bug fix 2022-04-29 07:47:23 +02:00
43aad11859 space packet bugfix 2022-04-29 07:43:52 +02:00
3225a8e350 added option to change initial submode 2022-04-28 16:48:14 +02:00
1c4ea6dd0d Merge branch 'eive/develop' into mueller/new-ss-adder-functions-eive 2022-04-28 14:34:20 +02:00
e2eb4bfea4 Merge remote-tracking branch 'upstream/development' into mueller/update-from-upstream 2022-04-28 14:30:08 +02:00
29b0a352fc added new functions to add sequences and tables 2022-04-28 14:26:00 +02:00
41682aab3f Merge branch 'eive/develop' into meier/develop 2022-04-28 11:45:27 +02:00
8642b13fd1 Merge branch 'mueller/hotfix-etl' into mueller/bump-catch2 2022-04-27 21:55:45 +02:00
6aa72892ed clean usage of FetchContent_MakeAvailable 2022-04-27 21:53:57 +02:00
70f0a72f1b added explicit checkout of v3.0.0-preview5 2022-04-27 13:54:15 +02:00
b5d890eedd install Catch2 for docker_d2 and update Jenkinsfile 2022-04-27 13:43:49 +02:00
50b1b48678 link Catch2 issue 2022-04-27 13:36:26 +02:00
0e0dbc74aa Merge branch 'mueller/hotfix-etl' into mueller/bump-catch2 2022-04-27 09:45:42 +02:00
8c34051d8b bump Catch2 revision 2022-04-27 09:45:20 +02:00
b00d83cb1a bump ETL revision 2022-04-27 09:41:16 +02:00
17e609c3a5 some more var replacements 2022-04-27 09:37:11 +02:00
64f0166b64 hotfix for new ETL dependency 2022-04-27 09:16:52 +02:00
c80f06fbcb hotfix for ETL lib dep 2022-04-27 09:08:17 +02:00
70eb8325a0 Merge remote-tracking branch 'upstream/development' into mueller/compiler-warning-fixes-upstream-2 2022-04-27 08:47:45 +02:00
e796f82203 Merge pull request 'gpio bug fix' (#602) from KSat/fsfw:hoffmann/gpioBugfix into development
Reviewed-on: fsfw/fsfw#602
2022-04-27 08:41:54 +02:00
5b7ca8c13c update CHANGELOG.md, apply afmt 2022-04-27 08:39:21 +02:00
d61fe7db93 Merge pull request 'Update FSFW' (#70) from mueller/update-fsfw into eive/develop
Reviewed-on: #70
2022-04-27 07:48:56 +02:00
c1be1fe232 update CMakeLists.txt etl handling 2022-04-26 20:06:26 +02:00
ec2e274f22 find_package call for Catch2 quiet 2022-04-26 16:31:45 +02:00
c5a7b98a7d name correction 2022-04-26 16:24:14 +02:00
6a8da303fb exporting etl target 2022-04-26 14:06:30 +02:00
3d047f9629 trying to export ETL lib 2022-04-26 11:15:24 +02:00
1739edd9b0 warning fix for modern compilers 2022-04-26 10:32:37 +02:00
466a3639a5 Merge remote-tracking branch 'upstream/development' into mueller/update-fsfw 2022-04-26 10:02:43 +02:00
900ef5b912 option to use coutdwon object to time out replies 2022-04-26 09:07:03 +02:00
031739ef51 Merge branch 'development' into hoffmann/gpioBugfix 2022-04-25 15:45:21 +02:00
80be937d9d Merge remote-tracking branch 'upstream/development' into mueller/compiler-warning-fixes-upstream-2 2022-04-25 15:24:17 +02:00
b8516b15cb Merge pull request 'Added an additional conversion function' (#584) from eive/fsfw:mueller/clock-addition into development
Reviewed-on: fsfw/fsfw#584
2022-04-25 15:22:07 +02:00
ac5a54b5da Merge branch 'development' into mueller/clock-addition 2022-04-25 15:12:24 +02:00
29015b340b update changelog 2022-04-25 15:10:50 +02:00
64274acbeb Merge pull request 'Add ETL dependency' (#592) from KSat/fsfw:mueller/add-etl-dependency into development
Reviewed-on: fsfw/fsfw#592
2022-04-25 15:08:05 +02:00
ff98c42514 Merge branch 'development' into mueller/clock-addition 2022-04-25 14:54:22 +02:00
126ac52975 Merge pull request 'Allow passing a MqArgs struct to the MQ creation' (#583) from eive/fsfw:mueller/ipc-pass-arbitrary-args-to-mq into development
Reviewed-on: fsfw/fsfw#583
2022-04-25 14:43:52 +02:00
70d3197212 gpio init bug fix
Return values from configureGpios were not checked
2022-04-25 14:32:05 +02:00
dd90980520 push test 2022-04-25 14:19:03 +02:00
280b641cbc Merge pull request 'mueller/extend-version-class-fork' (#69) from mueller/extend-version-class-fork into eive/develop
Reviewed-on: #69
2022-04-22 20:06:49 +02:00
24ef96d1b8 Merge branch 'eive/develop' into mueller/extend-version-class-fork 2022-04-22 16:04:55 +02:00
18f9958332 add git CST and sha info to version ctor 2022-04-22 15:39:44 +02:00
68231db9a1 changelog typo 2022-04-22 15:37:14 +02:00
85e849ca00 small remaining fix 2022-04-22 15:10:08 +02:00
617d41c7d5 maybe this fixed CI/CD issues 2022-04-22 15:08:16 +02:00
cccdced74d unique helper file name 2022-04-22 15:00:04 +02:00
750369b0a6 small addition and possible fix 2022-04-22 14:55:17 +02:00
539e01deee minor form change 2022-04-22 14:53:12 +02:00
4079edc80e update changelog 2022-04-22 14:52:55 +02:00
a569990ca2 fix tests 2022-04-22 14:50:50 +02:00
9c7eba4431 git version handler more robust now 2022-04-22 14:47:28 +02:00
513ae9dc10 prefixed git info variable 2022-04-22 14:38:32 +02:00
effecd4662 include cmake-modules manually instead
- Instead of using FetchContent
- Separate folder for easier update and for distintion
- LICENSE file included
2022-04-22 14:33:34 +02:00
b951cb736a add fsfw specific cmake message prefix 2022-04-22 14:08:26 +02:00
7e1aed6ad9 apply afmt 2022-04-22 14:04:30 +02:00
07155e2546 extend version handling with git describe 2022-04-22 14:04:18 +02:00
8c6c8ad3c0 exntend version class to allow add info 2022-04-22 11:58:44 +02:00
befaca78c6 Merge pull request 'space packet default length' (#66) from meier/develop into eive/develop
Reviewed-on: #66
2022-04-21 14:30:34 +02:00
af4f002a25 Merge branch 'eive/develop' into meier/develop 2022-04-21 14:30:11 +02:00
9f7b9be800 space packet default length 0 2022-04-21 14:24:20 +02:00
2c0f3b52e9 Merge pull request 'Space Packet Default Length' (#65) from meier/develop into eive/develop
Reviewed-on: #65
2022-04-21 14:11:09 +02:00
aa1ea33647 Merge pull request 'LIS3 handler nullptr check' (#64) from mueller/lis3-handler-nullptr-check into eive/develop
Reviewed-on: #64
2022-04-21 13:55:06 +02:00
9798b6b4ab Merge pull request 'Merge upstream and apply auto-formatter' (#63) from merge-upstream into eive/develop
Reviewed-on: #63
2022-04-21 13:54:20 +02:00
f0d7eaf35a Merge branch 'merge-upstream' into mueller/lis3-handler-nullptr-check 2022-04-21 10:38:43 +02:00
b128ef9da9 Merge remote-tracking branch 'upstream/development' into merge-upstream 2022-04-21 10:37:01 +02:00
085213c60f add nullptr check 2022-04-21 10:30:46 +02:00
613dbe9592 default argument 2022-04-21 09:33:06 +02:00
e949368b06 Merge pull request 'uart cookie API change' (#62) from mueller/uart-com-if-tweaks into eive/develop
Reviewed-on: #62
2022-04-13 15:04:03 +02:00
4d49cb6a3c Merge pull request 'added DLE parser' (#61) from mueller/dle-parser into eive/develop
Reviewed-on: #61
2022-04-13 15:03:27 +02:00
935a8e13a5 uart cookie API change 2022-04-13 14:57:43 +02:00
5ff88129b8 added DLE parser 2022-04-13 14:45:36 +02:00
ce17be63f4 Merge pull request 'Pool Entry Update V2' (#60) from mueller/pool-entry-update-v2 into eive/develop
Reviewed-on: #60
2022-04-12 19:28:01 +02:00
2734d9d758 Merge pull request 'fix compiler warnings' (#59) from mueller/compiler-warning-fixes-upstream into eive/develop
Reviewed-on: #59
2022-04-12 19:27:37 +02:00
352ab43c1f Merge remote-tracking branch 'upstream/development' into mueller/add-etl-dependency 2022-04-12 17:11:41 +02:00
07f5dbb9ac Merge branch 'development' into mueller/compiler-warning-fixes-upstream-2 2022-04-12 17:08:56 +02:00
97e98eae24 Merge branch 'development' into mueller/ipc-pass-arbitrary-args-to-mq 2022-04-12 17:06:30 +02:00
5ac88f2b15 Merge pull request 'added missing empty implementation in STM32 SPI HAL' (#599) from mueller/small-stm32-hal-bugifx into development
Reviewed-on: fsfw/fsfw#599
2022-04-11 17:48:48 +02:00
b03a6684f9 Merge branch 'development' into mueller/small-stm32-hal-bugifx 2022-04-11 17:34:39 +02:00
afce942bf8 Merge branch 'development' into mueller/compiler-warning-fixes-upstream-2 2022-04-11 17:33:04 +02:00
7c2e50b665 added related PR in changelog 2022-04-11 17:32:01 +02:00
c04ca704d2 Merge pull request 'progagate reply returnvalue' (#597) from eive/fsfw:mueller/propagate-pool-manage-retval into development
Reviewed-on: fsfw/fsfw#597
2022-04-11 17:31:54 +02:00
6aa54fe1d4 added missing empty implementation 2022-04-11 17:30:27 +02:00
a1d7a56dfa small fix 2022-04-11 17:14:43 +02:00
cb78fefbb3 afmt 2022-04-11 17:14:04 +02:00
c55925959b Merge branch 'mueller/add-etl-dependency' of https://egit.irs.uni-stuttgart.de/KSat/fsfw into mueller/add-etl-dependency 2022-04-11 16:44:30 +02:00
4f0669c574 doc update 2022-04-11 16:44:20 +02:00
f0d996ffd2 Merge branch 'development' into mueller/add-etl-dependency 2022-04-11 16:17:46 +02:00
f4d05c2c9c Merge branch 'mueller/ipc-pass-arbitrary-args-to-mq' of https://egit.irs.uni-stuttgart.de/eive/fsfw into mueller/ipc-pass-arbitrary-args-to-mq 2022-04-11 16:13:55 +02:00
d1151ca707 changelog update 2022-04-11 16:13:47 +02:00
82f46992f6 Merge branch 'development' into mueller/ipc-pass-arbitrary-args-to-mq 2022-04-11 16:11:57 +02:00
9947a648df fix compiler warnings 2022-04-11 16:06:13 +02:00
c45328b34d Merge branch 'eive/develop' into mueller/compiler-warning-fixes-upstream 2022-04-11 16:03:52 +02:00
478b305fbe fix compiler warnings 2022-04-11 16:02:20 +02:00
28e93696df Merge pull request 'IPC Pass Arbitrary Args to MQ IF' (#58) from mueller/ipc-pass-arbitrary-args-to-mq-eive into eive/develop
Reviewed-on: #58
2022-04-11 14:55:54 +02:00
942d1e5e4b Merge remote-tracking branch 'origin/eive/develop' into mueller/ipc-pass-arbitrary-args-to-mq-eive 2022-04-11 14:40:43 +02:00
c0f80680ef Merge branch 'mueller/propagate-pool-manage-retval' of https://egit.irs.uni-stuttgart.de/eive/fsfw into mueller/propagate-pool-manage-retval 2022-04-11 14:35:28 +02:00
7761b66fe2 delete data from ipc store if reply fails 2022-04-11 14:35:16 +02:00
acc4c8d975 check serialize result as well 2022-04-11 14:33:28 +02:00
fe739aa81a Merge branch 'development' into mueller/propagate-pool-manage-retval 2022-04-11 14:26:34 +02:00
afe006e234 Merge pull request 'Alternative reply ID activation' (#595) from meier/alternativeReply into development
Reviewed-on: fsfw/fsfw#595
2022-04-11 14:18:52 +02:00
95f018a0b0 update IF method 2022-04-11 14:07:02 +02:00
8c2105ae0a correct init value for object ID 2022-04-11 14:00:37 +02:00
ed2c2af4a0 take upstream impl of local data pool manager 2022-04-11 13:59:38 +02:00
17771c0497 progagate reply returnvalue 2022-04-11 13:57:18 +02:00
82df132e7d tests running again 2022-04-11 13:54:43 +02:00
a02619e5a2 strongly simplified and streamlined IPC MQ Impl
- Generic code was duplicated across all OSALs.
  Is contained in generic base class now
- Remove duplicate documentation
2022-04-11 11:19:38 +02:00
6ce09e968d Merge pull request 'Update from FSFW upstream' (#56) from mueller/update-from-upstream into eive/develop
Reviewed-on: #56
2022-04-11 09:03:08 +02:00
290db6ccad Merge remote-tracking branch 'upstream/development' into mueller/update-from-upstream 2022-04-11 08:51:55 +02:00
94ed582297 Merge branch 'eive/develop' of https://egit.irs.uni-stuttgart.de/eive/fsfw into eive/develop 2022-04-07 19:47:49 +02:00
47ced1efac pool entry takes const T* now 2022-04-07 19:47:37 +02:00
85a6e4b129 Merge pull request 'improve clock error handler' (#55) from mueller/clock-update into eive/develop
Reviewed-on: #55
2022-04-07 19:29:49 +02:00
f94bc02b6c Merge branch 'eive/develop' into mueller/clock-update 2022-04-07 17:36:04 +02:00
5bda877d97 improve clock error handler 2022-04-07 17:23:06 +02:00
51e7f1c2f2 Merge pull request 'FSFW Update' (#54) from mueller/clock-update into eive/develop
Reviewed-on: #54
2022-04-07 11:33:06 +02:00
a11d7455df Merge branch 'eive/develop' into mueller/master 2022-04-07 11:04:47 +02:00
4dc903fe20 Merge pull request 'Pool Entry Update' (#53) from mueller/pool-entry-update into eive/develop
Reviewed-on: #53
2022-04-07 11:04:29 +02:00
3325cc18fc Merge branch 'eive/develop' into mueller/pool-entry-update 2022-04-07 11:03:56 +02:00
43917d98c0 Merge branch 'mueller/master' of https://egit.irs.uni-stuttgart.de/eive/fsfw into mueller/master 2022-04-07 11:01:49 +02:00
e3ffcae3e0 emit warning in linux clock 2022-04-07 11:01:39 +02:00
0677de39aa make reporting setter public 2022-04-07 00:15:42 +02:00
aded4fae1e printout improvement 2022-04-04 20:34:31 +02:00
a011e70665 Merge branch 'development' into mueller/ipc-pass-arbitrary-args-to-mq 2022-04-04 17:21:11 +02:00
7df51f7202 Merge pull request 'add power switcher component' (#52) from mueller/power-switcher-component into eive/develop
Reviewed-on: #52
2022-04-04 15:51:18 +02:00
7530c44849 Merge branch 'eive/develop' into mueller/pool-entry-update 2022-04-04 15:46:10 +02:00
e4c6a69f77 this should also zero-init the pool entries 2022-04-04 15:44:03 +02:00
761a0c9bac new pool ctor which only takes len 2022-04-04 15:39:02 +02:00
c05184e1c4 Merge branch 'development' into meier/alternativeReply 2022-04-04 14:25:08 +02:00
b2252bdc0b Merge remote-tracking branch 'upstream/development' into mueller/add-etl-dependency 2022-04-04 14:18:30 +02:00
7e61ce1ed2 Merge pull request 'cmake lists update to suppress large warning' (#588) from eive/fsfw:mueller/catch2-find-package-quiet into development
Reviewed-on: fsfw/fsfw#588
2022-04-04 14:07:38 +02:00
518666f822 add power switcher component 2022-04-01 17:01:56 +02:00
318cd8e244 Merge pull request 'Update FSFW' (#51) from mueller/master into eive/develop
Reviewed-on: #51
2022-04-01 14:51:31 +02:00
1bc7a91869 apply auto-formatter 2022-04-01 14:08:29 +02:00
8e26e287c3 Merge remote-tracking branch 'origin/eive/develop' into mueller/master 2022-04-01 14:07:26 +02:00
ce2f7c4fdf Merge pull request 'DHB and CommandActionHelper changes' (#50) from meier/develop into eive/develop
Reviewed-on: #50
2022-04-01 14:07:05 +02:00
b3d2d440d7 Merge remote-tracking branch 'origin/eive/develop' into mueller/master 2022-03-31 16:13:47 +02:00
fbf9626fde Merge branch 'eive/develop' into meier/dhbAlternativeReply 2022-03-31 15:04:55 +02:00
fcb6437388 Merge branch 'development' into mueller/ipc-pass-arbitrary-args-to-mq 2022-03-31 14:42:15 +02:00
6c1db8473b get alternative reply from device command info 2022-03-31 14:36:45 +02:00
29cf8c9009 fix in getReplyLength 2022-03-31 11:42:39 +02:00
61d0815de8 Merge pull request 'Refactor Power Module' (#49) from mueller/refactor-power-switch-if-etc-eive into eive/develop
Reviewed-on: #49
2022-03-31 09:01:24 +02:00
127fbeb980 Merge branch 'mueller/refactor-power-switch-if-etc' into mueller/refactor-power-switch-if-etc-eive 2022-03-30 17:41:55 +02:00
b42987059a make dummy power switcher a system object 2022-03-30 17:41:38 +02:00
c2581ff4f5 Merge remote-tracking branch 'origin/eive/develop' into mueller/master 2022-03-30 13:12:11 +02:00
7b6f68c509 Merge remote-tracking branch 'origin/eive/develop' into mueller/refactor-power-switch-if-etc-eive 2022-03-30 12:14:57 +02:00
532607bf8f extended command info 2022-03-30 09:21:03 +02:00
a230dc4313 command action default arguments 2022-03-30 09:20:46 +02:00
a3930dafc5 Moved unused constructors 2022-03-28 21:37:25 +02:00
4f9797af3b Updated CCSDS CuC Functions 2022-03-28 21:24:33 +02:00
1a530633ca small fix 2022-03-28 21:10:51 +02:00
8037e8074b more docs 2022-03-28 21:03:18 +02:00
d07e0e5576 trying something 2022-03-28 21:01:26 +02:00
5525466b52 update changelog 2022-03-28 20:57:30 +02:00
c2a89bf709 bugfix 2022-03-28 20:57:13 +02:00
8dd0b2608d cache version variables 2022-03-28 20:55:34 +02:00
05495077ec doc update 2022-03-28 20:53:39 +02:00
8ff9eadf30 update changelog, add basic instructions for etl 2022-03-28 20:43:36 +02:00
082c86ea18 link ETL lib as well 2022-03-28 20:18:49 +02:00
2800d6f28c add ETL dependency 2022-03-28 20:16:11 +02:00
b4effe7a46 Clang format 2022-03-28 18:33:24 +02:00
e6e71436c2 Added more tests 2022-03-28 18:32:51 +02:00
4be45adae6 Merge branch 'development' into mueller/catch2-find-package-quiet 2022-03-28 16:44:23 +02:00
a887f852c8 Merge branch 'development' into mueller/clock-addition 2022-03-28 15:45:58 +02:00
0d7d2203d2 Merge pull request 'Added function to add component to a periodic task by pointer' (#591) from mohr/task into development
Reviewed-on: fsfw/fsfw#591
2022-03-28 15:23:26 +02:00
cde184f428 Merge branch 'development' into mohr/task 2022-03-28 15:18:23 +02:00
0b3255e463 Fixed tests 2022-03-28 15:17:59 +02:00
df3794dfd8 Merge pull request 'delete code which is not required anymore' (#587) from eive/fsfw:mueller/small-mgmlis3-tweak into development
Reviewed-on: fsfw/fsfw#587
2022-03-28 15:08:22 +02:00
d02d5c351d Merge branch 'development' into mueller/small-mgmlis3-tweak 2022-03-28 15:05:24 +02:00
631a531212 Merge branch 'development' into mueller/clock-addition 2022-03-28 14:51:23 +02:00
febecd4b30 Merge pull request 'small tweaks for local datapool code' (#582) from eive/fsfw:mueller/localpool-tweaks into development
Reviewed-on: fsfw/fsfw#582
2022-03-28 14:49:42 +02:00
964e311d8b Merge branch 'development' into mueller/localpool-tweaks 2022-03-28 14:48:34 +02:00
d43caa8296 Merge pull request 'Using enums to configure baudrate and bits per word in Linux HAL UART' (#585) from mohr/hal_uart into development
Reviewed-on: fsfw/fsfw#585
2022-03-28 14:47:04 +02:00
916ed3f56a added function to add component to a periodic task by pointer 2022-03-28 13:50:42 +02:00
3ea9f999b7 apply auto-formatter 2022-03-28 12:59:51 +02:00
79f3c7324a tweaks for dummy power switcher 2022-03-28 12:59:32 +02:00
23af170229 small tweaks 2022-03-28 12:59:03 +02:00
b32d1da421 this should compile 2022-03-28 12:57:11 +02:00
6f0362b956 refactor power module 2022-03-28 12:48:15 +02:00
60972228ef reworked power switch interface 2022-03-28 12:47:09 +02:00
6ea1eabb2d small order change in DHB 2022-03-28 12:21:25 +02:00
283a37dccc Merge pull request 'meier/gpioClass' (#47) from meier/gpioClass into eive/develop
Reviewed-on: #47
2022-03-28 10:22:32 +02:00
acf0cdfba3 Merge branch 'eive/develop' into meier/gpioClass 2022-03-28 10:22:11 +02:00
a01002aa5d Merge pull request 'meier/seqCount' (#46) from meier/seqCount into eive/develop
Reviewed-on: #46
2022-03-28 10:21:48 +02:00
b52f19254b class to abstract gpio handling 2022-03-28 09:12:29 +02:00
665d8cd479 Applied clang format 2022-03-25 18:48:53 +01:00
10398855a9 Added more unittest coverage
Added Mutex for gmtime functions
Moved Statics used in ClockCommon to ClockCommon
2022-03-25 18:47:31 +01:00
79615e47e4 Merge branch 'eive/develop' into meier/seqCount 2022-03-25 15:48:42 +01:00
e6130263ef Merge pull request 'Update FSFW' (#45) from mueller/master into eive/develop
Reviewed-on: #45
2022-03-25 14:56:19 +01:00
6895dbcc81 Merge remote-tracking branch 'origin/eive/develop' into mueller/master 2022-03-25 14:39:22 +01:00
d0fec93dc3 argument order inversion 2022-03-25 13:42:49 +01:00
59ab54b2fb call corrections 2022-03-25 13:41:37 +01:00
7095999bd2 remove CCSDSTime function 2022-03-25 13:34:35 +01:00
7ffb4107d2 added missing docs 2022-03-25 13:34:08 +01:00
d9d9a28ef8 delete code which is not required anymore 2022-03-24 21:04:46 +01:00
d95582b81b cmake lists update to suppress large warning 2022-03-24 16:54:41 +01:00
c60aa68d00 changed hal linux uart baudrate and bits per word to enums 2022-03-24 15:44:32 +01:00
4b5e3e70f7 add option to directly check switch state 2022-03-22 20:35:04 +01:00
9ce59d3c75 added an additional conversion function
- timeval to TimeOfDay_t
2022-03-22 17:54:09 +01:00
a0dfdfab2c Allow passing a MqArgs struct to the MQ creation
The struct contains context information (which can be extended)
and an arbitrary user argument in form of a void pointer.
This makes the API a lot more flexible
2022-03-22 17:49:22 +01:00
3e17011087 small tweaks for local datapool code 2022-03-22 17:42:56 +01:00
f441505476 update changelog 2022-03-22 17:41:49 +01:00
7c64797f03 Add more baud rates 2022-03-22 17:38:47 +01:00
bac8b40880 Merge branch 'eive/develop' into meier/seqCount 2022-03-17 20:02:29 +01:00
caf78835b2 added -- operator 2022-03-17 20:00:17 +01:00
38c87fdeb2 Merge remote-tracking branch 'upstream/development' into eive/develop 2022-03-17 09:47:20 +01:00
5ca5fe4040 Merge remote-tracking branch 'upstream/development' into eive/develop 2022-03-14 17:50:27 +01:00
1b7e0371c3 fixed merge conflict 2022-03-14 15:25:17 +01:00
d4ade5e885 sequence count operator overloading 2022-03-14 15:01:17 +01:00
d5ff6da40b Merge pull request 'function to get fsfw version' (#43) from mueller/version-getter into eive/develop
Reviewed-on: #43
2022-03-08 12:04:13 +01:00
e498136273 Merge pull request 'call setTimeout' (#44) from mueller/countdown-improvement into eive/develop
Reviewed-on: #44
2022-03-08 12:03:13 +01:00
47d158156b call setTimeout 2022-03-08 11:52:33 +01:00
114 changed files with 3092 additions and 1123 deletions

View File

@ -22,6 +22,32 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/572
- HAL Devicehandlers: Periodic printout is run-time configurable now
- `oneShotAction` flag in the `TestTask` class is not static anymore
- HAL Linux Uart: Baudrate and bits per word are enums now, avoiding misconfigurations
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/585
- Major update for version handling, using `git describe` to fetch version information with git.
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/601
- Place `Version` class outside of `fsfw` namespace. It is generic
- Add helper functions provided by [`cmake-modules`](https://github.com/bilke/cmake-modules)
manually now. Those should not change too often and only a small subset is needed
- Separate folder for easier update and for distinction
- LICENSE file included
- use `int` for version numbers to allow unset or uninitialized version
- Initialize Version object with numbers set to -1
- Instead of hardcoding the git hash, it is now retrieved from git
- `Version` now allows specifying additional version information like the git SHA1 hash and the
versions since the last tag
- Additional information is set to the last part of the git describe output for `FSFW_VERSION` now.
- Version still need to be hand-updated if the FSFW is not included as a submodule for now.
- IPC Message Queue Handling: Allow passing an optional `MqArgs` argument into the MessageQueue
creation call. It allows passing context information and an arbitrary user argument into
the message queue. Also streamlined and simplified `MessageQueue` implementation for all OSALs
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/583
- Clock:
- `timeval` to `TimeOfDay_t`
- Added Mutex for gmtime calls: (compare http://www.opengate.at/blog/2020/01/timeless/)
- Moved the statics used by Clock in ClockCommon.cpp to this file
- Better check for leap seconds
- Added Unittests for Clock (only getter)
## Removed
@ -35,6 +61,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- 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
- Added ETL dependency and improved library dependency management
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/592
## Fixed
- Small bugfix in STM32 HAL for SPI
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/599
- HAL GPIO: Improved error checking in `LinuxLibgpioIF::configureGpios(...)`. If a GPIO
configuration fails, the function will exit prematurely with a dedicated error code
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/602
# [v4.0.0]

View File

@ -1,11 +1,31 @@
cmake_minimum_required(VERSION 3.13)
project(${LIB_FSFW_NAME})
set(FSFW_VERSION 4)
set(FSFW_SUBVERSION 0)
set(FSFW_REVISION 0)
set(FSFW_VERSION_IF_GIT_FAILS 4)
set(FSFW_SUBVERSION_IF_GIT_FAILS 0)
set(FSFW_REVISION_IF_GIT_FAILS 0)
# Add the cmake folder so the FindSphinx module is found
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" )
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake-modules")
set(MSG_PREFIX "fsfw |")
set(FSFW_ETL_LIB_MAJOR_VERSION 20 CACHE STRING
"ETL library major version requirement"
)
set(FSFW_ETL_LIB_VERSION ${FSFW_ETL_LIB_MAJOR_VERSION}.27.3 CACHE STRING
"ETL library exact version requirement"
)
set(FSFW_ETL_LINK_TARGET etl::etl)
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}.0.0-preview5 CACHE STRING
"Catch2 library exact version requirement"
)
set(FSFW_ETL_LIB_NAME etl)
option(FSFW_GENERATE_SECTIONS
"Generate function and data sections. Required to remove unused code" ON
@ -42,26 +62,56 @@ set(LIB_FSFW_NAME fsfw)
set(FSFW_TEST_TGT fsfw-tests)
set(FSFW_DUMMY_TGT fsfw-dummy)
project(${LIB_FSFW_NAME})
add_library(${LIB_FSFW_NAME})
set(FSFW_GIT_VER_HANDLING_OK FALSE)
# Version handling
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git)
message(STATUS "${MSG_PREFIX} Determining version information with git")
include(FsfwHelpers)
determine_version_with_git("--exclude" "docker_*")
if(GIT_INFO)
set(FSFW_GIT_INFO ${GIT_INFO} CACHE STRING "Version information retrieved with git describe")
list(GET FSFW_GIT_INFO 1 FSFW_VERSION)
list(GET FSFW_GIT_INFO 2 FSFW_SUBVERSION)
list(GET FSFW_GIT_INFO 3 FSFW_REVISION)
list(GET FSFW_GIT_INFO 4 FSFW_VERSION_CST_GIT_SHA1)
if(NOT FSFW_VERSION)
set(FSFW_VERSION ${FSFW_VERSION_IF_GIT_FAILS})
endif()
if(NOT FSFW_SUBVERSION)
set(FSFW_SUBVERSION ${FSFW_SUBVERSION_IF_GIT_FAILS})
endif()
if(NOT FSFW_REVISION)
set(FSFW_REVISION ${FSFW_REVISION_IF_GIT_FAILS})
endif()
set(FSFW_GIT_VER_HANDLING_OK TRUE)
else()
set(FSFW_GIT_VER_HANDLING_OK FALSE)
endif()
endif()
if(NOT FSFW_GIT_VER_HANDLING_OK)
set(FSFW_VERSION ${FSFW_VERSION_IF_GIT_FAILS})
set(FSFW_SUBVERSION ${FSFW_SUBVERSION_IF_GIT_FAILS})
set(FSFW_REVISION ${FSFW_REVISION_IF_GIT_FAILS})
endif()
if(FSFW_BUILD_UNITTESTS)
message(STATUS "Building the FSFW unittests in addition to the static library")
message(STATUS "${MSG_PREFIX} Building the FSFW unittests in addition to the static library")
# Check whether the user has already installed Catch2 first
find_package(Catch2 3)
find_package(Catch2 ${FSFW_CATCH2_LIB_MAJOR_VERSION} CONFIG 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")
include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.0.0-preview4
GIT_TAG ${FSFW_CATCH2_LIB_VERSION}
)
FetchContent_MakeAvailable(Catch2)
#fixes regression -preview4, to be confirmed in later releases
set_target_properties(Catch2 PROPERTIES DEBUG_POSTFIX "")
list(APPEND FSFW_FETCH_CONTENT_TARGETS Catch2)
endif()
set(FSFW_CONFIG_PATH tests/src/fsfw_tests/unit/testcfg)
@ -72,22 +122,49 @@ if(FSFW_BUILD_UNITTESTS)
add_executable(${FSFW_TEST_TGT})
if(FSFW_TESTS_GEN_COV)
message(STATUS "Generating coverage data for the library")
message(STATUS "Targets linking against ${LIB_FSFW_NAME} "
message(STATUS "${MSG_PREFIX} Generating coverage data for the library")
message(STATUS "${MSG_PREFIX} Targets linking against ${LIB_FSFW_NAME} "
"will be compiled with coverage data as well"
)
include(FetchContent)
FetchContent_Declare(
cmake-modules
GIT_REPOSITORY https://github.com/bilke/cmake-modules.git
)
FetchContent_MakeAvailable(cmake-modules)
set(CMAKE_BUILD_TYPE "Debug")
list(APPEND CMAKE_MODULE_PATH ${cmake-modules_SOURCE_DIR})
include(CodeCoverage)
endif()
endif()
message(STATUS "Finding and/or providing etl library with version ${FSFW_ETL_LIB_MAJOR_VERSION}")
# Check whether the user has already installed ETL first
find_package(${FSFW_ETL_LIB_NAME} ${FSFW_ETL_LIB_MAJOR_VERSION} CONFIG QUIET)
# Not installed, so use FetchContent to download and provide etl
if(NOT ${FSFW_ETL_LIB_NAME}_FOUND)
message(STATUS
"No ETL installation was found with find_package. Installing and providing "
"etl with FindPackage"
)
include(FetchContent)
FetchContent_Declare(
${FSFW_ETL_LIB_NAME}
GIT_REPOSITORY https://github.com/ETLCPP/etl
GIT_TAG ${FSFW_ETL_LIB_VERSION}
)
list(APPEND FSFW_FETCH_CONTENT_TARGETS ${FSFW_ETL_LIB_NAME})
endif()
# The documentation for FetchContent recommends declaring all the dependencies
# before making them available. We make all declared dependency available here
# after their declaration
if(FSFW_FETCH_CONTENT_TARGETS)
FetchContent_MakeAvailable(${FSFW_FETCH_CONTENT_TARGETS})
if(TARGET ${FSFW_ETL_LIB_NAME})
add_library(${FSFW_ETL_LINK_TARGET} ALIAS ${FSFW_ETL_LIB_NAME})
endif()
if(TARGET Catch2)
# Fixes regression -preview4, to be confirmed in later releases
# Related GitHub issue: https://github.com/catchorg/Catch2/issues/2417
set_target_properties(Catch2 PROPERTIES DEBUG_POSTFIX "")
endif()
endif()
set(FSFW_CORE_INC_PATH "inc")
set_property(CACHE FSFW_OSAL PROPERTY STRINGS host linux rtems freertos)
@ -104,17 +181,17 @@ if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
elseif(${CMAKE_CXX_STANDARD} LESS 11)
message(FATAL_ERROR "Compiling the FSFW requires a minimum of C++11 support")
message(FATAL_ERROR "${MSG_PREFIX} Compiling the FSFW requires a minimum of C++11 support")
endif()
# Backwards comptability
if(OS_FSFW AND NOT FSFW_OSAL)
message(WARNING "Please pass the FSFW OSAL as FSFW_OSAL instead of OS_FSFW")
message(WARNING "${MSG_PREFIX} Please pass the FSFW OSAL as FSFW_OSAL instead of OS_FSFW")
set(FSFW_OSAL OS_FSFW)
endif()
if(NOT FSFW_OSAL)
message(STATUS "No OS for FSFW via FSFW_OSAL set. Assuming host OS")
message(STATUS "${MSG_PREFIX} No OS for FSFW via FSFW_OSAL set. Assuming host OS")
# Assume host OS and autodetermine from OS_FSFW
if(UNIX)
set(FSFW_OSAL "linux"
@ -148,7 +225,7 @@ elseif(FSFW_OSAL STREQUAL rtems)
set(FSFW_OSAL_RTEMS ON)
else()
message(WARNING
"Invalid operating system for FSFW specified! Setting to host.."
"${MSG_PREFIX} Invalid operating system for FSFW specified! Setting to host.."
)
set(FSFW_OS_NAME "Host")
set(OS_FSFW "host")
@ -157,7 +234,7 @@ endif()
configure_file(src/fsfw/FSFW.h.in fsfw/FSFW.h)
configure_file(src/fsfw/FSFWVersion.h.in fsfw/FSFWVersion.h)
message(STATUS "Compiling FSFW for the ${FSFW_OS_NAME} operating system.")
message(STATUS "${MSG_PREFIX} Compiling FSFW for the ${FSFW_OS_NAME} operating system")
add_subdirectory(src)
add_subdirectory(tests)
@ -241,8 +318,8 @@ endif()
if(NOT FSFW_CONFIG_PATH)
set(DEF_CONF_PATH misc/defaultcfg/fsfwconfig)
if(NOT FSFW_BUILD_DOCS)
message(WARNING "Flight Software Framework configuration path not set!")
message(WARNING "Setting default configuration from ${DEF_CONF_PATH} ..")
message(WARNING "${MSG_PREFIX} Flight Software Framework configuration path not set")
message(WARNING "${MSG_PREFIX} Setting default configuration from ${DEF_CONF_PATH} ..")
endif()
add_subdirectory(${DEF_CONF_PATH})
set(FSFW_CONFIG_PATH ${DEF_CONF_PATH})
@ -349,6 +426,7 @@ target_compile_options(${LIB_FSFW_NAME} PRIVATE
)
target_link_libraries(${LIB_FSFW_NAME} PRIVATE
${FSFW_ETL_LINK_TARGET}
${FSFW_ADDITIONAL_LINK_LIBS}
)

View File

@ -11,9 +11,15 @@ with Airbus Defence and Space GmbH.
## Quick facts
The framework is designed for systems, which communicate with external devices, perform control loops, receive telecommands and send telemetry, and need to maintain a high level of availability. Therefore, a mode and health system provides control over the states of the software and the controlled devices. In addition, a simple mechanism of event based fault detection, isolation and recovery is implemented as well.
The framework is designed for systems, which communicate with external devices, perform control loops,
receive telecommands and send telemetry, and need to maintain a high level of availability. Therefore,
a mode and health system provides control over the states of the software and the controlled devices.
In addition, a simple mechanism of event based fault detection, isolation and recovery is implemented as well.
The FSFW provides abstraction layers for operating systems to provide a uniform operating system abstraction layer (OSAL). Some components of this OSAL are required internally by the FSFW but is also very useful for developers to implement the same application logic on different operating systems with a uniform interface.
The FSFW provides abstraction layers for operating systems to provide a uniform operating system
abstraction layer (OSAL). Some components of this OSAL are required internally by the FSFW but is
also very useful for developers to implement the same application logic on different operating
systems with a uniform interface.
Currently, the FSFW provides the following OSALs:
@ -45,6 +51,28 @@ A template configuration folder was provided and can be copied into the project
a starting point. The [configuration section](docs/README-config.md#top) provides more specific
information about the possible options.
## Prerequisites
The Embedded Template Library (etl) is a dependency of the FSFW which is automatically
installed and provided by the build system unless the correction version was installed.
The current recommended version can be found inside the fsfw `CMakeLists.txt` file or by using
`ccmake` and looking up the `FSFW_ETL_LIB_MAJOR_VERSION` variable.
You can install the ETL library like this. On Linux, it might be necessary to add `sudo` before
the install call:
```cpp
git clone https://github.com/ETLCPP/etl
cd etl
git checkout <currentRecommendedVersion>
mkdir build && cd build
cmake ..
cmake --install .
```
It is recommended to install `20.27.2` or newer for the package version handling of
ETL to work.
## Adding the library
The following steps show how to add and use FSFW components. It is still recommended to
@ -83,6 +111,19 @@ The FSFW also has unittests which use the [Catch2 library](https://github.com/ca
These are built by setting the CMake option `FSFW_BUILD_UNITTESTS` to `ON` or `TRUE`
from your project `CMakeLists.txt` file or from the command line.
You can install the Catch2 library, which prevents the build system to avoid re-downloading
the dependency if the unit tests are completely rebuilt. The current recommended version
can be found inside the fsfw `CMakeLists.txt` file or by using `ccmake` and looking up
the `FSFW_CATCH2_LIB_VERSION` variable.
```sh
git clone https://github.com/catchorg/Catch2.git
cd Catch2
git checkout <currentRecommendedVersion>
cmake -Bbuild -H. -DBUILD_TESTING=OFF
sudo cmake --build build/ --target install
```
The fsfw-tests binary will be built as part of the static library and dropped alongside it.
If the unittests are built, the library and the tests will be built with coverage information by
default. This can be disabled by setting the `FSFW_TESTS_COV_GEN` option to `OFF` or `FALSE`.

View File

@ -6,3 +6,9 @@ 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
RUN git clone https://github.com/catchorg/Catch2.git && \
cd Catch2 && \
git checkout v3.0.0-preview5 && \
cmake -Bbuild -H. -DBUILD_TESTING=OFF && \
cmake --build build/ --target install

View File

@ -3,7 +3,7 @@ pipeline {
BUILDDIR = 'build-tests'
}
agent {
docker { image 'fsfw-ci:d1'}
docker { image 'fsfw-ci:d2'}
}
stages {
stage('Clean') {

28
cmake/FsfwHelpers.cmake Normal file
View File

@ -0,0 +1,28 @@
# Determines the git version with git describe and returns it by setting
# the GIT_INFO list in the parent scope. The list has the following entries
# 1. Full version string
# 2. Major version
# 3. Minor version
# 4. Revision
# 5. git SHA hash and commits since tag
function(determine_version_with_git)
include(GetGitRevisionDescription)
git_describe(VERSION ${ARGN})
string(FIND ${VERSION} "." VALID_VERSION)
if(VALID_VERSION EQUAL -1)
message(WARNING "Version string ${VERSION} retrieved with git describe is invalid")
return()
endif()
# Parse the version information into pieces.
string(REGEX REPLACE "^v([0-9]+)\\..*" "\\1" _VERSION_MAJOR "${VERSION}")
string(REGEX REPLACE "^v[0-9]+\\.([0-9]+).*" "\\1" _VERSION_MINOR "${VERSION}")
string(REGEX REPLACE "^v[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" _VERSION_PATCH "${VERSION}")
string(REGEX REPLACE "^v[0-9]+\\.[0-9]+\\.[0-9]+-(.*)" "\\1" VERSION_SHA1 "${VERSION}")
set(GIT_INFO ${VERSION})
list(APPEND GIT_INFO ${_VERSION_MAJOR})
list(APPEND GIT_INFO ${_VERSION_MINOR})
list(APPEND GIT_INFO ${_VERSION_PATCH})
list(APPEND GIT_INFO ${VERSION_SHA1})
set(GIT_INFO ${GIT_INFO} PARENT_SCOPE)
message(STATUS "${MSG_PREFIX} Set git version info into GIT_INFO from the git tag ${VERSION}")
endfunction()

View File

@ -0,0 +1,719 @@
# Copyright (c) 2012 - 2017, Lars Bilke
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# CHANGES:
#
# 2012-01-31, Lars Bilke
# - Enable Code Coverage
#
# 2013-09-17, Joakim Söderberg
# - Added support for Clang.
# - Some additional usage instructions.
#
# 2016-02-03, Lars Bilke
# - Refactored functions to use named parameters
#
# 2017-06-02, Lars Bilke
# - Merged with modified version from github.com/ufz/ogs
#
# 2019-05-06, Anatolii Kurotych
# - Remove unnecessary --coverage flag
#
# 2019-12-13, FeRD (Frank Dana)
# - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor
# of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments.
# - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY
# - All setup functions: accept BASE_DIRECTORY, EXCLUDE list
# - Set lcov basedir with -b argument
# - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be
# overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().)
# - Delete output dir, .info file on 'make clean'
# - Remove Python detection, since version mismatches will break gcovr
# - Minor cleanup (lowercase function names, update examples...)
#
# 2019-12-19, FeRD (Frank Dana)
# - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets
#
# 2020-01-19, Bob Apthorpe
# - Added gfortran support
#
# 2020-02-17, FeRD (Frank Dana)
# - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters
# in EXCLUDEs, and remove manual escaping from gcovr targets
#
# 2021-01-19, Robin Mueller
# - Add CODE_COVERAGE_VERBOSE option which will allow to print out commands which are run
# - Added the option for users to set the GCOVR_ADDITIONAL_ARGS variable to supply additional
# flags to the gcovr command
#
# 2020-05-04, Mihchael Davis
# - Add -fprofile-abs-path to make gcno files contain absolute paths
# - Fix BASE_DIRECTORY not working when defined
# - Change BYPRODUCT from folder to index.html to stop ninja from complaining about double defines
#
# 2021-05-10, Martin Stump
# - Check if the generator is multi-config before warning about non-Debug builds
#
# 2022-02-22, Marko Wehle
# - Change gcovr output from -o <filename> for --xml <filename> and --html <filename> output respectively.
# This will allow for Multiple Output Formats at the same time by making use of GCOVR_ADDITIONAL_ARGS, e.g. GCOVR_ADDITIONAL_ARGS "--txt".
#
# USAGE:
#
# 1. Copy this file into your cmake modules path.
#
# 2. Add the following line to your CMakeLists.txt (best inside an if-condition
# using a CMake option() to enable it just optionally):
# include(CodeCoverage)
#
# 3. Append necessary compiler flags for all supported source files:
# append_coverage_compiler_flags()
# Or for specific target:
# append_coverage_compiler_flags_to_target(YOUR_TARGET_NAME)
#
# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og
#
# 4. If you need to exclude additional directories from the report, specify them
# using full paths in the COVERAGE_EXCLUDES variable before calling
# setup_target_for_coverage_*().
# Example:
# set(COVERAGE_EXCLUDES
# '${PROJECT_SOURCE_DIR}/src/dir1/*'
# '/path/to/my/src/dir2/*')
# Or, use the EXCLUDE argument to setup_target_for_coverage_*().
# Example:
# setup_target_for_coverage_lcov(
# NAME coverage
# EXECUTABLE testrunner
# EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*")
#
# 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set
# relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR)
# Example:
# set(COVERAGE_EXCLUDES "dir1/*")
# setup_target_for_coverage_gcovr_html(
# NAME coverage
# EXECUTABLE testrunner
# BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src"
# EXCLUDE "dir2/*")
#
# 5. Use the functions described below to create a custom make target which
# runs your test executable and produces a code coverage report.
#
# 6. Build a Debug build:
# cmake -DCMAKE_BUILD_TYPE=Debug ..
# make
# make my_coverage_target
#
include(CMakeParseArguments)
option(CODE_COVERAGE_VERBOSE "Verbose information" FALSE)
# Check prereqs
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( CPPFILT_PATH NAMES c++filt )
if(NOT GCOV_PATH)
message(FATAL_ERROR "gcov not found! Aborting...")
endif() # NOT GCOV_PATH
get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
list(GET LANGUAGES 0 LANG)
if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
if("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3)
message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
endif()
elseif(NOT CMAKE_COMPILER_IS_GNUCXX)
if("${CMAKE_Fortran_COMPILER_ID}" MATCHES "[Ff]lang")
# Do nothing; exit conditional without error if true
elseif("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU")
# Do nothing; exit conditional without error if true
else()
message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
endif()
endif()
set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage"
CACHE INTERNAL "")
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-fprofile-abs-path HAVE_fprofile_abs_path)
if(HAVE_fprofile_abs_path)
set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
endif()
endif()
set(CMAKE_Fortran_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the Fortran compiler during coverage builds."
FORCE )
set(CMAKE_CXX_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the C++ compiler during coverage builds."
FORCE )
set(CMAKE_C_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the C compiler during coverage builds."
FORCE )
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used for linking binaries during coverage builds."
FORCE )
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
FORCE )
mark_as_advanced(
CMAKE_Fortran_FLAGS_COVERAGE
CMAKE_CXX_FLAGS_COVERAGE
CMAKE_C_FLAGS_COVERAGE
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG))
message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
link_libraries(gcov)
endif()
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# setup_target_for_coverage_lcov(
# NAME testrunner_coverage # New target name
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES testrunner # Dependencies to build first
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
# # to BASE_DIRECTORY, with CMake 3.4+)
# NO_DEMANGLE # Don't demangle C++ symbols
# # even if c++filt is found
# )
function(setup_target_for_coverage_lcov)
set(options NO_DEMANGLE)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT LCOV_PATH)
message(FATAL_ERROR "lcov not found! Aborting...")
endif() # NOT LCOV_PATH
if(NOT GENHTML_PATH)
message(FATAL_ERROR "genhtml not found! Aborting...")
endif() # NOT GENHTML_PATH
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
if(DEFINED Coverage_BASE_DIRECTORY)
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
else()
set(BASEDIR ${PROJECT_SOURCE_DIR})
endif()
# Collect excludes (CMake 3.4+: Also compute absolute paths)
set(LCOV_EXCLUDES "")
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES})
if(CMAKE_VERSION VERSION_GREATER 3.4)
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
endif()
list(APPEND LCOV_EXCLUDES "${EXCLUDE}")
endforeach()
list(REMOVE_DUPLICATES LCOV_EXCLUDES)
# Conditional arguments
if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
set(GENHTML_EXTRA_ARGS "--demangle-cpp")
endif()
# Setting up commands which will be run to generate coverage data.
# Cleanup lcov
set(LCOV_CLEAN_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory .
-b ${BASEDIR} --zerocounters
)
# Create baseline to make sure untouched files show up in the report
set(LCOV_BASELINE_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b
${BASEDIR} -o ${Coverage_NAME}.base
)
# Run tests
set(LCOV_EXEC_TESTS_CMD
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
)
# Capturing lcov counters and generating report
set(LCOV_CAPTURE_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b
${BASEDIR} --capture --output-file ${Coverage_NAME}.capture
)
# add baseline counters
set(LCOV_BASELINE_COUNT_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base
-a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total
)
# filter collected data to final coverage report
set(LCOV_FILTER_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove
${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info
)
# Generate HTML output
set(LCOV_GEN_HTML_CMD
${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o
${Coverage_NAME} ${Coverage_NAME}.info
)
if(CODE_COVERAGE_VERBOSE)
message(STATUS "Executed command report")
message(STATUS "Command to clean up lcov: ")
string(REPLACE ";" " " LCOV_CLEAN_CMD_SPACED "${LCOV_CLEAN_CMD}")
message(STATUS "${LCOV_CLEAN_CMD_SPACED}")
message(STATUS "Command to create baseline: ")
string(REPLACE ";" " " LCOV_BASELINE_CMD_SPACED "${LCOV_BASELINE_CMD}")
message(STATUS "${LCOV_BASELINE_CMD_SPACED}")
message(STATUS "Command to run the tests: ")
string(REPLACE ";" " " LCOV_EXEC_TESTS_CMD_SPACED "${LCOV_EXEC_TESTS_CMD}")
message(STATUS "${LCOV_EXEC_TESTS_CMD_SPACED}")
message(STATUS "Command to capture counters and generate report: ")
string(REPLACE ";" " " LCOV_CAPTURE_CMD_SPACED "${LCOV_CAPTURE_CMD}")
message(STATUS "${LCOV_CAPTURE_CMD_SPACED}")
message(STATUS "Command to add baseline counters: ")
string(REPLACE ";" " " LCOV_BASELINE_COUNT_CMD_SPACED "${LCOV_BASELINE_COUNT_CMD}")
message(STATUS "${LCOV_BASELINE_COUNT_CMD_SPACED}")
message(STATUS "Command to filter collected data: ")
string(REPLACE ";" " " LCOV_FILTER_CMD_SPACED "${LCOV_FILTER_CMD}")
message(STATUS "${LCOV_FILTER_CMD_SPACED}")
message(STATUS "Command to generate lcov HTML output: ")
string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}")
message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}")
endif()
# Setup target
add_custom_target(${Coverage_NAME}
COMMAND ${LCOV_CLEAN_CMD}
COMMAND ${LCOV_BASELINE_CMD}
COMMAND ${LCOV_EXEC_TESTS_CMD}
COMMAND ${LCOV_CAPTURE_CMD}
COMMAND ${LCOV_BASELINE_COUNT_CMD}
COMMAND ${LCOV_FILTER_CMD}
COMMAND ${LCOV_GEN_HTML_CMD}
# Set output files as GENERATED (will be removed on 'make clean')
BYPRODUCTS
${Coverage_NAME}.base
${Coverage_NAME}.capture
${Coverage_NAME}.total
${Coverage_NAME}.info
${Coverage_NAME}/index.html
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
VERBATIM # Protect arguments to commands
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
)
# Show where to find the lcov info report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info."
)
# Show info where to find the report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
)
endfunction() # setup_target_for_coverage_lcov
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# setup_target_for_coverage_gcovr_xml(
# NAME ctest_coverage # New target name
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES executable_target # Dependencies to build first
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
# # to BASE_DIRECTORY, with CMake 3.4+)
# )
# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
# GCVOR command.
function(setup_target_for_coverage_gcovr_xml)
set(options NONE)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT GCOVR_PATH)
message(FATAL_ERROR "gcovr not found! Aborting...")
endif() # NOT GCOVR_PATH
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
if(DEFINED Coverage_BASE_DIRECTORY)
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
else()
set(BASEDIR ${PROJECT_SOURCE_DIR})
endif()
# Collect excludes (CMake 3.4+: Also compute absolute paths)
set(GCOVR_EXCLUDES "")
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
if(CMAKE_VERSION VERSION_GREATER 3.4)
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
endif()
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
endforeach()
list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
# Combine excludes to several -e arguments
set(GCOVR_EXCLUDE_ARGS "")
foreach(EXCLUDE ${GCOVR_EXCLUDES})
list(APPEND GCOVR_EXCLUDE_ARGS "-e")
list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
endforeach()
# Set up commands which will be run to generate coverage data
# Run tests
set(GCOVR_XML_EXEC_TESTS_CMD
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
)
# Running gcovr
set(GCOVR_XML_CMD
${GCOVR_PATH} --xml ${Coverage_NAME}.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR}
)
if(CODE_COVERAGE_VERBOSE)
message(STATUS "Executed command report")
message(STATUS "Command to run tests: ")
string(REPLACE ";" " " GCOVR_XML_EXEC_TESTS_CMD_SPACED "${GCOVR_XML_EXEC_TESTS_CMD}")
message(STATUS "${GCOVR_XML_EXEC_TESTS_CMD_SPACED}")
message(STATUS "Command to generate gcovr XML coverage data: ")
string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}")
message(STATUS "${GCOVR_XML_CMD_SPACED}")
endif()
add_custom_target(${Coverage_NAME}
COMMAND ${GCOVR_XML_EXEC_TESTS_CMD}
COMMAND ${GCOVR_XML_CMD}
BYPRODUCTS ${Coverage_NAME}.xml
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
VERBATIM # Protect arguments to commands
COMMENT "Running gcovr to produce Cobertura code coverage report."
)
# Show info where to find the report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml."
)
endfunction() # setup_target_for_coverage_gcovr_xml
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# setup_target_for_coverage_gcovr_html(
# NAME ctest_coverage # New target name
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES executable_target # Dependencies to build first
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
# # to BASE_DIRECTORY, with CMake 3.4+)
# )
# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
# GCVOR command.
function(setup_target_for_coverage_gcovr_html)
set(options NONE)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT GCOVR_PATH)
message(FATAL_ERROR "gcovr not found! Aborting...")
endif() # NOT GCOVR_PATH
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
if(DEFINED Coverage_BASE_DIRECTORY)
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
else()
set(BASEDIR ${PROJECT_SOURCE_DIR})
endif()
# Collect excludes (CMake 3.4+: Also compute absolute paths)
set(GCOVR_EXCLUDES "")
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
if(CMAKE_VERSION VERSION_GREATER 3.4)
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
endif()
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
endforeach()
list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
# Combine excludes to several -e arguments
set(GCOVR_EXCLUDE_ARGS "")
foreach(EXCLUDE ${GCOVR_EXCLUDES})
list(APPEND GCOVR_EXCLUDE_ARGS "-e")
list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
endforeach()
# Set up commands which will be run to generate coverage data
# Run tests
set(GCOVR_HTML_EXEC_TESTS_CMD
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
)
# Create folder
set(GCOVR_HTML_FOLDER_CMD
${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME}
)
# Running gcovr
set(GCOVR_HTML_CMD
${GCOVR_PATH} --html ${Coverage_NAME}/index.html --html-details -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR}
)
if(CODE_COVERAGE_VERBOSE)
message(STATUS "Executed command report")
message(STATUS "Command to run tests: ")
string(REPLACE ";" " " GCOVR_HTML_EXEC_TESTS_CMD_SPACED "${GCOVR_HTML_EXEC_TESTS_CMD}")
message(STATUS "${GCOVR_HTML_EXEC_TESTS_CMD_SPACED}")
message(STATUS "Command to create a folder: ")
string(REPLACE ";" " " GCOVR_HTML_FOLDER_CMD_SPACED "${GCOVR_HTML_FOLDER_CMD}")
message(STATUS "${GCOVR_HTML_FOLDER_CMD_SPACED}")
message(STATUS "Command to generate gcovr HTML coverage data: ")
string(REPLACE ";" " " GCOVR_HTML_CMD_SPACED "${GCOVR_HTML_CMD}")
message(STATUS "${GCOVR_HTML_CMD_SPACED}")
endif()
add_custom_target(${Coverage_NAME}
COMMAND ${GCOVR_HTML_EXEC_TESTS_CMD}
COMMAND ${GCOVR_HTML_FOLDER_CMD}
COMMAND ${GCOVR_HTML_CMD}
BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html # report directory
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
VERBATIM # Protect arguments to commands
COMMENT "Running gcovr to produce HTML code coverage report."
)
# Show info where to find the report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
)
endfunction() # setup_target_for_coverage_gcovr_html
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# setup_target_for_coverage_fastcov(
# NAME testrunner_coverage # New target name
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES testrunner # Dependencies to build first
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# EXCLUDE "src/dir1/" "src/dir2/" # Patterns to exclude.
# NO_DEMANGLE # Don't demangle C++ symbols
# # even if c++filt is found
# SKIP_HTML # Don't create html report
# POST_CMD perl -i -pe s!${PROJECT_SOURCE_DIR}/!!g ctest_coverage.json # E.g. for stripping source dir from file paths
# )
function(setup_target_for_coverage_fastcov)
set(options NO_DEMANGLE SKIP_HTML)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES FASTCOV_ARGS GENHTML_ARGS POST_CMD)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT FASTCOV_PATH)
message(FATAL_ERROR "fastcov not found! Aborting...")
endif()
if(NOT Coverage_SKIP_HTML AND NOT GENHTML_PATH)
message(FATAL_ERROR "genhtml not found! Aborting...")
endif()
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
if(Coverage_BASE_DIRECTORY)
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
else()
set(BASEDIR ${PROJECT_SOURCE_DIR})
endif()
# Collect excludes (Patterns, not paths, for fastcov)
set(FASTCOV_EXCLUDES "")
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_FASTCOV_EXCLUDES})
list(APPEND FASTCOV_EXCLUDES "${EXCLUDE}")
endforeach()
list(REMOVE_DUPLICATES FASTCOV_EXCLUDES)
# Conditional arguments
if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
set(GENHTML_EXTRA_ARGS "--demangle-cpp")
endif()
# Set up commands which will be run to generate coverage data
set(FASTCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS})
set(FASTCOV_CAPTURE_CMD ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
--search-directory ${BASEDIR}
--process-gcno
--output ${Coverage_NAME}.json
--exclude ${FASTCOV_EXCLUDES}
--exclude ${FASTCOV_EXCLUDES}
)
set(FASTCOV_CONVERT_CMD ${FASTCOV_PATH}
-C ${Coverage_NAME}.json --lcov --output ${Coverage_NAME}.info
)
if(Coverage_SKIP_HTML)
set(FASTCOV_HTML_CMD ";")
else()
set(FASTCOV_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS}
-o ${Coverage_NAME} ${Coverage_NAME}.info
)
endif()
set(FASTCOV_POST_CMD ";")
if(Coverage_POST_CMD)
set(FASTCOV_POST_CMD ${Coverage_POST_CMD})
endif()
if(CODE_COVERAGE_VERBOSE)
message(STATUS "Code coverage commands for target ${Coverage_NAME} (fastcov):")
message(" Running tests:")
string(REPLACE ";" " " FASTCOV_EXEC_TESTS_CMD_SPACED "${FASTCOV_EXEC_TESTS_CMD}")
message(" ${FASTCOV_EXEC_TESTS_CMD_SPACED}")
message(" Capturing fastcov counters and generating report:")
string(REPLACE ";" " " FASTCOV_CAPTURE_CMD_SPACED "${FASTCOV_CAPTURE_CMD}")
message(" ${FASTCOV_CAPTURE_CMD_SPACED}")
message(" Converting fastcov .json to lcov .info:")
string(REPLACE ";" " " FASTCOV_CONVERT_CMD_SPACED "${FASTCOV_CONVERT_CMD}")
message(" ${FASTCOV_CONVERT_CMD_SPACED}")
if(NOT Coverage_SKIP_HTML)
message(" Generating HTML report: ")
string(REPLACE ";" " " FASTCOV_HTML_CMD_SPACED "${FASTCOV_HTML_CMD}")
message(" ${FASTCOV_HTML_CMD_SPACED}")
endif()
if(Coverage_POST_CMD)
message(" Running post command: ")
string(REPLACE ";" " " FASTCOV_POST_CMD_SPACED "${FASTCOV_POST_CMD}")
message(" ${FASTCOV_POST_CMD_SPACED}")
endif()
endif()
# Setup target
add_custom_target(${Coverage_NAME}
# Cleanup fastcov
COMMAND ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
--search-directory ${BASEDIR}
--zerocounters
COMMAND ${FASTCOV_EXEC_TESTS_CMD}
COMMAND ${FASTCOV_CAPTURE_CMD}
COMMAND ${FASTCOV_CONVERT_CMD}
COMMAND ${FASTCOV_HTML_CMD}
COMMAND ${FASTCOV_POST_CMD}
# Set output files as GENERATED (will be removed on 'make clean')
BYPRODUCTS
${Coverage_NAME}.info
${Coverage_NAME}.json
${Coverage_NAME}/index.html # report directory
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
VERBATIM # Protect arguments to commands
COMMENT "Resetting code coverage counters to zero. Processing code coverage counters and generating report."
)
set(INFO_MSG "fastcov code coverage info report saved in ${Coverage_NAME}.info and ${Coverage_NAME}.json.")
if(NOT Coverage_SKIP_HTML)
string(APPEND INFO_MSG " Open ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html in your browser to view the coverage report.")
endif()
# Show where to find the fastcov info report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo ${INFO_MSG}
)
endfunction() # setup_target_for_coverage_fastcov
function(append_coverage_compiler_flags)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
endfunction() # append_coverage_compiler_flags
# Setup coverage for specific library
function(append_coverage_compiler_flags_to_target name)
target_compile_options(${name}
PRIVATE ${COVERAGE_COMPILER_FLAGS})
endfunction()

View File

@ -0,0 +1,141 @@
# - Returns a version string from Git
#
# These functions force a re-configure on each git commit so that you can
# trust the values of the variables in your build system.
#
# get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
#
# Returns the refspec and sha hash of the current head revision
#
# git_describe(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe on the source tree, and adjusting
# the output so that it tests false if an error occurs.
#
# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe --exact-match on the source tree,
# and adjusting the output so that it tests false if there was no exact
# matching tag.
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2009-2010.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
if(__get_git_revision_description)
return()
endif()
set(__get_git_revision_description YES)
# We must run the following at "include" time, not at function call time,
# to find the path to this module rather than the path to a calling list file
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
function(get_git_head_revision _refspecvar _hashvar)
set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories
set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
# We have reached the root directory, we are not in git
set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
return()
endif()
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
endwhile()
# check if this is a submodule
if(NOT IS_DIRECTORY ${GIT_DIR})
file(READ ${GIT_DIR} submodule)
string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule})
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
if (IS_ABSOLUTE ${GIT_DIR_RELATIVE})
set(GIT_DIR ${GIT_DIR_RELATIVE})
else()
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
endif()
endif()
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
if(NOT EXISTS "${GIT_DATA}")
file(MAKE_DIRECTORY "${GIT_DATA}")
endif()
if(NOT EXISTS "${GIT_DIR}/HEAD")
return()
endif()
set(HEAD_FILE "${GIT_DATA}/HEAD")
configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
"${GIT_DATA}/grabRef.cmake"
@ONLY)
include("${GIT_DATA}/grabRef.cmake")
set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
endfunction()
function(git_describe _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
get_git_head_revision(refspec hash)
if(NOT GIT_FOUND)
set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
return()
endif()
if(NOT hash)
set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
return()
endif()
# TODO sanitize
#if((${ARGN}" MATCHES "&&") OR
# (ARGN MATCHES "||") OR
# (ARGN MATCHES "\\;"))
# message("Please report the following error to the project!")
# message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
#endif()
#message(STATUS "Arguments to execute_process: ${ARGN}")
execute_process(COMMAND
${GIT_EXECUTABLE}
describe
${hash}
${ARGN}
WORKING_DIRECTORY
"${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE
res
OUTPUT_VARIABLE
out
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
set(${_var} "${out}" PARENT_SCOPE)
endfunction()
function(git_get_exact_tag _var)
git_describe(out --exact-match ${ARGN})
set(${_var} "${out}" PARENT_SCOPE)
endfunction()
function(git_get_tag _var)
git_describe(out --tags ${ARGN})
set(${_var} "${out}" PARENT_SCOPE)
endfunction()

View File

@ -0,0 +1,38 @@
#
# Internal file for GetGitRevisionDescription.cmake
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2009-2010.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
set(HEAD_HASH)
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
if(HEAD_CONTENTS MATCHES "ref")
# named branch
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
if(EXISTS "@GIT_DIR@/${HEAD_REF}")
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}")
configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
set(HEAD_HASH "${HEAD_REF}")
endif()
else()
# detached HEAD
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
endif()
if(NOT HEAD_HASH)
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
string(STRIP "${HEAD_HASH}" HEAD_HASH)
endif()

View File

@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,5 @@
The files in these folder were manually copy and pasted from the
[cmake-modules repository](https://github.com/bilke/cmake-modules). It was decided to do
this because only a small subset of its provided functions are needed.
The license file in included here as well.

View File

@ -19,6 +19,29 @@ A template configuration folder was provided and can be copied into the project
a starting point. The [configuration section](docs/README-config.md#top) provides more specific
information about the possible options.
Prerequisites
-------------------
The Embedded Template Library (etl) is a dependency of the FSFW which is automatically
installed and provided by the build system unless the correction version was installed.
The current recommended version can be found inside the fsfw ``CMakeLists.txt`` file or by using
``ccmake`` and looking up the ``FSFW_ETL_LIB_MAJOR_VERSION`` variable.
You can install the ETL library like this. On Linux, it might be necessary to add ``sudo`` before
the install call:
.. code-block:: console
git clone https://github.com/ETLCPP/etl
cd etl
git checkout <currentRecommendedVersion>
mkdir build && cd build
cmake ..
cmake --install .
It is recommended to install ``20.27.2`` or newer for the package version handling of
ETL to work.
Adding the library
-------------------
@ -60,6 +83,20 @@ The FSFW also has unittests which use the `Catch2 library`_.
These are built by setting the CMake option ``FSFW_BUILD_UNITTESTS`` to ``ON`` or `TRUE`
from your project `CMakeLists.txt` file or from the command line.
You can install the Catch2 library, which prevents the build system to avoid re-downloading
the dependency if the unit tests are completely rebuilt. The current recommended version
can be found inside the fsfw ``CMakeLists.txt`` file or by using ``ccmake`` and looking up
the ``FSFW_CATCH2_LIB_VERSION`` variable.
.. code-block:: console
git clone https://github.com/catchorg/Catch2.git
cd Catch2
git checkout <currentRecommendedVersion>
cmake -Bbuild -H. -DBUILD_TESTING=OFF
sudo cmake --build build/ --target install
The fsfw-tests binary will be built as part of the static library and dropped alongside it.
If the unittests are built, the library and the tests will be built with coverage information by
default. This can be disabled by setting the `FSFW_TESTS_COV_GEN` option to `OFF` or `FALSE`.

View File

@ -46,9 +46,9 @@ class GpioIF : public HasReturnvaluesIF {
* an ouput or input gpio.
*
* @param gpioId A unique number which specifies the GPIO to read.
* @param gpioState State of GPIO will be written to this pointer.
* @param gpioState State of GPIO will be written to this reference
*/
virtual ReturnValue_t readGpio(gpioId_t gpioId, int* gpioState) = 0;
virtual ReturnValue_t readGpio(gpioId_t gpioId, gpio::Levels& gpioState) = 0;
};
#endif /* COMMON_GPIO_GPIOIF_H_ */

View File

@ -9,7 +9,7 @@ using gpioId_t = uint16_t;
namespace gpio {
enum class Levels : int { LOW = 0, HIGH = 1, NONE = 99 };
enum class Levels : int { LOW = 0, HIGH = 1, FAILED = -1, NONE = 99 };
enum class Direction : int { IN = 0, OUT = 1 };

View File

@ -1,12 +1,9 @@
#include "MgmLIS3MDLHandler.h"
#include "fsfw/datapool/PoolReadGuard.h"
#if FSFW_HAL_LIS3MDL_MGM_DEBUG == 1
#include "fsfw/globalfunctions/PeriodicOperationDivider.h"
#endif
#include <cmath>
#include "fsfw/datapool/PoolReadGuard.h"
MgmLIS3MDLHandler::MgmLIS3MDLHandler(object_id_t objectId, object_id_t deviceCommunication,
CookieIF *comCookie, uint32_t transitionDelay)
: DeviceHandlerBase(objectId, deviceCommunication, comCookie),
@ -378,13 +375,16 @@ float MgmLIS3MDLHandler::getSensitivityFactor(MGMLIS3MDL::Sensitivies sens) {
ReturnValue_t MgmLIS3MDLHandler::enableTemperatureSensor(const uint8_t *commandData,
size_t commandDataLen) {
if (commandData == nullptr) {
return INVALID_COMMAND_PARAMETER;
}
triggerEvent(CHANGE_OF_SETUP_PARAMETER);
uint32_t size = 2;
commandBuffer[0] = writeCommand(MGMLIS3MDL::CTRL_REG1);
if (commandDataLen > 1) {
return INVALID_NUMBER_OR_LENGTH_OF_PARAMETERS;
}
switch (*commandData) {
switch (commandData[0]) {
case (MGMLIS3MDL::ON): {
commandBuffer[1] = registers[0] | (1 << 7);
break;

View File

@ -12,9 +12,14 @@ if(FSFW_HAL_LINUX_ADD_PERIPHERAL_DRIVERS)
if(FSFW_HAL_LINUX_ADD_LIBGPIOD)
add_subdirectory(gpio)
endif()
add_subdirectory(spi)
add_subdirectory(i2c)
add_subdirectory(uart)
# Adding those does not really make sense on Apple systems which
# are generally host systems. It won't even compile as the headers
# are missing
if(NOT APPLE)
add_subdirectory(i2c)
add_subdirectory(spi)
endif()
endif()
add_subdirectory(uio)

View File

@ -0,0 +1,27 @@
#ifndef FSFW_HAL_SRC_FSFW_HAL_LINUX_GPIO_GPIO_H_
#define FSFW_HAL_SRC_FSFW_HAL_LINUX_GPIO_GPIO_H_
#include "fsfw_hal/common/gpio/GpioIF.h"
#include "fsfw_hal/common/gpio/gpioDefinitions.h"
/**
* @brief Additional abstraction layer for handling GPIOs.
*
* @author J. Meier
*/
class Gpio {
public:
Gpio(gpioId_t gpioId, GpioIF* gpioIF) : gpioId(gpioId), gpioIF(gpioIF) {
if (gpioIF == nullptr) {
sif::error << "Gpio::Gpio: Invalid GpioIF" << std::endl;
}
}
ReturnValue_t pullHigh() { return gpioIF->pullHigh(gpioId); }
ReturnValue_t pullLow() { return gpioIF->pullLow(gpioId); }
private:
gpioId_t gpioId = gpio::NO_GPIO;
GpioIF* gpioIF = nullptr;
};
#endif /* FSFW_HAL_SRC_FSFW_HAL_LINUX_GPIO_GPIO_H_ */

View File

@ -44,6 +44,7 @@ ReturnValue_t LinuxLibgpioIF::addGpios(GpioCookie* gpioCookie) {
}
ReturnValue_t LinuxLibgpioIF::configureGpios(GpioMap& mapToAdd) {
ReturnValue_t result = RETURN_OK;
for (auto& gpioConfig : mapToAdd) {
auto& gpioType = gpioConfig.second->gpioType;
switch (gpioType) {
@ -55,7 +56,7 @@ ReturnValue_t LinuxLibgpioIF::configureGpios(GpioMap& mapToAdd) {
if (regularGpio == nullptr) {
return GPIO_INVALID_INSTANCE;
}
configureGpioByChip(gpioConfig.first, *regularGpio);
result = configureGpioByChip(gpioConfig.first, *regularGpio);
break;
}
case (gpio::GpioTypes::GPIO_REGULAR_BY_LABEL): {
@ -63,7 +64,7 @@ ReturnValue_t LinuxLibgpioIF::configureGpios(GpioMap& mapToAdd) {
if (regularGpio == nullptr) {
return GPIO_INVALID_INSTANCE;
}
configureGpioByLabel(gpioConfig.first, *regularGpio);
result = configureGpioByLabel(gpioConfig.first, *regularGpio);
break;
}
case (gpio::GpioTypes::GPIO_REGULAR_BY_LINE_NAME): {
@ -71,7 +72,7 @@ ReturnValue_t LinuxLibgpioIF::configureGpios(GpioMap& mapToAdd) {
if (regularGpio == nullptr) {
return GPIO_INVALID_INSTANCE;
}
configureGpioByLineName(gpioConfig.first, *regularGpio);
result = configureGpioByLineName(gpioConfig.first, *regularGpio);
break;
}
case (gpio::GpioTypes::CALLBACK): {
@ -83,8 +84,11 @@ ReturnValue_t LinuxLibgpioIF::configureGpios(GpioMap& mapToAdd) {
gpioCallback->initValue, gpioCallback->callbackArgs);
}
}
if (result != RETURN_OK) {
return GPIO_INIT_FAILED;
}
}
return RETURN_OK;
return result;
}
ReturnValue_t LinuxLibgpioIF::configureGpioByLabel(gpioId_t gpioId,
@ -276,7 +280,7 @@ ReturnValue_t LinuxLibgpioIF::driveGpio(gpioId_t gpioId, GpiodRegularBase& regul
return RETURN_OK;
}
ReturnValue_t LinuxLibgpioIF::readGpio(gpioId_t gpioId, int* gpioState) {
ReturnValue_t LinuxLibgpioIF::readGpio(gpioId_t gpioId, gpio::Levels& gpioState) {
gpioMapIter = gpioMap.find(gpioId);
if (gpioMapIter == gpioMap.end()) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
@ -295,7 +299,10 @@ ReturnValue_t LinuxLibgpioIF::readGpio(gpioId_t gpioId, int* gpioState) {
if (regularGpio == nullptr) {
return GPIO_TYPE_FAILURE;
}
*gpioState = gpiod_line_get_value(regularGpio->lineHandle);
gpioState = static_cast<gpio::Levels>(gpiod_line_get_value(regularGpio->lineHandle));
if (gpioState == gpio::Levels::FAILED) {
return GPIO_GET_VALUE_FAILED;
}
} else {
auto gpioCallback = dynamic_cast<GpioCallback*>(gpioMapIter->second);
if (gpioCallback->callback == nullptr) {

View File

@ -29,14 +29,18 @@ class LinuxLibgpioIF : public GpioIF, public SystemObject {
HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 4);
static constexpr ReturnValue_t GPIO_DUPLICATE_DETECTED =
HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 5);
static constexpr ReturnValue_t GPIO_INIT_FAILED =
HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 6);
// Will be returned if getting the line value failed. Error type will be set to errno in this case
static constexpr ReturnValue_t GPIO_GET_VALUE_FAILED =
HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 7);
LinuxLibgpioIF(object_id_t objectId);
virtual ~LinuxLibgpioIF();
ReturnValue_t addGpios(GpioCookie* gpioCookie) override;
ReturnValue_t pullHigh(gpioId_t gpioId) override;
ReturnValue_t pullLow(gpioId_t gpioId) override;
ReturnValue_t readGpio(gpioId_t gpioId, int* gpioState) override;
ReturnValue_t readGpio(gpioId_t gpioId, gpio::Levels& gpioState) override;
private:
static const size_t MAX_CHIPNAME_LENGTH = 11;

View File

@ -170,18 +170,20 @@ ReturnValue_t I2cComIF::requestReceiveMessage(CookieIF* cookie, size_t requestLe
int readLen = read(fd, replyBuffer, requestLen);
if (readLen != static_cast<int>(requestLen)) {
#if FSFW_VERBOSE_LEVEL >= 1 and FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "I2cComIF::requestReceiveMessage: Reading from I2C "
<< "device failed with error code " << errno << ". Description"
<< " of error: " << strerror(errno) << std::endl;
sif::error << "I2cComIF::requestReceiveMessage: Read only " << readLen << " from " << requestLen
<< " bytes" << std::endl;
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1
if (readLen < 0) {
sif::warning << "I2cComIF::requestReceiveMessage: Reading from I2C "
<< "device failed with error code " << errno << " | " << strerror(errno)
<< std::endl;
} else {
sif::warning << "I2cComIF::requestReceiveMessage: Read only " << readLen << " from "
<< requestLen << " bytes" << std::endl;
}
#else
#endif
#endif
i2cDeviceMapIter->second.replyLen = 0;
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << "I2cComIF::requestReceiveMessage: Read " << readLen << " of " << requestLen
<< " bytes" << std::endl;
#endif
return HasReturnvaluesIF::RETURN_FAILED;
}

View File

@ -1,4 +1,4 @@
#include "fsfw_hal/linux/spi/SpiCookie.h"
#include "SpiCookie.h"
SpiCookie::SpiCookie(address_t spiAddress, gpioId_t chipSelect, std::string spiDev,
const size_t maxSize, spi::SpiModes spiMode, uint32_t spiSpeed)

View File

@ -148,16 +148,16 @@ void UartComIF::setDatasizeOptions(struct termios* options, UartCookie* uartCook
/* Clear size bits */
options->c_cflag &= ~CSIZE;
switch (uartCookie->getBitsPerWord()) {
case 5:
case BitsPerWord::BITS_5:
options->c_cflag |= CS5;
break;
case 6:
case BitsPerWord::BITS_6:
options->c_cflag |= CS6;
break;
case 7:
case BitsPerWord::BITS_7:
options->c_cflag |= CS7;
break;
case 8:
case BitsPerWord::BITS_8:
options->c_cflag |= CS8;
break;
default:
@ -193,126 +193,128 @@ void UartComIF::setFixedOptions(struct termios* options) {
void UartComIF::configureBaudrate(struct termios* options, UartCookie* uartCookie) {
switch (uartCookie->getBaudrate()) {
case 50:
case UartBaudRate::RATE_50:
cfsetispeed(options, B50);
cfsetospeed(options, B50);
break;
case 75:
case UartBaudRate::RATE_75:
cfsetispeed(options, B75);
cfsetospeed(options, B75);
break;
case 110:
case UartBaudRate::RATE_110:
cfsetispeed(options, B110);
cfsetospeed(options, B110);
break;
case 134:
case UartBaudRate::RATE_134:
cfsetispeed(options, B134);
cfsetospeed(options, B134);
break;
case 150:
case UartBaudRate::RATE_150:
cfsetispeed(options, B150);
cfsetospeed(options, B150);
break;
case 200:
case UartBaudRate::RATE_200:
cfsetispeed(options, B200);
cfsetospeed(options, B200);
break;
case 300:
case UartBaudRate::RATE_300:
cfsetispeed(options, B300);
cfsetospeed(options, B300);
break;
case 600:
case UartBaudRate::RATE_600:
cfsetispeed(options, B600);
cfsetospeed(options, B600);
break;
case 1200:
case UartBaudRate::RATE_1200:
cfsetispeed(options, B1200);
cfsetospeed(options, B1200);
break;
case 1800:
case UartBaudRate::RATE_1800:
cfsetispeed(options, B1800);
cfsetospeed(options, B1800);
break;
case 2400:
case UartBaudRate::RATE_2400:
cfsetispeed(options, B2400);
cfsetospeed(options, B2400);
break;
case 4800:
case UartBaudRate::RATE_4800:
cfsetispeed(options, B4800);
cfsetospeed(options, B4800);
break;
case 9600:
case UartBaudRate::RATE_9600:
cfsetispeed(options, B9600);
cfsetospeed(options, B9600);
break;
case 19200:
case UartBaudRate::RATE_19200:
cfsetispeed(options, B19200);
cfsetospeed(options, B19200);
break;
case 38400:
case UartBaudRate::RATE_38400:
cfsetispeed(options, B38400);
cfsetospeed(options, B38400);
break;
case 57600:
case UartBaudRate::RATE_57600:
cfsetispeed(options, B57600);
cfsetospeed(options, B57600);
break;
case 115200:
case UartBaudRate::RATE_115200:
cfsetispeed(options, B115200);
cfsetospeed(options, B115200);
break;
case 230400:
case UartBaudRate::RATE_230400:
cfsetispeed(options, B230400);
cfsetospeed(options, B230400);
break;
case 460800:
#ifndef __APPLE__
case UartBaudRate::RATE_460800:
cfsetispeed(options, B460800);
cfsetospeed(options, B460800);
break;
case 500000:
case UartBaudRate::RATE_500000:
cfsetispeed(options, B500000);
cfsetospeed(options, B500000);
break;
case 576000:
case UartBaudRate::RATE_576000:
cfsetispeed(options, B576000);
cfsetospeed(options, B576000);
break;
case 921600:
case UartBaudRate::RATE_921600:
cfsetispeed(options, B921600);
cfsetospeed(options, B921600);
break;
case 1000000:
case UartBaudRate::RATE_1000000:
cfsetispeed(options, B1000000);
cfsetospeed(options, B1000000);
break;
case 1152000:
case UartBaudRate::RATE_1152000:
cfsetispeed(options, B1152000);
cfsetospeed(options, B1152000);
break;
case 1500000:
case UartBaudRate::RATE_1500000:
cfsetispeed(options, B1500000);
cfsetospeed(options, B1500000);
break;
case 2000000:
case UartBaudRate::RATE_2000000:
cfsetispeed(options, B2000000);
cfsetospeed(options, B2000000);
break;
case 2500000:
case UartBaudRate::RATE_2500000:
cfsetispeed(options, B2500000);
cfsetospeed(options, B2500000);
break;
case 3000000:
case UartBaudRate::RATE_3000000:
cfsetispeed(options, B3000000);
cfsetospeed(options, B3000000);
break;
case 3500000:
case UartBaudRate::RATE_3500000:
cfsetispeed(options, B3500000);
cfsetospeed(options, B3500000);
break;
case 4000000:
case UartBaudRate::RATE_4000000:
cfsetispeed(options, B4000000);
cfsetospeed(options, B4000000);
break;
#endif // ! __APPLE__
default:
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "UartComIF::configureBaudrate: Baudrate not supported" << std::endl;

View File

@ -2,8 +2,8 @@
#include <fsfw/serviceinterface.h>
UartCookie::UartCookie(object_id_t handlerId, std::string deviceFile, UartModes uartMode,
uint32_t baudrate, size_t maxReplyLen)
UartCookie::UartCookie(object_id_t handlerId, std::string deviceFile, UartBaudRate baudrate,
size_t maxReplyLen, UartModes uartMode)
: handlerId(handlerId),
deviceFile(deviceFile),
uartMode(uartMode),
@ -12,7 +12,7 @@ UartCookie::UartCookie(object_id_t handlerId, std::string deviceFile, UartModes
UartCookie::~UartCookie() {}
uint32_t UartCookie::getBaudrate() const { return baudrate; }
UartBaudRate UartCookie::getBaudrate() const { return baudrate; }
size_t UartCookie::getMaxReplyLen() const { return maxReplyLen; }
@ -24,23 +24,9 @@ void UartCookie::setParityEven() { parity = Parity::EVEN; }
Parity UartCookie::getParity() const { return parity; }
void UartCookie::setBitsPerWord(uint8_t bitsPerWord_) {
switch (bitsPerWord_) {
case 5:
case 6:
case 7:
case 8:
break;
default:
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << "UartCookie::setBitsPerWord: Invalid bits per word specified" << std::endl;
#endif
return;
}
bitsPerWord = bitsPerWord_;
}
void UartCookie::setBitsPerWord(BitsPerWord bitsPerWord_) { bitsPerWord = bitsPerWord_; }
uint8_t UartCookie::getBitsPerWord() const { return bitsPerWord; }
BitsPerWord UartCookie::getBitsPerWord() const { return bitsPerWord; }
StopBits UartCookie::getStopBits() const { return stopBits; }

View File

@ -12,6 +12,41 @@ enum class StopBits { ONE_STOP_BIT, TWO_STOP_BITS };
enum class UartModes { CANONICAL, NON_CANONICAL };
enum class BitsPerWord { BITS_5, BITS_6, BITS_7, BITS_8 };
enum class UartBaudRate {
RATE_50,
RATE_75,
RATE_110,
RATE_134,
RATE_150,
RATE_200,
RATE_300,
RATE_600,
RATE_1200,
RATE_1800,
RATE_2400,
RATE_4800,
RATE_9600,
RATE_19200,
RATE_38400,
RATE_57600,
RATE_115200,
RATE_230400,
RATE_460800,
RATE_500000,
RATE_576000,
RATE_921600,
RATE_1000000,
RATE_1152000,
RATE_1500000,
RATE_2000000,
RATE_2500000,
RATE_3000000,
RATE_3500000,
RATE_4000000
};
/**
* @brief Cookie for the UartComIF. There are many options available to configure the UART driver.
* The constructor only requests for common options like the baudrate. Other options can
@ -27,25 +62,23 @@ class UartCookie : public CookieIF {
* @param uartMode Specify the UART mode. The canonical mode should be used if the
* messages are separated by a delimited character like '\n'. See the
* termios documentation for more information
* @param baudrate The baudrate to use for input and output. Possible Baudrates are: 50,
* 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, B19200,
* 38400, 57600, 115200, 230400, 460800
* @param baudrate The baudrate to use for input and output.
* @param maxReplyLen The maximum size an object using this cookie expects
* @details
* Default configuration: No parity
* 8 databits (number of bits transfered with one uart frame)
* One stop bit
*/
UartCookie(object_id_t handlerId, std::string deviceFile, UartModes uartMode, uint32_t baudrate,
size_t maxReplyLen);
UartCookie(object_id_t handlerId, std::string deviceFile, UartBaudRate baudrate,
size_t maxReplyLen, UartModes uartMode = UartModes::NON_CANONICAL);
virtual ~UartCookie();
uint32_t getBaudrate() const;
UartBaudRate getBaudrate() const;
size_t getMaxReplyLen() const;
std::string getDeviceFile() const;
Parity getParity() const;
uint8_t getBitsPerWord() const;
BitsPerWord getBitsPerWord() const;
StopBits getStopBits() const;
UartModes getUartMode() const;
object_id_t getHandlerId() const;
@ -76,7 +109,7 @@ class UartCookie : public CookieIF {
/**
* Function two set number of bits per UART frame.
*/
void setBitsPerWord(uint8_t bitsPerWord_);
void setBitsPerWord(BitsPerWord bitsPerWord_);
/**
* Function to specify the number of stopbits.
@ -97,10 +130,10 @@ class UartCookie : public CookieIF {
std::string deviceFile;
const UartModes uartMode;
bool flushInput = false;
uint32_t baudrate;
UartBaudRate baudrate;
size_t maxReplyLen = 0;
Parity parity = Parity::NONE;
uint8_t bitsPerWord = 8;
BitsPerWord bitsPerWord = BitsPerWord::BITS_8;
uint8_t readCycles = 1;
StopBits stopBits = StopBits::ONE_STOP_BIT;
bool replySizeFixed = true;

View File

@ -21,7 +21,7 @@ using mspCb = void (*)(void);
namespace spi {
struct MspCfgBase {
MspCfgBase();
MspCfgBase() {}
MspCfgBase(stm32h7::GpioCfg sck, stm32h7::GpioCfg mosi, stm32h7::GpioCfg miso,
mspCb cleanupCb = nullptr, mspCb setupCb = nullptr)
: sck(sck), mosi(mosi), miso(miso), cleanupCb(cleanupCb), setupCb(setupCb) {}

View File

@ -1,9 +1,11 @@
#ifndef FSFW_VERSION_H_
#define FSFW_VERSION_H_
// Versioning is kept in project CMakeLists.txt file
#define FSFW_VERSION_MAJOR @FSFW_VERSION@
#define FSFW_VERSION_MINOR @FSFW_SUBVERSION@
#define FSFW_VERSION_REVISION @FSFW_REVISION@
// Versioning is managed in project CMakeLists.txt file
static constexpr int FSFW_VERSION_MAJOR = @FSFW_VERSION@;
static constexpr int FSFW_VERSION_MINOR = @FSFW_SUBVERSION@;
static constexpr int FSFW_VERSION_REVISION = @FSFW_REVISION@;
// Also contains CST (Commits since tag) information
static const char FSFW_VERSION_CST_GIT_SHA1[] = "@FSFW_VERSION_CST_GIT_SHA1@";
#endif /* FSFW_VERSION_H_ */

View File

@ -16,8 +16,8 @@ class CommandActionHelper {
public:
CommandActionHelper(CommandsActionsIF* owner);
virtual ~CommandActionHelper();
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,
const uint8_t* data = nullptr, uint32_t size = 0);
ReturnValue_t commandAction(object_id_t commandTo, ActionId_t actionId, SerializeIF* data);
ReturnValue_t initialize();
ReturnValue_t handleReply(CommandMessage* reply);

View File

@ -44,7 +44,7 @@ class HeaderSerializer : public SerializeIF, public PduHeaderIF {
cfdp::WidthInBytes getLenEntityIds() const override;
cfdp::WidthInBytes getLenSeqNum() const override;
cfdp::SegmentMetadataFlag getSegmentMetadataFlag() const override;
bool hasSegmentMetadataFlag() const;
bool hasSegmentMetadataFlag() const override;
void setSegmentationControl(cfdp::SegmentationControl);
void getSourceId(cfdp::EntityId& sourceId) const override;

View File

@ -9,7 +9,7 @@
*/
template <typename T, size_t MAX_SIZE, typename count_t = uint8_t>
class FixedArrayList : public ArrayList<T, count_t> {
#if !defined(_MSC_VER)
#if !defined(_MSC_VER) && !defined(__clang__)
static_assert(MAX_SIZE <= (std::pow(2, sizeof(count_t) * 8) - 1),
"count_t is not large enough to hold MAX_SIZE");
#endif

View File

@ -10,16 +10,23 @@ class HybridIterator : public LinkedElement<T>::Iterator, public ArrayList<T, co
HybridIterator() {}
HybridIterator(typename LinkedElement<T>::Iterator *iter)
: LinkedElement<T>::Iterator(*iter), value(iter->value), linked(true) {}
: LinkedElement<T>::Iterator(*iter), linked(true) {
if (iter != nullptr) {
value = iter->value;
}
}
HybridIterator(LinkedElement<T> *start)
: LinkedElement<T>::Iterator(start), value(start->value), linked(true) {}
HybridIterator(LinkedElement<T> *start) : LinkedElement<T>::Iterator(start), linked(true) {
if (start != nullptr) {
value = start->value;
}
}
HybridIterator(typename ArrayList<T, count_t>::Iterator start,
typename ArrayList<T, count_t>::Iterator end)
: ArrayList<T, count_t>::Iterator(start), value(start.value), linked(false), end(end.value) {
if (value == this->end) {
value = NULL;
value = nullptr;
}
}

View File

@ -55,7 +55,7 @@ class ControllerBase : public HasModesIF,
virtual void performControlOperation() = 0;
virtual ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode,
uint32_t *msToReachTheMode) = 0;
uint32_t *msToReachTheMode) override = 0;
const object_id_t parentId;
@ -80,9 +80,9 @@ class ControllerBase : public HasModesIF,
/** Mode helpers */
virtual void modeChanged(Mode_t mode, Submode_t submode);
virtual void startTransition(Mode_t mode, Submode_t submode);
virtual void getMode(Mode_t *mode, Submode_t *submode);
virtual void setToExternalControl();
virtual void startTransition(Mode_t mode, Submode_t submode) override;
virtual void getMode(Mode_t *mode, Submode_t *submode) override;
virtual void setToExternalControl() override;
virtual void announceMode(bool recursive);
/** HK helpers */
virtual void changeHK(Mode_t mode, Submode_t submode, bool enable);

View File

@ -109,7 +109,7 @@ class PoolDataSetBase : public PoolDataSetIF, public SerializeIF, public HasRetu
*/
virtual ReturnValue_t unlockDataPool() override;
virtual uint16_t getFillCount() const;
virtual uint16_t getFillCount() const override;
/* SerializeIF implementations */
virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, const size_t maxSize,

View File

@ -7,24 +7,26 @@
#include "fsfw/serviceinterface/ServiceInterface.h"
template <typename T>
PoolEntry<T>::PoolEntry(std::initializer_list<T> initValue, bool setValid)
: length(static_cast<uint8_t>(initValue.size())), valid(setValid) {
this->address = new T[this->length];
if (initValue.size() == 0) {
std::memset(this->address, 0, this->getByteSize());
} else {
std::copy(initValue.begin(), initValue.end(), this->address);
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(T* initValue, uint8_t setLength, bool setValid)
PoolEntry<T>::PoolEntry(const T* initValue, uint8_t setLength, bool setValid)
: length(setLength), valid(setValid) {
this->address = new T[this->length];
this->address = new T[this->length]();
if (initValue != nullptr) {
std::memcpy(this->address, initValue, this->getByteSize());
} else {
std::memset(this->address, 0, this->getByteSize());
}
}

View File

@ -33,6 +33,9 @@ class PoolEntry : public PoolEntryIF {
"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.
@ -49,7 +52,7 @@ class PoolEntry : public PoolEntryIF {
* @param setValid
* Sets the initialization flag. It is invalid by default.
*/
PoolEntry(std::initializer_list<T> initValue = {0}, bool setValid = false);
PoolEntry(std::initializer_list<T> initValue, bool setValid = false);
/**
* @brief In the classe's constructor, space is allocated on the heap and
@ -62,7 +65,7 @@ class PoolEntry : public PoolEntryIF {
* @param setValid
* Sets the initialization flag. It is invalid by default.
*/
PoolEntry(T* initValue, uint8_t setLength = 1, bool setValid = false);
PoolEntry(const T* initValue, uint8_t setLength = 1, bool setValid = false);
//! Explicitely deleted copy ctor, copying is not allowed.
PoolEntry(const PoolEntry&) = delete;

View File

@ -787,6 +787,10 @@ ReturnValue_t LocalDataPoolManager::generateSetStructurePacket(sid_t sid, bool i
// Serialize set packet into store.
size_t size = 0;
result = setPacket.serialize(&storePtr, &size, expectedSize, SerializeIF::Endianness::BIG);
if (result != HasReturnvaluesIF::RETURN_OK) {
ipcStore->deleteData(storeId);
return result;
}
if (expectedSize != size) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "generateSetStructurePacket",
HasReturnvaluesIF::RETURN_FAILED,
@ -801,7 +805,10 @@ ReturnValue_t LocalDataPoolManager::generateSetStructurePacket(sid_t sid, bool i
HousekeepingMessage::setHkStuctureReportReply(&reply, sid, storeId);
}
hkQueue->reply(&reply);
result = hkQueue->reply(&reply);
if (result != HasReturnvaluesIF::RETURN_OK) {
ipcStore->deleteData(storeId);
}
return result;
}

View File

@ -94,13 +94,14 @@ ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
const uint8_t validityMaskSize = std::ceil(static_cast<float>(fillCount) / 8.0);
uint8_t *validityPtr = nullptr;
#ifdef _MSC_VER
/* Use a std::vector here because MSVC will (rightly) not create a fixed size array
with a non constant size specifier */
std::vector<uint8_t> validityMask(validityMaskSize);
#if defined(_MSC_VER) || defined(__clang__)
// Use a std::vector here because MSVC will (rightly) not create a fixed size array
// with a non constant size specifier. The Apple compiler (LLVM) will not accept
// the initialization of a variable sized array
std::vector<uint8_t> validityMask(validityMaskSize, 0);
validityPtr = validityMask.data();
#else
uint8_t validityMask[validityMaskSize] = {0};
uint8_t validityMask[validityMaskSize] = {};
validityPtr = validityMask;
#endif
uint8_t validBufferIndex = 0;

View File

@ -162,6 +162,7 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
object_id_t getCreatorObjectId();
bool getReportingEnabled() const;
void setReportingEnabled(bool enabled);
/**
* Returns the current periodic HK generation interval this set
@ -189,7 +190,6 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
* Used for periodic generation.
*/
bool reportingEnabled = false;
void setReportingEnabled(bool enabled);
void initializePeriodicHelper(float collectionInterval, dur_millis_t minimumPeriodicInterval,
uint8_t nonDiagIntervalFactor = 5);

View File

@ -23,8 +23,8 @@ class LocalPoolObjectBase : public PoolVariableIF, public HasReturnvaluesIF, pub
LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId, DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
void setReadWriteMode(pool_rwm_t newReadWriteMode);
pool_rwm_t getReadWriteMode() const;
void setReadWriteMode(pool_rwm_t newReadWriteMode) override;
pool_rwm_t getReadWriteMode() const override;
bool isValid() const override;
void setValid(bool valid) override;

View File

@ -125,13 +125,14 @@ ReturnValue_t DeviceHandlerBase::initialize() {
return result;
}
if (this->fdirInstance == nullptr) {
this->fdirInstance = new DeviceHandlerFailureIsolation(this->getObjectId(), defaultFdirParentId);
this->fdirInstance =
new DeviceHandlerFailureIsolation(this->getObjectId(), defaultFdirParentId);
}
if(this->parent != objects::NO_OBJECT) {
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) {
if (modeIF != nullptr and healthIF != nullptr) {
setParentQueue(modeIF->getCommandQueue());
}
}
@ -242,17 +243,28 @@ ReturnValue_t DeviceHandlerBase::initialize() {
}
void DeviceHandlerBase::decrementDeviceReplyMap() {
bool timedOut = false;
for (std::pair<const DeviceCommandId_t, DeviceReplyInfo>& replyPair : deviceReplyMap) {
if (replyPair.second.delayCycles != 0) {
if (replyPair.second.countdown != nullptr && replyPair.second.active) {
if (replyPair.second.countdown->hasTimedOut()) {
timedOut = true;
}
}
if (replyPair.second.delayCycles != 0 && replyPair.second.countdown == nullptr) {
replyPair.second.delayCycles--;
if (replyPair.second.delayCycles == 0) {
if (replyPair.second.periodic) {
replyPair.second.delayCycles = replyPair.second.maxDelayCycles;
}
replyToReply(replyPair.first, replyPair.second, TIMEOUT);
missedReply(replyPair.first);
timedOut = true;
}
}
if (timedOut) {
replyToReply(replyPair.first, replyPair.second, TIMEOUT);
missedReply(replyPair.first);
timedOut = false;
replyPair.second.active = false;
}
}
}
@ -361,14 +373,12 @@ void DeviceHandlerBase::doStateMachine() {
}
} break;
case _MODE_WAIT_OFF: {
uint32_t currentUptime;
Clock::getUptime(&currentUptime);
if (powerSwitcher == nullptr) {
setMode(MODE_OFF);
break;
}
uint32_t currentUptime;
Clock::getUptime(&currentUptime);
if (currentUptime - timeoutStart >= powerSwitcher->getSwitchDelayMs()) {
triggerEvent(MODE_TRANSITION_FAILED, PowerSwitchIF::SWITCH_TIMEOUT, 0);
setMode(MODE_ERROR_ON);
@ -417,20 +427,22 @@ ReturnValue_t DeviceHandlerBase::isModeCombinationValid(Mode_t mode, Submode_t s
ReturnValue_t DeviceHandlerBase::insertInCommandAndReplyMap(
DeviceCommandId_t deviceCommand, uint16_t maxDelayCycles, LocalPoolDataSetBase* replyDataSet,
size_t replyLen, bool periodic, bool hasDifferentReplyId, DeviceCommandId_t replyId) {
size_t replyLen, bool periodic, bool hasDifferentReplyId, DeviceCommandId_t replyId,
Countdown* countdown) {
// No need to check, as we may try to insert multiple times.
insertInCommandMap(deviceCommand);
insertInCommandMap(deviceCommand, hasDifferentReplyId, replyId);
if (hasDifferentReplyId) {
return insertInReplyMap(replyId, maxDelayCycles, replyDataSet, replyLen, periodic);
return insertInReplyMap(replyId, maxDelayCycles, replyDataSet, replyLen, periodic, countdown);
} else {
return insertInReplyMap(deviceCommand, maxDelayCycles, replyDataSet, replyLen, periodic);
return insertInReplyMap(deviceCommand, maxDelayCycles, replyDataSet, replyLen, periodic,
countdown);
}
}
ReturnValue_t DeviceHandlerBase::insertInReplyMap(DeviceCommandId_t replyId,
uint16_t maxDelayCycles,
LocalPoolDataSetBase* dataSet, size_t replyLen,
bool periodic) {
bool periodic, Countdown* countdown) {
DeviceReplyInfo info;
info.maxDelayCycles = maxDelayCycles;
info.periodic = periodic;
@ -438,6 +450,10 @@ ReturnValue_t DeviceHandlerBase::insertInReplyMap(DeviceCommandId_t replyId,
info.replyLen = replyLen;
info.dataSet = dataSet;
info.command = deviceCommandMap.end();
info.countdown = countdown;
if (info.periodic) {
info.active = true;
}
auto resultPair = deviceReplyMap.emplace(replyId, info);
if (resultPair.second) {
return RETURN_OK;
@ -446,11 +462,15 @@ ReturnValue_t DeviceHandlerBase::insertInReplyMap(DeviceCommandId_t replyId,
}
}
ReturnValue_t DeviceHandlerBase::insertInCommandMap(DeviceCommandId_t deviceCommand) {
ReturnValue_t DeviceHandlerBase::insertInCommandMap(DeviceCommandId_t deviceCommand,
bool useAlternativeReply,
DeviceCommandId_t alternativeReplyId) {
DeviceCommandInfo info;
info.expectedReplies = 0;
info.isExecuting = false;
info.sendReplyTo = NO_COMMANDER;
info.useAlternativeReplyId = alternativeReplyId;
info.alternativeReplyId = alternativeReplyId;
auto resultPair = deviceCommandMap.emplace(deviceCommand, info);
if (resultPair.second) {
return RETURN_OK;
@ -460,12 +480,21 @@ ReturnValue_t DeviceHandlerBase::insertInCommandMap(DeviceCommandId_t deviceComm
}
size_t DeviceHandlerBase::getNextReplyLength(DeviceCommandId_t commandId) {
DeviceReplyIter iter = deviceReplyMap.find(commandId);
if (iter != deviceReplyMap.end()) {
return iter->second.replyLen;
DeviceCommandId_t replyId = NO_COMMAND_ID;
DeviceCommandMap::iterator command = cookieInfo.pendingCommand;
if (command->second.useAlternativeReplyId) {
replyId = command->second.alternativeReplyId;
} else {
return 0;
replyId = commandId;
}
DeviceReplyIter iter = deviceReplyMap.find(replyId);
if (iter != deviceReplyMap.end()) {
if ((iter->second.delayCycles != 0 && iter->second.countdown == nullptr) ||
(iter->second.active && iter->second.countdown != nullptr)) {
return iter->second.replyLen;
}
}
return 0;
}
ReturnValue_t DeviceHandlerBase::updateReplyMapEntry(DeviceCommandId_t deviceReply,
@ -660,7 +689,9 @@ void DeviceHandlerBase::doGetWrite() {
// We need to distinguish here, because a raw command never expects a reply.
//(Could be done in eRIRM, but then child implementations need to be careful.
result = enableReplyInReplyMap(cookieInfo.pendingCommand);
DeviceCommandMap::iterator command = cookieInfo.pendingCommand;
result = enableReplyInReplyMap(command, 1, command->second.useAlternativeReplyId,
command->second.alternativeReplyId);
} else {
// always generate a failure event, so that FDIR knows what's up
triggerEvent(DEVICE_SENDING_COMMAND_FAILED, result, cookieInfo.pendingCommand->first);
@ -803,17 +834,18 @@ void DeviceHandlerBase::handleReply(const uint8_t* receivedData, DeviceCommandId
DeviceReplyInfo* info = &(iter->second);
if (info->delayCycles != 0) {
if ((info->delayCycles != 0 && info->countdown == nullptr) ||
(info->active && info->countdown != nullptr)) {
result = interpretDeviceReply(foundId, receivedData);
if (result == IGNORE_REPLY_DATA) {
return;
}
if (info->periodic) {
info->delayCycles = info->maxDelayCycles;
} else {
info->delayCycles = 0;
if (info->active && info->countdown != nullptr) {
disableTimeoutControlledReply(info);
} else if (info->delayCycles != 0) {
disableDelayCyclesControlledReply(info);
}
if (result != RETURN_OK) {
@ -832,6 +864,24 @@ void DeviceHandlerBase::handleReply(const uint8_t* receivedData, DeviceCommandId
}
}
void DeviceHandlerBase::disableTimeoutControlledReply(DeviceReplyInfo* info) {
if (info->periodic) {
info->countdown->resetTimer();
} else {
info->active = false;
info->countdown->timeOut();
}
}
void DeviceHandlerBase::disableDelayCyclesControlledReply(DeviceReplyInfo* info) {
if (info->periodic) {
info->delayCycles = info->maxDelayCycles;
} else {
info->delayCycles = 0;
info->active = false;
}
}
ReturnValue_t DeviceHandlerBase::getStorageData(store_address_t storageAddress, uint8_t** data,
size_t* len) {
size_t lenTmp;
@ -957,6 +1007,10 @@ ReturnValue_t DeviceHandlerBase::enableReplyInReplyMap(DeviceCommandMap::iterato
info->delayCycles = info->maxDelayCycles;
info->command = command;
command->second.expectedReplies = expectedReplies;
if (info->countdown != nullptr) {
info->countdown->resetTimer();
}
info->active = true;
return RETURN_OK;
} else {
return NO_REPLY_EXPECTED;
@ -1191,7 +1245,8 @@ void DeviceHandlerBase::setParentQueue(MessageQueueId_t parentQueueId) {
bool DeviceHandlerBase::isAwaitingReply() {
std::map<DeviceCommandId_t, DeviceReplyInfo>::iterator iter;
for (iter = deviceReplyMap.begin(); iter != deviceReplyMap.end(); ++iter) {
if (iter->second.delayCycles != 0) {
if ((iter->second.delayCycles != 0 && iter->second.countdown == nullptr) ||
(iter->second.active && iter->second.countdown != nullptr)) {
return true;
}
}
@ -1347,6 +1402,9 @@ uint8_t DeviceHandlerBase::getReplyDelayCycles(DeviceCommandId_t deviceCommand)
if (iter == deviceReplyMap.end()) {
return 0;
}
else if (iter->second.countdown != nullptr) {
return 0;
}
return iter->second.delayCycles;
}
@ -1507,4 +1565,6 @@ void DeviceHandlerBase::setCustomFdir(FailureIsolationBase* fdir) { this->fdirIn
void DeviceHandlerBase::setParent(object_id_t parent) { this->parent = parent; }
void DeviceHandlerBase::setPowerSwitcher(PowerSwitchIF* switcher) { this->powerSwitcher = switcher; }
void DeviceHandlerBase::setPowerSwitcher(PowerSwitchIF* switcher) {
this->powerSwitcher = switcher;
}

View File

@ -103,9 +103,9 @@ 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 setCustomFdir(FailureIsolationBase *fdir);
void setParent(object_id_t parent);
void setPowerSwitcher(PowerSwitchIF* switcher);
void setPowerSwitcher(PowerSwitchIF *switcher);
void setHkDestination(object_id_t hkDestination);
/**
@ -166,7 +166,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
* @param counter Specifies which Action to perform
* @return RETURN_OK for successful execution
*/
virtual ReturnValue_t performOperation(uint8_t counter);
virtual ReturnValue_t performOperation(uint8_t counter) override;
/**
* @brief Initializes the device handler
@ -176,7 +176,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
* Calls fillCommandAndReplyMap().
* @return
*/
virtual ReturnValue_t initialize();
virtual ReturnValue_t initialize() override;
/**
* @brief Intialization steps performed after all tasks have been created.
@ -451,6 +451,9 @@ class DeviceHandlerBase : public DeviceHandlerIF,
* 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
* @return - @c RETURN_OK when the command was successfully inserted,
* - @c RETURN_FAILED else.
*/
@ -458,7 +461,8 @@ class DeviceHandlerBase : public DeviceHandlerIF,
LocalPoolDataSetBase *replyDataSet = nullptr,
size_t replyLen = 0, bool periodic = false,
bool hasDifferentReplyId = false,
DeviceCommandId_t replyId = 0);
DeviceCommandId_t replyId = 0,
Countdown *countdown = nullptr);
/**
* @brief This is a helper method to insert replies in the reply map.
* @param deviceCommand Identifier of the reply to add.
@ -468,12 +472,15 @@ class DeviceHandlerBase : public DeviceHandlerIF,
* 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
* @return - @c RETURN_OK when the command was successfully inserted,
* - @c RETURN_FAILED else.
*/
ReturnValue_t insertInReplyMap(DeviceCommandId_t deviceCommand, uint16_t maxDelayCycles,
LocalPoolDataSetBase *dataSet = nullptr, size_t replyLen = 0,
bool periodic = false);
bool periodic = false, Countdown *countdown = nullptr);
/**
* @brief A simple command to add a command to the commandList.
@ -481,7 +488,9 @@ class DeviceHandlerBase : public DeviceHandlerIF,
* @return - @c RETURN_OK when the command was successfully inserted,
* - @c RETURN_FAILED else.
*/
ReturnValue_t insertInCommandMap(DeviceCommandId_t deviceCommand);
ReturnValue_t insertInCommandMap(DeviceCommandId_t deviceCommand,
bool useAlternativeReply = false,
DeviceCommandId_t alternativeReplyId = 0);
/**
* Enables a periodic reply for a given command. It sets to delay cycles to the specified
@ -760,6 +769,8 @@ class DeviceHandlerBase : public DeviceHandlerIF,
//! if this is != NO_COMMANDER, DHB was commanded externally and shall
//! report everything to commander.
MessageQueueId_t sendReplyTo;
bool useAlternativeReplyId;
DeviceCommandId_t alternativeReplyId;
};
using DeviceCommandMap = std::map<DeviceCommandId_t, DeviceCommandInfo>;
/**
@ -788,6 +799,11 @@ class DeviceHandlerBase : public DeviceHandlerIF,
LocalPoolDataSetBase *dataSet = nullptr;
//! 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
//! is also possible specify a countdown
Countdown* countdown = nullptr;
//! will be set to true when reply is enabled
bool active = false;
};
using DeviceReplyMap = std::map<DeviceCommandId_t, DeviceReplyInfo>;
@ -1064,11 +1080,12 @@ class DeviceHandlerBase : public DeviceHandlerIF,
* @param parameter1 Optional parameter 1
* @param parameter2 Optional parameter 2
*/
void triggerEvent(Event event, uint32_t parameter1 = 0, uint32_t parameter2 = 0);
void triggerEvent(Event event, uint32_t parameter1 = 0, uint32_t parameter2 = 0) override;
/**
* Same as triggerEvent, but for forwarding if object is used as proxy.
*/
virtual void forwardEvent(Event event, uint32_t parameter1 = 0, uint32_t parameter2 = 0) const;
virtual void forwardEvent(Event event, uint32_t parameter1 = 0,
uint32_t parameter2 = 0) const override;
/**
* Checks if current mode is transitional mode.
@ -1249,6 +1266,17 @@ class DeviceHandlerBase : public DeviceHandlerIF,
*/
void doGetRead(void);
/**
* @brief Handles disabling of replies which use a timeout to detect missed replies.
*/
void disableTimeoutControlledReply(DeviceReplyInfo* info);
/**
* @brief Handles disabling of replies which use a number of maximum delay cycles to detect
* missed replies.
*/
void disableDelayCyclesControlledReply(DeviceReplyInfo* info);
/**
* Retrive data from the #IPCStore.
*

View File

@ -1,13 +1,13 @@
target_sources(${LIB_FSFW_NAME}
PRIVATE
arrayprinter.cpp
AsciiConverter.cpp
CRC.cpp
DleEncoder.cpp
PeriodicOperationDivider.cpp
timevalOperations.cpp
Type.cpp
bitutility.cpp
target_sources(${LIB_FSFW_NAME} PRIVATE
arrayprinter.cpp
AsciiConverter.cpp
CRC.cpp
DleEncoder.cpp
DleParser.cpp
PeriodicOperationDivider.cpp
timevalOperations.cpp
Type.cpp
bitutility.cpp
)
add_subdirectory(math)

View File

@ -0,0 +1,231 @@
#include "DleParser.h"
#include <fsfw/globalfunctions/DleEncoder.h>
#include <fsfw/serviceinterface/ServiceInterface.h>
#include <cstdio>
DleParser::DleParser(SimpleRingBuffer& decodeRingBuf, DleEncoder& decoder, BufPair encodedBuf,
BufPair decodedBuf, UserHandler handler, void* args)
: 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
}
}
ReturnValue_t DleParser::passData(uint8_t* data, size_t len) {
if (data == nullptr or len == 0 or handler == nullptr) {
return RETURN_FAILED;
}
size_t copyIntoRingBufFromHere = 0;
size_t copyAmount = len;
size_t startIdx = 0;
ReturnValue_t result = RETURN_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;
} else {
// Maybe print warning, should not happen
decodeRingBuf.clear();
ErrorInfo info;
info.len = idx;
prepareErrorContext(ErrorTypes::CONSECUTIVE_STX_CHARS, info);
handler(ctx);
copyIntoRingBufFromHere = idx;
copyAmount = len - idx;
}
startFound = true;
startFoundInThisPacket = true;
} else if (data[idx] == DleEncoder::ETX_CHAR) {
if (startFoundInThisPacket) {
size_t readLen = 0;
size_t decodedLen = 0;
result = decoder.decode(data + startIdx, idx + 1 - startIdx, &readLen, decodedBuf.first,
decodedBuf.second, &decodedLen);
if (result == HasReturnvaluesIF::RETURN_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) {
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 != HasReturnvaluesIF::RETURN_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 == HasReturnvaluesIF::RETURN_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;
}
}
} else {
// End data without preceeding STX
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;
}
}
startFoundInThisPacket = false;
startFound = false;
}
}
if (copyAmount > 0) {
result = decodeRingBuf.writeData(data + copyIntoRingBufFromHere, copyAmount);
if (result != HasReturnvaluesIF::RETURN_OK) {
ErrorInfo info;
info.res = result;
prepareErrorContext(ErrorTypes::RING_BUF_ERROR, info);
handler(ctx);
}
}
return RETURN_OK;
}
void DleParser::defaultFoundPacketHandler(uint8_t* packet, size_t len, void* args) {
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "DleParserBase::handleFoundPacket: Detected DLE packet with " << len << " bytes"
<< std::endl;
#else
sif::printInfo("DleParserBase::handleFoundPacket: Detected DLE packet with %d bytes\n", len);
#endif
#endif
}
void DleParser::defaultErrorHandler(ErrorTypes err, ErrorInfo ctx) {
switch (err) {
case (ErrorTypes::NONE): {
errorPrinter("No error");
break;
}
case (ErrorTypes::DECODE_ERROR): {
errorPrinter("Decode Error");
break;
}
case (ErrorTypes::RING_BUF_ERROR): {
errorPrinter("Ring Buffer Error");
break;
}
case (ErrorTypes::ENCODED_BUF_TOO_SMALL):
case (ErrorTypes::DECODING_BUF_TOO_SMALL): {
char opt[64];
snprintf(opt, sizeof(opt), ": Too small for packet with length %zu", ctx.len);
if (err == ErrorTypes::ENCODED_BUF_TOO_SMALL) {
errorPrinter("Encoded buf too small", opt);
} else {
errorPrinter("Decoding buf too small", opt);
}
break;
}
case (ErrorTypes::CONSECUTIVE_STX_CHARS): {
errorPrinter("Consecutive STX chars detected");
break;
}
case (ErrorTypes::CONSECUTIVE_ETX_CHARS): {
errorPrinter("Consecutive ETX chars detected");
break;
}
}
}
void DleParser::errorPrinter(const char* str, const char* opt) {
if (opt == nullptr) {
opt = "";
}
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "DleParserBase::handleParseError: " << str << opt << std::endl;
#else
sif::printInfo("DleParserBase::handleParseError: %s%s\n", str, opt);
#endif
#endif
}
void DleParser::prepareErrorContext(ErrorTypes err, ErrorInfo info) {
ctx.setType(ContextType::ERROR);
ctx.error.first = err;
ctx.error.second = info;
}
void DleParser::reset() {
startFound = false;
decodeRingBuf.clear();
}

View File

@ -0,0 +1,127 @@
#ifndef MISSION_DEVICES_DLEPARSER_H_
#define MISSION_DEVICES_DLEPARSER_H_
#include <fsfw/container/SimpleRingBuffer.h>
#include <fsfw/globalfunctions/DleEncoder.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <cstddef>
#include <utility>
/**
* @brief This base helper class can be used to extract DLE encoded packets from a data stream
* @details
* The core API of the parser takes received packets which can contains DLE packets. The parser
* can deal with DLE packets split across multiple packets. It does so by using a dedicated
* decoding ring buffer. The user can process received packets and detect errors by
* overriding two provided virtual methods. This also allows detecting multiple DLE packets
* inside one passed packet.
*/
class DleParser : public HasReturnvaluesIF {
public:
using BufPair = std::pair<uint8_t*, size_t>;
enum class ContextType { PACKET_FOUND, ERROR };
enum class ErrorTypes {
NONE,
ENCODED_BUF_TOO_SMALL,
DECODING_BUF_TOO_SMALL,
DECODE_ERROR,
RING_BUF_ERROR,
CONSECUTIVE_STX_CHARS,
CONSECUTIVE_ETX_CHARS
};
union ErrorInfo {
size_t len;
ReturnValue_t res;
};
using ErrorPair = std::pair<ErrorTypes, ErrorInfo>;
struct Context {
public:
Context(void* args) : userArgs(args) { setType(ContextType::PACKET_FOUND); }
void setType(ContextType type) {
if (type == ContextType::PACKET_FOUND) {
error.first = ErrorTypes::NONE;
error.second.len = 0;
} else {
decodedPacket.first = nullptr;
decodedPacket.second = 0;
}
}
ContextType getType() const { return type; }
BufPair decodedPacket = {};
ErrorPair error;
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
* split across multiple packets
* @param decoder Decoder instance
* @param encodedBuf Buffer used to store encoded packets. It has to be large enough to hold
* the largest expected encoded DLE packet size
* @param decodedBuf Buffer used to store decoded packets. It has to be large enough to hold the
* largest expected decoded DLE packet size
* @param handler Function which will be called on a found packet
* @param args Arbitrary user argument
*/
DleParser(SimpleRingBuffer& decodeRingBuf, DleEncoder& decoder, BufPair encodedBuf,
BufPair decodedBuf, UserHandler handler, void* args);
/**
* This function allows to pass new data into the parser. It then scans for DLE packets
* automatically and inserts (part of) the packet into a ring buffer if necessary.
* @param data
* @param len
* @return
*/
ReturnValue_t passData(uint8_t* data, size_t len);
/**
* Example found packet handler
* function call
* @param packet Decoded packet
* @param len Length of detected packet
*/
void defaultFoundPacketHandler(uint8_t* packet, size_t len, void* args);
/**
* Will be called if an error occured in the #passData call
* @param err
* @param ctx Context information depending on the error type
* - For buffer length errors, will be set to the detected packet length which is too large
* - For decode or ring buffer errors, will be set to the result returned from the failed call
*/
static void defaultErrorHandler(ErrorTypes err, ErrorInfo ctx);
static void errorPrinter(const char* str, const char* opt = nullptr);
void prepareErrorContext(ErrorTypes err, ErrorInfo ctx);
/**
* Resets the parser by resetting the internal states and clearing the decoding ring buffer
*/
void reset();
private:
SimpleRingBuffer& decodeRingBuf;
DleEncoder& decoder;
BufPair encodedBuf;
BufPair decodedBuf;
UserHandler handler = nullptr;
Context ctx;
bool startFound = false;
};
#endif /* MISSION_DEVICES_DLEPARSER_H_ */

View File

@ -25,7 +25,7 @@ class MatchTree : public SerializeableMatcherIF<T>, public BinaryTree<Serializea
: BinaryTree<SerializeableMatcherIF<T>>(root.element), maxDepth(maxDepth) {}
MatchTree() : BinaryTree<SerializeableMatcherIF<T>>(), maxDepth(-1) {}
virtual ~MatchTree() {}
virtual bool match(T number) { return matchesTree(number); }
virtual bool match(T number) override { return matchesTree(number); }
bool matchesTree(T number) {
iterator iter = this->begin();
if (iter == this->end()) {
@ -179,6 +179,9 @@ class MatchTree : public SerializeableMatcherIF<T>, public BinaryTree<Serializea
virtual ReturnValue_t cleanUpElement(iterator position) { return HasReturnvaluesIF::RETURN_OK; }
bool matchSubtree(iterator iter, T number) {
if (iter == nullptr) {
return false;
}
bool isMatch = iter->match(number);
if (isMatch) {
if (iter.left() == this->end()) {

View File

@ -15,7 +15,7 @@ class RangeMatcher : public SerializeableMatcherIF<T> {
RangeMatcher(T lowerBound, T upperBound, bool inverted = false)
: inverted(inverted), lowerBound(lowerBound), upperBound(upperBound) {}
bool match(T input) {
bool match(T input) override {
if (inverted) {
return !doMatch(input);
} else {

View File

@ -1,6 +1,6 @@
target_sources(${LIB_FSFW_NAME}
PRIVATE
CommandMessage.cpp
CommandMessageCleaner.cpp
MessageQueueMessage.cpp
target_sources(${LIB_FSFW_NAME} PRIVATE
CommandMessage.cpp
CommandMessageCleaner.cpp
MessageQueueMessage.cpp
MessageQueueBase.cpp
)

View File

@ -0,0 +1,54 @@
#include "MessageQueueBase.h"
MessageQueueBase::MessageQueueBase(MessageQueueId_t id, MessageQueueId_t defaultDest, MqArgs* args)
: id(id) {
this->defaultDest = defaultDest;
if (args != nullptr) {
this->args = *args;
}
}
MessageQueueBase::~MessageQueueBase() {}
ReturnValue_t MessageQueueBase::sendToDefault(MessageQueueMessageIF* message) {
return sendToDefaultFrom(message, this->getId(), false);
}
ReturnValue_t MessageQueueBase::reply(MessageQueueMessageIF* message) {
if (this->last != MessageQueueIF::NO_QUEUE) {
return sendMessageFrom(this->last, message, this->getId());
} else {
return NO_REPLY_PARTNER;
}
}
ReturnValue_t MessageQueueBase::receiveMessage(MessageQueueMessageIF* message,
MessageQueueId_t* receivedFrom) {
ReturnValue_t status = this->receiveMessage(message);
*receivedFrom = this->last;
return status;
}
MessageQueueId_t MessageQueueBase::getLastPartner() const { return last; }
MessageQueueId_t MessageQueueBase::getId() const { return id; }
MqArgs& MessageQueueBase::getMqArgs() { return args; }
void MessageQueueBase::setDefaultDestination(MessageQueueId_t defaultDestination) {
this->defaultDest = defaultDestination;
}
MessageQueueId_t MessageQueueBase::getDefaultDestination() const { return defaultDest; }
bool MessageQueueBase::isDefaultDestinationSet() const { return (defaultDest != NO_QUEUE); }
ReturnValue_t MessageQueueBase::sendMessage(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
bool ignoreFault) {
return sendMessageFrom(sendTo, message, this->getId(), false);
}
ReturnValue_t MessageQueueBase::sendToDefaultFrom(MessageQueueMessageIF* message,
MessageQueueId_t sentFrom, bool ignoreFault) {
return sendMessageFrom(defaultDest, message, sentFrom, ignoreFault);
}

View File

@ -0,0 +1,40 @@
#ifndef FSFW_SRC_FSFW_IPC_MESSAGEQUEUEBASE_H_
#define FSFW_SRC_FSFW_IPC_MESSAGEQUEUEBASE_H_
#include <fsfw/ipc/MessageQueueIF.h>
#include <fsfw/ipc/definitions.h>
class MessageQueueBase : public MessageQueueIF {
public:
MessageQueueBase(MessageQueueId_t id, MessageQueueId_t defaultDest, MqArgs* mqArgs);
virtual ~MessageQueueBase();
// Default implementations for MessageQueueIF where possible
virtual MessageQueueId_t getLastPartner() const override;
virtual MessageQueueId_t getId() const override;
virtual MqArgs& getMqArgs() override;
virtual void setDefaultDestination(MessageQueueId_t defaultDestination) override;
virtual MessageQueueId_t getDefaultDestination() const override;
virtual bool isDefaultDestinationSet() const override;
virtual ReturnValue_t sendMessage(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
bool ignoreFault) override;
virtual ReturnValue_t sendToDefault(MessageQueueMessageIF* message) override;
virtual ReturnValue_t reply(MessageQueueMessageIF* message) override;
virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message,
MessageQueueId_t* receivedFrom) override;
virtual ReturnValue_t sendToDefaultFrom(MessageQueueMessageIF* message, MessageQueueId_t sentFrom,
bool ignoreFault = false) override;
// OSAL specific, forward the abstract function
virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message) = 0;
virtual ReturnValue_t sendMessageFrom(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
MessageQueueId_t sentFrom, bool ignoreFault = false) = 0;
protected:
MessageQueueId_t id = MessageQueueIF::NO_QUEUE;
MessageQueueId_t last = MessageQueueIF::NO_QUEUE;
MessageQueueId_t defaultDest = MessageQueueIF::NO_QUEUE;
MqArgs args = {};
};
#endif /* FSFW_SRC_FSFW_IPC_MESSAGEQUEUEBASE_H_ */

View File

@ -1,6 +1,8 @@
#ifndef FSFW_IPC_MESSAGEQUEUEIF_H_
#define FSFW_IPC_MESSAGEQUEUEIF_H_
#include <fsfw/ipc/definitions.h>
#include <cstdint>
#include "../returnvalues/HasReturnvaluesIF.h"
@ -44,8 +46,8 @@ class MessageQueueIF {
virtual ReturnValue_t reply(MessageQueueMessageIF* message) = 0;
/**
* @brief This function reads available messages from the message queue
* and returns the sender.
* @brief This function reads available messages from the message queue and returns the
* sender.
* @details
* It works identically to the other receiveMessage call, but in addition
* returns the sender's queue id.
@ -78,19 +80,16 @@ class MessageQueueIF {
*/
virtual ReturnValue_t flush(uint32_t* count) = 0;
/**
* @brief This method returns the message queue
* id of the last communication partner.
* @brief This method returns the message queue ID of the last communication partner.
*/
virtual MessageQueueId_t getLastPartner() const = 0;
/**
* @brief This method returns the message queue
* id of this class's message queue.
* @brief This method returns the message queue ID of this class's message queue.
*/
virtual MessageQueueId_t getId() const = 0;
/**
* @brief With the sendMessage call, a queue message
* is sent to a receiving queue.
* @brief With the sendMessage call, a queue message is sent to a receiving queue.
* @details
* This method takes the message provided, adds the sentFrom information
* and passes it on to the destination provided with an operating system
@ -129,8 +128,7 @@ class MessageQueueIF {
bool ignoreFault = false) = 0;
/**
* @brief The sendToDefaultFrom method sends a queue message
* to the default destination.
* @brief The sendToDefaultFrom method sends a queue message to the default destination.
* @details
* In all other aspects, it works identical to the sendMessage method.
* @param message
@ -164,6 +162,8 @@ class MessageQueueIF {
virtual MessageQueueId_t getDefaultDestination() const = 0;
virtual bool isDefaultDestinationSet() const = 0;
virtual MqArgs& getMqArgs() = 0;
};
#endif /* FSFW_IPC_MESSAGEQUEUEIF_H_ */

View File

@ -1,11 +1,13 @@
#ifndef FSFW_SRC_FSFW_IPC_DEFINITIONS_H_
#define FSFW_SRC_FSFW_IPC_DEFINITIONS_H_
#include <fsfw/objectmanager/SystemObjectIF.h>
#include <fsfw/objectmanager/frameworkObjects.h>
struct MqArgs {
MqArgs(){};
MqArgs(object_id_t objectId, void* args = nullptr) : objectId(objectId), args(args) {}
object_id_t objectId = 0;
object_id_t objectId = objects::NO_OBJECT;
void* args = nullptr;
};

View File

@ -34,7 +34,7 @@ class MonitoringReportContent : public SerialLinkedListAdapter<SerializeIF> {
SerializeElement<T> limitValue;
SerializeElement<ReturnValue_t> oldState;
SerializeElement<ReturnValue_t> newState;
uint8_t rawTimestamp[TimeStamperIF::MISSION_TIMESTAMP_SIZE];
uint8_t rawTimestamp[TimeStamperIF::MISSION_TIMESTAMP_SIZE] = {};
SerializeElement<SerialBufferAdapter<uint8_t>> timestampSerializer;
TimeStamperIF* timeStamper;
MonitoringReportContent()
@ -46,7 +46,6 @@ class MonitoringReportContent : public SerialLinkedListAdapter<SerializeIF> {
limitValue(0),
oldState(0),
newState(0),
rawTimestamp({0}),
timestampSerializer(rawTimestamp, sizeof(rawTimestamp)),
timeStamper(NULL) {
setAllNext();

View File

@ -48,9 +48,10 @@ class SystemObject : public SystemObjectIF {
virtual ~SystemObject();
object_id_t getObjectId() const override;
virtual ReturnValue_t initialize() override;
virtual ReturnValue_t checkObjectConnections();
virtual ReturnValue_t checkObjectConnections() override;
virtual void forwardEvent(Event event, uint32_t parameter1 = 0, uint32_t parameter2 = 0) const;
virtual void forwardEvent(Event event, uint32_t parameter1 = 0,
uint32_t parameter2 = 0) const override;
};
#endif /* FSFW_OBJECTMANAGER_SYSTEMOBJECT_H_ */

View File

@ -16,11 +16,13 @@
//! Debugging preprocessor define.
#define FSFW_UDP_RECV_WIRETAPPING_ENABLED 0
const timeval UdpTcPollingTask::DEFAULT_TIMEOUT = {0, 500000};
UdpTcPollingTask::UdpTcPollingTask(object_id_t objectId, object_id_t tmtcUdpBridge,
size_t maxRecvSize, double timeoutSeconds)
: SystemObject(objectId), tmtcBridgeId(tmtcUdpBridge) {
if (frameSize > 0) {
this->frameSize = frameSize;
if (maxRecvSize > 0) {
this->frameSize = maxRecvSize;
} else {
this->frameSize = DEFAULT_MAX_RECV_SIZE;
}
@ -31,22 +33,20 @@ UdpTcPollingTask::UdpTcPollingTask(object_id_t objectId, object_id_t tmtcUdpBrid
receptionBuffer.resize(this->frameSize);
if (timeoutSeconds == -1) {
receptionTimeout = DEFAULT_TIMEOUT;
receptionTimeout = UdpTcPollingTask::DEFAULT_TIMEOUT;
} else {
receptionTimeout = timevalOperations::toTimeval(timeoutSeconds);
}
}
UdpTcPollingTask::~UdpTcPollingTask() {}
ReturnValue_t UdpTcPollingTask::performOperation(uint8_t opCode) {
[[noreturn]] ReturnValue_t UdpTcPollingTask::performOperation(uint8_t opCode) {
/* Sender Address is cached here. */
struct sockaddr senderAddress;
struct sockaddr senderAddress {};
socklen_t senderAddressSize = sizeof(senderAddress);
/* Poll for new UDP datagrams in permanent loop. */
while (true) {
int bytesReceived =
ssize_t bytesReceived =
recvfrom(this->serverSocket, reinterpret_cast<char*>(receptionBuffer.data()), frameSize,
receptionFlags, &senderAddress, &senderAddressSize);
if (bytesReceived == SOCKET_ERROR) {
@ -70,7 +70,6 @@ ReturnValue_t UdpTcPollingTask::performOperation(uint8_t opCode) {
}
tmtcBridge->checkAndSetClientAddress(senderAddress);
}
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t UdpTcPollingTask::handleSuccessfullTcRead(size_t bytesRead) {
@ -155,7 +154,7 @@ void UdpTcPollingTask::setTimeout(double timeoutSeconds) {
#endif
}
#elif defined(PLATFORM_UNIX)
timeval tval;
timeval tval{};
tval = timevalOperations::toTimeval(timeoutSeconds);
int result = setsockopt(serverSocket, SOL_SOCKET, SO_RCVTIMEO, &tval, sizeof(receptionTimeout));
if (result == -1) {

View File

@ -21,11 +21,11 @@ class UdpTcPollingTask : public TcpIpBase, public SystemObject, public Executabl
public:
static constexpr size_t DEFAULT_MAX_RECV_SIZE = 1500;
//! 0.5 default milliseconds timeout for now.
static constexpr timeval DEFAULT_TIMEOUT = {0, 500};
static const timeval DEFAULT_TIMEOUT;
UdpTcPollingTask(object_id_t objectId, object_id_t tmtcUdpBridge, size_t maxRecvSize = 0,
double timeoutSeconds = -1);
virtual ~UdpTcPollingTask();
~UdpTcPollingTask() override = default;
/**
* Turn on optional timeout for UDP polling. In the default mode,
@ -34,9 +34,9 @@ class UdpTcPollingTask : public TcpIpBase, public SystemObject, public Executabl
*/
void setTimeout(double timeoutSeconds);
virtual ReturnValue_t performOperation(uint8_t opCode) override;
virtual ReturnValue_t initialize() override;
virtual ReturnValue_t initializeAfterTaskCreation() override;
[[noreturn]] ReturnValue_t performOperation(uint8_t opCode) override;
ReturnValue_t initialize() override;
ReturnValue_t initializeAfterTaskCreation() override;
protected:
StorageManagerIF* tcStore = nullptr;
@ -51,7 +51,7 @@ class UdpTcPollingTask : public TcpIpBase, public SystemObject, public Executabl
std::vector<uint8_t> receptionBuffer;
size_t frameSize = 0;
timeval receptionTimeout;
timeval receptionTimeout{};
ReturnValue_t handleSuccessfullTcRead(size_t bytesRead);
};

View File

@ -20,13 +20,13 @@
const std::string UdpTmTcBridge::DEFAULT_SERVER_PORT = tcpip::DEFAULT_SERVER_PORT;
UdpTmTcBridge::UdpTmTcBridge(object_id_t objectId, object_id_t tcDestination,
std::string udpServerPort, object_id_t tmStoreId,
const std::string &udpServerPort_, object_id_t tmStoreId,
object_id_t tcStoreId)
: TmTcBridge(objectId, tcDestination, tmStoreId, tcStoreId) {
if (udpServerPort == "") {
this->udpServerPort = DEFAULT_SERVER_PORT;
if (udpServerPort_.empty()) {
udpServerPort = DEFAULT_SERVER_PORT;
} else {
this->udpServerPort = udpServerPort;
udpServerPort = udpServerPort_;
}
mutex = MutexFactory::instance()->createMutex();
@ -117,8 +117,8 @@ ReturnValue_t UdpTmTcBridge::sendTm(const uint8_t *data, size_t dataLen) {
tcpip::printAddress(&clientAddress);
#endif
int bytesSent = sendto(serverSocket, reinterpret_cast<const char *>(data), dataLen, flags,
&clientAddress, clientAddressLen);
ssize_t bytesSent = sendto(serverSocket, reinterpret_cast<const char *>(data), dataLen, flags,
&clientAddress, clientAddressLen);
if (bytesSent == SOCKET_ERROR) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "TmTcUdpBridge::sendTm: Send operation failed." << std::endl;
@ -150,7 +150,7 @@ void UdpTmTcBridge::checkAndSetClientAddress(sockaddr &newAddress) {
clientAddressLen = sizeof(clientAddress);
}
void UdpTmTcBridge::setMutexProperties(MutexIF::TimeoutType timeoutType, dur_millis_t timeoutMs) {
this->timeoutType = timeoutType;
this->mutexTimeoutMs = timeoutMs;
void UdpTmTcBridge::setMutexProperties(MutexIF::TimeoutType timeoutType_, dur_millis_t timeoutMs) {
timeoutType = timeoutType_;
mutexTimeoutMs = timeoutMs;
}

View File

@ -29,10 +29,10 @@ class UdpTmTcBridge : public TmTcBridge, public TcpIpBase {
/* The ports chosen here should not be used by any other process. */
static const std::string DEFAULT_SERVER_PORT;
UdpTmTcBridge(object_id_t objectId, object_id_t tcDestination, std::string udpServerPort = "",
object_id_t tmStoreId = objects::TM_STORE,
UdpTmTcBridge(object_id_t objectId, object_id_t tcDestination,
const std::string& udpServerPort = "", object_id_t tmStoreId = objects::TM_STORE,
object_id_t tcStoreId = objects::TC_STORE);
virtual ~UdpTmTcBridge();
~UdpTmTcBridge() override;
/**
* Set properties of internal mutex.
@ -46,12 +46,12 @@ class UdpTmTcBridge : public TmTcBridge, public TcpIpBase {
std::string getUdpPort() const;
protected:
virtual ReturnValue_t sendTm(const uint8_t* data, size_t dataLen) override;
ReturnValue_t sendTm(const uint8_t* data, size_t dataLen) override;
private:
std::string udpServerPort;
struct sockaddr clientAddress;
struct sockaddr clientAddress = {};
socklen_t clientAddressLen = 0;
//! Access to the client address is mutex protected as it is set by another task.

View File

@ -11,9 +11,6 @@
// TODO sanitize input?
// TODO much of this code can be reused for tick-only systems
uint16_t Clock::leapSeconds = 0;
MutexIF* Clock::timeMutex = nullptr;
uint32_t Clock::getTicksPerSecond(void) { return 1000; }
ReturnValue_t Clock::setClock(const TimeOfDay_t* time) {

View File

@ -5,7 +5,8 @@
#include "fsfw/serviceinterface/ServiceInterface.h"
MessageQueue::MessageQueue(size_t messageDepth, size_t maxMessageSize, MqArgs* args)
: maxMessageSize(maxMessageSize) {
: MessageQueueBase(MessageQueueIF::NO_QUEUE, MessageQueueIF::NO_QUEUE, args),
maxMessageSize(maxMessageSize) {
handle = xQueueCreate(messageDepth, maxMessageSize);
if (handle == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
@ -15,10 +16,10 @@ MessageQueue::MessageQueue(size_t messageDepth, size_t maxMessageSize, MqArgs* a
#else
sif::printError("MessageQueue::MessageQueue: Creation failed\n");
sif::printError("Specified Message Depth: %d\n", messageDepth);
sif::printError("Specified MAximum Message Size: %d\n", maxMessageSize);
sif::printError("Specified Maximum Message Size: %d\n", maxMessageSize);
#endif
}
QueueMapManager::instance()->addMessageQueue(handle, &queueId);
QueueMapManager::instance()->addMessageQueue(handle, &id);
}
MessageQueue::~MessageQueue() {
@ -29,28 +30,6 @@ MessageQueue::~MessageQueue() {
void MessageQueue::switchSystemContext(CallContext callContext) { this->callContext = callContext; }
ReturnValue_t MessageQueue::sendMessage(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
bool ignoreFault) {
return sendMessageFrom(sendTo, message, this->getId(), ignoreFault);
}
ReturnValue_t MessageQueue::sendToDefault(MessageQueueMessageIF* message) {
return sendToDefaultFrom(message, this->getId());
}
ReturnValue_t MessageQueue::sendToDefaultFrom(MessageQueueMessageIF* message,
MessageQueueId_t sentFrom, bool ignoreFault) {
return sendMessageFrom(defaultDestination, message, sentFrom, ignoreFault);
}
ReturnValue_t MessageQueue::reply(MessageQueueMessageIF* message) {
if (this->lastPartner != MessageQueueIF::NO_QUEUE) {
return sendMessageFrom(this->lastPartner, message, this->getId());
} else {
return NO_REPLY_PARTNER;
}
}
ReturnValue_t MessageQueue::sendMessageFrom(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
MessageQueueId_t sentFrom, bool ignoreFault) {
return sendMessageFromMessageQueue(sendTo, message, sentFrom, ignoreFault, callContext);
@ -72,27 +51,16 @@ ReturnValue_t MessageQueue::handleSendResult(BaseType_t result, bool ignoreFault
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message,
MessageQueueId_t* receivedFrom) {
ReturnValue_t status = this->receiveMessage(message);
if (status == HasReturnvaluesIF::RETURN_OK) {
*receivedFrom = this->lastPartner;
}
return status;
}
ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message) {
BaseType_t result = xQueueReceive(handle, reinterpret_cast<void*>(message->getBuffer()), 0);
if (result == pdPASS) {
this->lastPartner = message->getSender();
this->last = message->getSender();
return HasReturnvaluesIF::RETURN_OK;
} else {
return MessageQueueIF::EMPTY;
}
}
MessageQueueId_t MessageQueue::getLastPartner() const { return lastPartner; }
ReturnValue_t MessageQueue::flush(uint32_t* count) {
// TODO FreeRTOS does not support flushing partially
// Is always successful
@ -100,17 +68,6 @@ ReturnValue_t MessageQueue::flush(uint32_t* count) {
return HasReturnvaluesIF::RETURN_OK;
}
MessageQueueId_t MessageQueue::getId() const { return queueId; }
void MessageQueue::setDefaultDestination(MessageQueueId_t defaultDestination) {
defaultDestinationSet = true;
this->defaultDestination = defaultDestination;
}
MessageQueueId_t MessageQueue::getDefaultDestination() const { return defaultDestination; }
bool MessageQueue::isDefaultDestinationSet() const { return defaultDestinationSet; }
// static core function to send messages.
ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo,
MessageQueueMessageIF* message,

View File

@ -1,6 +1,8 @@
#ifndef FSFW_OSAL_FREERTOS_MESSAGEQUEUE_H_
#define FSFW_OSAL_FREERTOS_MESSAGEQUEUE_H_
#include <fsfw/ipc/MessageQueueBase.h>
#include "FreeRTOS.h"
#include "TaskManagement.h"
#include "fsfw/internalerror/InternalErrorReporterIF.h"
@ -33,7 +35,7 @@
* @ingroup osal
* @ingroup message_queue
*/
class MessageQueue : public MessageQueueIF {
class MessageQueue : public MessageQueueBase {
friend class MessageQueueSenderIF;
public:
@ -75,40 +77,15 @@ class MessageQueue : public MessageQueueIF {
*/
void switchSystemContext(CallContext callContext);
/** MessageQueueIF implementation */
ReturnValue_t sendMessage(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
bool ignoreFault = false) override;
QueueHandle_t getNativeQueueHandle();
ReturnValue_t sendToDefault(MessageQueueMessageIF* message) override;
ReturnValue_t reply(MessageQueueMessageIF* message) override;
// Implement non-generic MessageQueueIF functions not handled by MessageQueueBase
virtual ReturnValue_t sendMessageFrom(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
MessageQueueId_t sentFrom = NO_QUEUE,
bool ignoreFault = false) override;
virtual ReturnValue_t sendToDefaultFrom(MessageQueueMessageIF* message,
MessageQueueId_t sentFrom = NO_QUEUE,
bool ignoreFault = false) override;
ReturnValue_t receiveMessage(MessageQueueMessageIF* message,
MessageQueueId_t* receivedFrom) override;
ReturnValue_t receiveMessage(MessageQueueMessageIF* message) override;
ReturnValue_t flush(uint32_t* count) override;
MessageQueueId_t getLastPartner() const override;
MessageQueueId_t getId() const override;
void setDefaultDestination(MessageQueueId_t defaultDestination) override;
MessageQueueId_t getDefaultDestination() const override;
bool isDefaultDestinationSet() const override;
QueueHandle_t getNativeQueueHandle();
protected:
/**
* @brief Implementation to be called from any send Call within
@ -138,12 +115,8 @@ class MessageQueue : public MessageQueueIF {
static ReturnValue_t handleSendResult(BaseType_t result, bool ignoreFault);
private:
bool defaultDestinationSet = false;
QueueHandle_t handle;
MessageQueueId_t queueId = MessageQueueIF::NO_QUEUE;
MessageQueueId_t defaultDestination = MessageQueueIF::NO_QUEUE;
MessageQueueId_t lastPartner = MessageQueueIF::NO_QUEUE;
const size_t maxMessageSize;
//! Stores the current system context
CallContext callContext = CallContext::TASK;

View File

@ -97,7 +97,11 @@ void PeriodicTask::taskFunctionality() {
ReturnValue_t PeriodicTask::addComponent(object_id_t object) {
ExecutableObjectIF* newObject = ObjectManager::instance()->get<ExecutableObjectIF>(object);
if (newObject == nullptr) {
return addComponent(newObject);
}
ReturnValue_t PeriodicTask::addComponent(ExecutableObjectIF* object) {
if (object == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PeriodicTask::addComponent: Invalid object. Make sure"
"it implement ExecutableObjectIF"
@ -105,8 +109,8 @@ ReturnValue_t PeriodicTask::addComponent(object_id_t object) {
#endif
return HasReturnvaluesIF::RETURN_FAILED;
}
objectList.push_back(newObject);
newObject->setTaskIF(this);
objectList.push_back(object);
object->setTaskIF(this);
return HasReturnvaluesIF::RETURN_OK;
}

View File

@ -63,6 +63,16 @@ class PeriodicTask : public PeriodicTaskIF, public FreeRTOSTaskIF {
*/
ReturnValue_t addComponent(object_id_t object) override;
/**
* Adds an object to the list of objects to be executed.
* The objects are executed in the order added.
* @param object Id of the object to add.
* @return
* -@c RETURN_OK on success
* -@c RETURN_FAILED if the object could not be added.
*/
ReturnValue_t addComponent(ExecutableObjectIF* object) override;
uint32_t getPeriodMs() const override;
ReturnValue_t sleepFor(uint32_t ms) override;

View File

@ -1,24 +1,27 @@
target_sources(${LIB_FSFW_NAME}
PRIVATE
Clock.cpp
FixedTimeslotTask.cpp
MessageQueue.cpp
Mutex.cpp
MutexFactory.cpp
PeriodicTask.cpp
QueueFactory.cpp
QueueMapManager.cpp
SemaphoreFactory.cpp
TaskFactory.cpp
taskHelpers.cpp
target_sources(${LIB_FSFW_NAME} PRIVATE
Clock.cpp
FixedTimeslotTask.cpp
MessageQueue.cpp
Mutex.cpp
MutexFactory.cpp
PeriodicTask.cpp
QueueFactory.cpp
QueueMapManager.cpp
SemaphoreFactory.cpp
TaskFactory.cpp
taskHelpers.cpp
)
if(UNIX)
find_package(Threads REQUIRED)
target_link_libraries(${LIB_FSFW_NAME}
PRIVATE
rt
${CMAKE_THREAD_LIBS_INIT}
)
find_package(Threads REQUIRED)
target_link_libraries(${LIB_FSFW_NAME} PRIVATE
${CMAKE_THREAD_LIBS_INIT}
)
if(NOT APPLE)
target_link_libraries(${LIB_FSFW_NAME} PRIVATE
rt
)
endif()
endif()

View File

@ -2,6 +2,7 @@
#include <chrono>
#include "fsfw/ipc/MutexGuard.h"
#include "fsfw/platform.h"
#include "fsfw/serviceinterface/ServiceInterface.h"
@ -11,9 +12,6 @@
#include <fstream>
#endif
uint16_t Clock::leapSeconds = 0;
MutexIF* Clock::timeMutex = NULL;
using SystemClock = std::chrono::system_clock;
uint32_t Clock::getTicksPerSecond(void) {
@ -127,6 +125,13 @@ ReturnValue_t Clock::getDateAndTime(TimeOfDay_t* time) {
auto seconds = std::chrono::time_point_cast<std::chrono::seconds>(now);
auto fraction = now - seconds;
time_t tt = SystemClock::to_time_t(now);
ReturnValue_t result = checkOrCreateClockMutex();
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
MutexGuard helper(timeMutex);
// gmtime writes its output in a global buffer which is not Thread Safe
// Therefore we have to use a Mutex here
struct tm* timeInfo;
timeInfo = gmtime(&tt);
time->year = timeInfo->tm_year + 1900;

View File

@ -9,9 +9,11 @@
#include "fsfw/serviceinterface/ServiceInterface.h"
MessageQueue::MessageQueue(size_t messageDepth, size_t maxMessageSize, MqArgs* args)
: messageSize(maxMessageSize), messageDepth(messageDepth) {
: MessageQueueBase(MessageQueueIF::NO_QUEUE, MessageQueueIF::NO_QUEUE, args),
messageSize(maxMessageSize),
messageDepth(messageDepth) {
queueLock = MutexFactory::instance()->createMutex();
auto result = QueueMapManager::instance()->addMessageQueue(this, &mqId);
auto result = QueueMapManager::instance()->addMessageQueue(this, &id);
if (result != HasReturnvaluesIF::RETURN_OK) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "MessageQueue::MessageQueue: Could not be created" << std::endl;
@ -23,42 +25,11 @@ MessageQueue::MessageQueue(size_t messageDepth, size_t maxMessageSize, MqArgs* a
MessageQueue::~MessageQueue() { MutexFactory::instance()->deleteMutex(queueLock); }
ReturnValue_t MessageQueue::sendMessage(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
bool ignoreFault) {
return sendMessageFrom(sendTo, message, this->getId(), ignoreFault);
}
ReturnValue_t MessageQueue::sendToDefault(MessageQueueMessageIF* message) {
return sendToDefaultFrom(message, this->getId());
}
ReturnValue_t MessageQueue::sendToDefaultFrom(MessageQueueMessageIF* message,
MessageQueueId_t sentFrom, bool ignoreFault) {
return sendMessageFrom(defaultDestination, message, sentFrom, ignoreFault);
}
ReturnValue_t MessageQueue::reply(MessageQueueMessageIF* message) {
if (this->lastPartner != MessageQueueIF::NO_QUEUE) {
return sendMessageFrom(this->lastPartner, message, this->getId());
} else {
return MessageQueueIF::NO_REPLY_PARTNER;
}
}
ReturnValue_t MessageQueue::sendMessageFrom(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
MessageQueueId_t sentFrom, bool ignoreFault) {
return sendMessageFromMessageQueue(sendTo, message, sentFrom, ignoreFault);
}
ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message,
MessageQueueId_t* receivedFrom) {
ReturnValue_t status = this->receiveMessage(message);
if (status == HasReturnvaluesIF::RETURN_OK) {
*receivedFrom = this->lastPartner;
}
return status;
}
ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message) {
if (messageQueue.empty()) {
return MessageQueueIF::EMPTY;
@ -68,12 +39,10 @@ ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message) {
message->getBuffer());
messageQueue.pop();
// The last partner is the first uint32_t field in the message
this->lastPartner = message->getSender();
this->last = message->getSender();
return HasReturnvaluesIF::RETURN_OK;
}
MessageQueueId_t MessageQueue::getLastPartner() const { return lastPartner; }
ReturnValue_t MessageQueue::flush(uint32_t* count) {
*count = messageQueue.size();
// Clears the queue.
@ -81,17 +50,6 @@ ReturnValue_t MessageQueue::flush(uint32_t* count) {
return HasReturnvaluesIF::RETURN_OK;
}
MessageQueueId_t MessageQueue::getId() const { return mqId; }
void MessageQueue::setDefaultDestination(MessageQueueId_t defaultDestination) {
defaultDestinationSet = true;
this->defaultDestination = defaultDestination;
}
MessageQueueId_t MessageQueue::getDefaultDestination() const { return defaultDestination; }
bool MessageQueue::isDefaultDestinationSet() const { return defaultDestinationSet; }
// static core function to send messages.
ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo,
MessageQueueMessageIF* message,

View File

@ -5,6 +5,7 @@
#include <queue>
#include "fsfw/internalerror/InternalErrorReporterIF.h"
#include "fsfw/ipc/MessageQueueBase.h"
#include "fsfw/ipc/MessageQueueIF.h"
#include "fsfw/ipc/MessageQueueMessage.h"
#include "fsfw/ipc/MutexIF.h"
@ -34,7 +35,7 @@
* @ingroup osal
* @ingroup message_queue
*/
class MessageQueue : public MessageQueueIF {
class MessageQueue : public MessageQueueBase {
friend class MessageQueueSenderIF;
public:
@ -69,121 +70,12 @@ class MessageQueue : public MessageQueueIF {
*/
virtual ~MessageQueue();
/**
* @brief This operation sends a message to the given destination.
* @details It directly uses the sendMessage call of the MessageQueueSender
* parent, but passes its queue id as "sentFrom" parameter.
* @param sendTo This parameter specifies the message queue id of the
* destination message queue.
* @param message A pointer to a previously created message, which is sent.
* @param ignoreFault If set to true, the internal software fault counter
* is not incremented if queue is full.
*/
ReturnValue_t sendMessage(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
bool ignoreFault = false) override;
/**
* @brief This operation sends a message to the default destination.
* @details As in the sendMessage method, this function uses the
* sendToDefault call of the MessageQueueSender parent class and adds its
* queue id as "sentFrom" information.
* @param message A pointer to a previously created message, which is sent.
*/
ReturnValue_t sendToDefault(MessageQueueMessageIF* message) override;
/**
* @brief This operation sends a message to the last communication partner.
* @details This operation simplifies answering an incoming message by using
* the stored lastPartner information as destination. If there was no
* message received yet (i.e. lastPartner is zero), an error code is returned.
* @param message A pointer to a previously created message, which is sent.
*/
ReturnValue_t reply(MessageQueueMessageIF* message) override;
/**
* @brief With the sendMessage call, a queue message is sent to a
* receiving queue.
* @details
* This method takes the message provided, adds the sentFrom information and
* passes it on to the destination provided with an operating system call.
* The OS's return value is returned.
* @param sendTo This parameter specifies the message queue id to send
* the message to.
* @param message This is a pointer to a previously created message,
* which is sent.
* @param sentFrom The sentFrom information can be set to inject the
* sender's queue id into the message. This variable is set to zero by
* default.
* @param ignoreFault If set to true, the internal software fault counter
* is not incremented if queue is full.
*/
// Implement non-generic MessageQueueIF functions not handled by MessageQueueBase
virtual ReturnValue_t sendMessageFrom(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
MessageQueueId_t sentFrom = NO_QUEUE,
bool ignoreFault = false) override;
/**
* @brief The sendToDefault method sends a queue message to the default
* destination.
* @details
* In all other aspects, it works identical to the sendMessage method.
* @param message This is a pointer to a previously created message,
* which is sent.
* @param sentFrom The sentFrom information can be set to inject the
* sender's queue id into the message. This variable is set to zero by
* default.
*/
virtual ReturnValue_t sendToDefaultFrom(MessageQueueMessageIF* message,
MessageQueueId_t sentFrom = NO_QUEUE,
bool ignoreFault = false) override;
/**
* @brief This function reads available messages from the message queue
* and returns the sender.
* @details
* It works identically to the other receiveMessage call, but in addition
* returns the sender's queue id.
* @param message A pointer to a message in which the received data is stored.
* @param receivedFrom A pointer to a queue id in which the sender's id is stored.
*/
ReturnValue_t receiveMessage(MessageQueueMessageIF* message,
MessageQueueId_t* receivedFrom) override;
/**
* @brief This function reads available messages from the message queue.
* @details
* If data is available it is stored in the passed message pointer.
* The message's original content is overwritten and the sendFrom
* information is stored in the lastPartner attribute. Else, the lastPartner
* information remains untouched, the message's content is cleared and the
* function returns immediately.
* @param message A pointer to a message in which the received data is stored.
*/
ReturnValue_t receiveMessage(MessageQueueMessageIF* message) override;
/**
* Deletes all pending messages in the queue.
* @param count The number of flushed messages.
* @return RETURN_OK on success.
*/
ReturnValue_t flush(uint32_t* count) override;
/**
* @brief This method returns the message queue id of the last
* communication partner.
*/
MessageQueueId_t getLastPartner() const override;
/**
* @brief This method returns the message queue id of this class's
* message queue.
*/
MessageQueueId_t getId() const override;
/**
* @brief This method is a simple setter for the default destination.
*/
void setDefaultDestination(MessageQueueId_t defaultDestination) override;
/**
* @brief This method is a simple getter for the default destination.
*/
MessageQueueId_t getDefaultDestination() const override;
bool isDefaultDestinationSet() const override;
ReturnValue_t lockQueue(MutexIF::TimeoutType timeoutType, dur_millis_t lockTimeout);
ReturnValue_t unlockQueue();
@ -213,23 +105,14 @@ class MessageQueue : public MessageQueueIF {
MessageQueueId_t sentFrom = NO_QUEUE,
bool ignoreFault = false);
// static ReturnValue_t handleSendResult(BaseType_t result, bool ignoreFault);
private:
std::queue<std::vector<uint8_t>> messageQueue;
/**
* @brief The class stores the queue id it got assigned.
* If initialization fails, the queue id is set to zero.
*/
MessageQueueId_t mqId = MessageQueueIF::NO_QUEUE;
size_t messageSize = 0;
size_t messageDepth = 0;
MutexIF* queueLock;
bool defaultDestinationSet = false;
MessageQueueId_t defaultDestination = MessageQueueIF::NO_QUEUE;
MessageQueueId_t lastPartner = MessageQueueIF::NO_QUEUE;
};
#endif /* FRAMEWORK_OSAL_HOST_MESSAGEQUEUE_H_ */

View File

@ -102,11 +102,15 @@ void PeriodicTask::taskFunctionality() {
ReturnValue_t PeriodicTask::addComponent(object_id_t object) {
ExecutableObjectIF* newObject = ObjectManager::instance()->get<ExecutableObjectIF>(object);
if (newObject == nullptr) {
return addComponent(newObject);
}
ReturnValue_t PeriodicTask::addComponent(ExecutableObjectIF* object) {
if (object == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
newObject->setTaskIF(this);
objectList.push_back(newObject);
object->setTaskIF(this);
objectList.push_back(object);
return HasReturnvaluesIF::RETURN_OK;
}

View File

@ -59,6 +59,16 @@ class PeriodicTask : public PeriodicTaskIF {
*/
ReturnValue_t addComponent(object_id_t object);
/**
* Adds an object to the list of objects to be executed.
* The objects are executed in the order added.
* @param object pointer to the object to add.
* @return
* -@c RETURN_OK on success
* -@c RETURN_FAILED if the object could not be added.
*/
ReturnValue_t addComponent(ExecutableObjectIF* object);
uint32_t getPeriodMs() const;
ReturnValue_t sleepFor(uint32_t ms);

View File

@ -1,29 +1,29 @@
target_sources(${LIB_FSFW_NAME} PRIVATE
Clock.cpp
BinarySemaphore.cpp
CountingSemaphore.cpp
FixedTimeslotTask.cpp
InternalErrorCodes.cpp
MessageQueue.cpp
Mutex.cpp
MutexFactory.cpp
PeriodicPosixTask.cpp
PosixThread.cpp
QueueFactory.cpp
SemaphoreFactory.cpp
TaskFactory.cpp
tcpipHelpers.cpp
unixUtility.cpp
)
Clock.cpp
BinarySemaphore.cpp
CountingSemaphore.cpp
FixedTimeslotTask.cpp
InternalErrorCodes.cpp
MessageQueue.cpp
Mutex.cpp
MutexFactory.cpp
PeriodicPosixTask.cpp
PosixThread.cpp
QueueFactory.cpp
SemaphoreFactory.cpp
TaskFactory.cpp
tcpipHelpers.cpp
unixUtility.cpp
)
find_package(Threads REQUIRED)
target_link_libraries(${LIB_FSFW_NAME} PRIVATE
${CMAKE_THREAD_LIBS_INIT}
target_link_libraries(${LIB_FSFW_NAME} PUBLIC
${CMAKE_THREAD_LIBS_INIT}
)
if(NOT APPLE)
target_link_libraries(${LIB_FSFW_NAME} PUBLIC
rt
)
target_link_libraries(${LIB_FSFW_NAME} INTERFACE
${CMAKE_THREAD_LIBS_INIT}
)
)
endif()

View File

@ -8,11 +8,9 @@
#include <fstream>
#include "fsfw/ipc/MutexGuard.h"
#include "fsfw/serviceinterface/ServiceInterface.h"
uint16_t Clock::leapSeconds = 0;
MutexIF* Clock::timeMutex = NULL;
uint32_t Clock::getTicksPerSecond(void) {
uint32_t ticks = sysconf(_SC_CLK_TCK);
return ticks;
@ -117,7 +115,13 @@ ReturnValue_t Clock::getDateAndTime(TimeOfDay_t* time) {
// TODO errno
return HasReturnvaluesIF::RETURN_FAILED;
}
ReturnValue_t result = checkOrCreateClockMutex();
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
MutexGuard helper(timeMutex);
// gmtime writes its output in a global buffer which is not Thread Safe
// Therefore we have to use a Mutex here
struct tm* timeInfo;
timeInfo = gmtime(&timeUnix.tv_sec);
time->year = timeInfo->tm_year + 1900;

View File

@ -12,12 +12,9 @@
#include "fsfw/serviceinterface/ServiceInterface.h"
MessageQueue::MessageQueue(uint32_t messageDepth, size_t maxMessageSize, MqArgs* args)
: id(MessageQueueIF::NO_QUEUE),
lastPartner(MessageQueueIF::NO_QUEUE),
defaultDestination(MessageQueueIF::NO_QUEUE),
: MessageQueueBase(MessageQueueIF::NO_QUEUE, MessageQueueIF::NO_QUEUE, args),
maxMessageSize(maxMessageSize) {
mq_attr attributes;
this->id = 0;
// Set attributes
attributes.mq_curmsgs = 0;
attributes.mq_maxmsg = messageDepth;
@ -37,9 +34,6 @@ MessageQueue::MessageQueue(uint32_t messageDepth, size_t maxMessageSize, MqArgs*
// Successful mq_open call
this->id = tempId;
}
if (args != nullptr) {
this->mqArgs = *args;
}
}
MessageQueue::~MessageQueue() {
@ -53,30 +47,6 @@ MessageQueue::~MessageQueue() {
}
}
ReturnValue_t MessageQueue::sendMessage(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
bool ignoreFault) {
return sendMessageFrom(sendTo, message, this->getId(), false);
}
ReturnValue_t MessageQueue::sendToDefault(MessageQueueMessageIF* message) {
return sendToDefaultFrom(message, this->getId());
}
ReturnValue_t MessageQueue::reply(MessageQueueMessageIF* message) {
if (this->lastPartner != 0) {
return sendMessageFrom(this->lastPartner, message, this->getId());
} else {
return NO_REPLY_PARTNER;
}
}
ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message,
MessageQueueId_t* receivedFrom) {
ReturnValue_t status = this->receiveMessage(message);
*receivedFrom = this->lastPartner;
return status;
}
ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message) {
if (message == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
@ -99,7 +69,7 @@ ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message) {
int status = mq_receive(id, reinterpret_cast<char*>(message->getBuffer()),
message->getMaximumMessageSize(), &messagePriority);
if (status > 0) {
this->lastPartner = message->getSender();
this->last = message->getSender();
// Check size of incoming message.
if (message->getMessageSize() < message->getMinimumMessageSize()) {
return HasReturnvaluesIF::RETURN_FAILED;
@ -167,8 +137,6 @@ ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message) {
}
}
MessageQueueId_t MessageQueue::getLastPartner() const { return this->lastPartner; }
ReturnValue_t MessageQueue::flush(uint32_t* count) {
mq_attr attrib;
int status = mq_getattr(id, &attrib);
@ -215,26 +183,11 @@ ReturnValue_t MessageQueue::flush(uint32_t* count) {
return HasReturnvaluesIF::RETURN_OK;
}
MessageQueueId_t MessageQueue::getId() const { return this->id; }
void MessageQueue::setDefaultDestination(MessageQueueId_t defaultDestination) {
this->defaultDestination = defaultDestination;
}
ReturnValue_t MessageQueue::sendToDefaultFrom(MessageQueueMessageIF* message,
MessageQueueId_t sentFrom, bool ignoreFault) {
return sendMessageFrom(defaultDestination, message, sentFrom, ignoreFault);
}
ReturnValue_t MessageQueue::sendMessageFrom(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
MessageQueueId_t sentFrom, bool ignoreFault) {
return sendMessageFromMessageQueue(sendTo, message, sentFrom, ignoreFault);
}
MessageQueueId_t MessageQueue::getDefaultDestination() const { return this->defaultDestination; }
bool MessageQueue::isDefaultDestinationSet() const { return (defaultDestination != NO_QUEUE); }
uint16_t MessageQueue::queueCounter = 0;
ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo,

View File

@ -1,6 +1,7 @@
#ifndef FSFW_OSAL_LINUX_MESSAGEQUEUE_H_
#define FSFW_OSAL_LINUX_MESSAGEQUEUE_H_
#include <fsfw/ipc/MessageQueueBase.h>
#include <mqueue.h>
#include "fsfw/internalerror/InternalErrorReporterIF.h"
@ -26,7 +27,7 @@
* makes use of the operating system calls provided.
* @ingroup message_queue
*/
class MessageQueue : public MessageQueueIF {
class MessageQueue : public MessageQueueBase {
friend class MessageQueueSenderIF;
public:
@ -45,103 +46,22 @@ class MessageQueue : public MessageQueueIF {
MessageQueue(uint32_t messageDepth = 3,
size_t maxMessageSize = MessageQueueMessage::MAX_MESSAGE_SIZE,
MqArgs* args = nullptr);
/** Copying message queues forbidden */
MessageQueue(const MessageQueue&) = delete;
MessageQueue& operator=(const MessageQueue&) = delete;
/**
* @brief The destructor deletes the formerly created message queue.
* @details This is accomplished by using the delete call provided by the operating system.
*/
virtual ~MessageQueue();
/**
* @brief This operation sends a message to the given destination.
* @details It directly uses the sendMessage call of the MessageQueueSender parent, but passes
* its queue id as "sentFrom" parameter.
* @param sendTo This parameter specifies the message queue id of the destination message
* queue.
* @param message A pointer to a previously created message, which is sent.
* @param ignoreFault If set to true, the internal software fault counter is not incremented if
* queue is full.
*/
virtual ReturnValue_t sendMessage(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
bool ignoreFault = false);
/**
* @brief This operation sends a message to the default destination.
* @details As in the sendMessage method, this function uses the sendToDefault call of the
* MessageQueueSender parent class and adds its queue id as "sentFrom"
* information.
* @param message A pointer to a previously created message, which is sent.
*/
virtual ReturnValue_t sendToDefault(MessageQueueMessageIF* message);
/**
* @brief This operation sends a message to the last communication partner.
* @details This operation simplifies answering an incoming message by using the stored
* lastParnter information as destination. If there was no message received yet
* (i.e. lastPartner is zero), an error code is returned.
* @param message A pointer to a previously created message, which is sent.
*/
ReturnValue_t reply(MessageQueueMessageIF* message);
/**
* @brief This function reads available messages from the message queue and returns the
* sender.
* @details It works identically to the other receiveMessage call, but in addition returns the
* sender's queue id.
* @param message A pointer to a message in which the received data is stored.
* @param receivedFrom A pointer to a queue id in which the sender's id is stored.
*/
ReturnValue_t receiveMessage(MessageQueueMessageIF* message, MessageQueueId_t* receivedFrom);
/**
* @brief This function reads available messages from the message queue.
* @details If data is available it is stored in the passed message pointer. The message's
* original content is overwritten and the sendFrom information is stored in
* the lastPartner attribute. Else, the lastPartner information remains untouched, the message's
* content is cleared and the function returns immediately.
* @param message A pointer to a message in which the received data is stored.
*/
ReturnValue_t receiveMessage(MessageQueueMessageIF* message);
/**
* Deletes all pending messages in the queue.
* @param count The number of flushed messages.
* @return RETURN_OK on success.
*/
ReturnValue_t flush(uint32_t* count);
/**
* @brief This method returns the message queue id of the last communication partner.
*/
MessageQueueId_t getLastPartner() const;
/**
* @brief This method returns the message queue id of this class's message queue.
*/
MessageQueueId_t getId() const;
/**
* \brief With the sendMessage call, a queue message is sent to a receiving queue.
* \param sendTo This parameter specifies the message queue id to send the message to.
* \param message This is a pointer to a previously created message, which is sent.
* \param sentFrom The sentFrom information can be set to inject the sender's queue id into the
* message. This variable is set to zero by default. \param ignoreFault If set to true, the
* internal software fault counter is not incremented if queue is full.
*/
virtual ReturnValue_t sendMessageFrom(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
MessageQueueId_t sentFrom, bool ignoreFault = false);
/**
* \brief The sendToDefault method sends a queue message to the default destination.
* \details In all other aspects, it works identical to the sendMessage method.
* \param message This is a pointer to a previously created message, which is sent.
* \param sentFrom The sentFrom information can be set to inject the sender's queue id into the
* message. This variable is set to zero by default.
*/
virtual ReturnValue_t sendToDefaultFrom(MessageQueueMessageIF* message,
MessageQueueId_t sentFrom = NO_QUEUE,
bool ignoreFault = false);
/**
* \brief This method is a simple setter for the default destination.
*/
void setDefaultDestination(MessageQueueId_t defaultDestination);
/**
* \brief This method is a simple getter for the default destination.
*/
MessageQueueId_t getDefaultDestination() const;
bool isDefaultDestinationSet() const;
// Implement non-generic MessageQueueIF functions not handled by MessageQueueBase
ReturnValue_t receiveMessage(MessageQueueMessageIF* message) override;
ReturnValue_t flush(uint32_t* count) override;
ReturnValue_t sendMessageFrom(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
MessageQueueId_t sentFrom, bool ignoreFault = false) override;
protected:
/**
@ -160,33 +80,10 @@ class MessageQueue : public MessageQueueIF {
bool ignoreFault = false);
private:
/**
* @brief The class stores the queue id it got assigned from the operating system in this
* attribute. If initialization fails, the queue id is set to zero.
*/
MessageQueueId_t id;
/**
* @brief In this attribute, the queue id of the last communication partner is stored
* to allow for replying.
*/
MessageQueueId_t lastPartner;
/**
* @brief The message queue's name -a user specific information for the operating system- is
* generated automatically with the help of this static counter.
*/
/**
* \brief This attribute stores a default destination to send messages to.
* \details It is stored to simplify sending to always-the-same receiver. The attribute may
* be set in the constructor or by a setter call to setDefaultDestination.
*/
MessageQueueId_t defaultDestination;
/**
* The name of the message queue, stored for unlinking
*/
char name[16];
MqArgs mqArgs = {};
char name[16] = {};
static uint16_t queueCounter;
const size_t maxMessageSize;

View File

@ -28,7 +28,11 @@ void* PeriodicPosixTask::taskEntryPoint(void* arg) {
ReturnValue_t PeriodicPosixTask::addComponent(object_id_t object) {
ExecutableObjectIF* newObject = ObjectManager::instance()->get<ExecutableObjectIF>(object);
if (newObject == nullptr) {
return addComponent(newObject);
}
ReturnValue_t PeriodicPosixTask::addComponent(ExecutableObjectIF* object) {
if (object == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PeriodicTask::addComponent: Invalid object. Make sure"
<< " it implements ExecutableObjectIF!" << std::endl;
@ -39,8 +43,8 @@ ReturnValue_t PeriodicPosixTask::addComponent(object_id_t object) {
#endif
return HasReturnvaluesIF::RETURN_FAILED;
}
objectList.push_back(newObject);
newObject->setTaskIF(this);
objectList.push_back(object);
object->setTaskIF(this);
return HasReturnvaluesIF::RETURN_OK;
}

View File

@ -42,6 +42,14 @@ class PeriodicPosixTask : public PosixThread, public PeriodicTaskIF {
*/
ReturnValue_t addComponent(object_id_t object) override;
/**
* Adds an object to the list of objects to be executed.
* The objects are executed in the order added.
* @param object pointer to the object to add.
* @return RETURN_OK on success, RETURN_FAILED if the object could not be added.
*/
ReturnValue_t addComponent(ExecutableObjectIF* object) override;
uint32_t getPeriodMs() const override;
ReturnValue_t sleepFor(uint32_t ms) override;

View File

@ -6,9 +6,6 @@
#include "fsfw/ipc/MutexGuard.h"
#include "fsfw/osal/rtems/RtemsBasic.h"
uint16_t Clock::leapSeconds = 0;
MutexIF* Clock::timeMutex = nullptr;
uint32_t Clock::getTicksPerSecond(void) {
rtems_interval ticks_per_second = rtems_clock_get_ticks_per_second();
return static_cast<uint32_t>(ticks_per_second);

View File

@ -7,7 +7,8 @@
#include "fsfw/serviceinterface/ServiceInterface.h"
MessageQueue::MessageQueue(size_t message_depth, size_t max_message_size, MqArgs* args)
: id(0), lastPartner(0), defaultDestination(NO_QUEUE), internalErrorReporter(nullptr) {
: MessageQueueBase(MessageQueueIF::NO_QUEUE, MessageQueueIF::NO_QUEUE, args),
internalErrorReporter(nullptr) {
rtems_name name = ('Q' << 24) + (queueCounter++ << 8);
rtems_status_code status =
rtems_message_queue_create(name, message_depth, max_message_size, 0, &(this->id));
@ -16,43 +17,19 @@ MessageQueue::MessageQueue(size_t message_depth, size_t max_message_size, MqArgs
sif::error << "MessageQueue::MessageQueue: Creating Queue " << std::hex << name << std::dec
<< " failed with status:" << (uint32_t)status << std::endl;
#endif
this->id = 0;
this->id = MessageQueueIF::NO_QUEUE;
}
}
MessageQueue::~MessageQueue() { rtems_message_queue_delete(id); }
ReturnValue_t MessageQueue::sendMessage(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
bool ignoreFault) {
return sendMessageFrom(sendTo, message, this->getId(), ignoreFault);
}
ReturnValue_t MessageQueue::sendToDefault(MessageQueueMessageIF* message) {
return sendToDefaultFrom(message, this->getId());
}
ReturnValue_t MessageQueue::reply(MessageQueueMessageIF* message) {
if (this->lastPartner != 0) {
return sendMessage(this->lastPartner, message, this->getId());
} else {
return NO_REPLY_PARTNER;
}
}
ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message,
MessageQueueId_t* receivedFrom) {
ReturnValue_t status = this->receiveMessage(message);
*receivedFrom = this->lastPartner;
return status;
}
ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message) {
size_t size = 0;
rtems_status_code status =
rtems_message_queue_receive(id, message->getBuffer(), &size, RTEMS_NO_WAIT, 1);
if (status == RTEMS_SUCCESSFUL) {
message->setMessageSize(size);
this->lastPartner = message->getSender();
this->last = message->getSender();
// Check size of incoming message.
if (message->getMessageSize() < message->getMinimumMessageSize()) {
return HasReturnvaluesIF::RETURN_FAILED;
@ -65,19 +42,11 @@ ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message) {
return convertReturnCode(status);
}
MessageQueueId_t MessageQueue::getLastPartner() const { return this->lastPartner; }
ReturnValue_t MessageQueue::flush(uint32_t* count) {
rtems_status_code status = rtems_message_queue_flush(id, count);
return convertReturnCode(status);
}
MessageQueueId_t MessageQueue::getId() const { return this->id; }
void MessageQueue::setDefaultDestination(MessageQueueId_t defaultDestination) {
this->defaultDestination = defaultDestination;
}
ReturnValue_t MessageQueue::sendMessageFrom(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
MessageQueueId_t sentFrom, bool ignoreFault) {
message->setSender(sentFrom);
@ -103,15 +72,6 @@ ReturnValue_t MessageQueue::sendMessageFrom(MessageQueueId_t sendTo, MessageQueu
return returnCode;
}
ReturnValue_t MessageQueue::sendToDefaultFrom(MessageQueueMessageIF* message,
MessageQueueId_t sentFrom, bool ignoreFault) {
return sendMessageFrom(defaultDestination, message, sentFrom, ignoreFault);
}
MessageQueueId_t MessageQueue::getDefaultDestination() const { return this->defaultDestination; }
bool MessageQueue::isDefaultDestinationSet() const { return (defaultDestination != NO_QUEUE); }
ReturnValue_t MessageQueue::convertReturnCode(rtems_status_code inValue) {
switch (inValue) {
case RTEMS_SUCCESSFUL:

View File

@ -1,6 +1,8 @@
#ifndef FSFW_OSAL_RTEMS_MESSAGEQUEUE_H_
#define FSFW_OSAL_RTEMS_MESSAGEQUEUE_H_
#include <fsfw/ipc/MessageQueueBase.h>
#include "RtemsBasic.h"
#include "fsfw/internalerror/InternalErrorReporterIF.h"
#include "fsfw/ipc/MessageQueueIF.h"
@ -20,7 +22,7 @@
*as well as sending and receiving messages, the class makes use of the operating system calls
*provided. \ingroup message_queue
*/
class MessageQueue : public MessageQueueIF {
class MessageQueue : public MessageQueueBase {
public:
/**
* @brief The constructor initializes and configures the message queue.
@ -37,130 +39,24 @@ class MessageQueue : public MessageQueueIF {
MessageQueue(size_t message_depth = 3,
size_t max_message_size = MessageQueueMessage::MAX_MESSAGE_SIZE,
MqArgs* args = nullptr);
/** Copying message queues forbidden */
MessageQueue(const MessageQueue&) = delete;
MessageQueue& operator=(const MessageQueue&) = delete;
/**
* @brief The destructor deletes the formerly created message queue.
* @details This is accomplished by using the delete call provided by the operating system.
*/
virtual ~MessageQueue();
/**
* @brief This operation sends a message to the given destination.
* @details It directly uses the sendMessage call of the MessageQueueSender parent, but passes
* its queue id as "sentFrom" parameter.
* @param sendTo This parameter specifies the message queue id of the destination message
* queue.
* @param message A pointer to a previously created message, which is sent.
* @param ignoreFault If set to true, the internal software fault counter is not incremented if
* queue is full.
*/
ReturnValue_t sendMessage(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
bool ignoreFault = false);
/**
* @brief This operation sends a message to the default destination.
* @details As in the sendMessage method, this function uses the sendToDefault call of the
* MessageQueueSender parent class and adds its queue id as "sentFrom"
* information.
* @param message A pointer to a previously created message, which is sent.
*/
ReturnValue_t sendToDefault(MessageQueueMessageIF* message);
/**
* @brief This operation sends a message to the last communication partner.
* @details This operation simplifies answering an incoming message by using the stored
* lastParnter information as destination. If there was no message received yet
* (i.e. lastPartner is zero), an error code is returned.
* @param message A pointer to a previously created message, which is sent.
*/
ReturnValue_t reply(MessageQueueMessageIF* message);
/**
* @brief This function reads available messages from the message queue and returns the
* sender.
* @details It works identically to the other receiveMessage call, but in addition returns the
* sender's queue id.
* @param message A pointer to a message in which the received data is stored.
* @param receivedFrom A pointer to a queue id in which the sender's id is stored.
*/
ReturnValue_t receiveMessage(MessageQueueMessageIF* message, MessageQueueId_t* receivedFrom);
/**
* @brief This function reads available messages from the message queue.
* @details If data is available it is stored in the passed message pointer. The message's
* original content is overwritten and the sendFrom information is stored in
* the lastPartner attribute. Else, the lastPartner information remains untouched, the message's
* content is cleared and the function returns immediately.
* @param message A pointer to a message in which the received data is stored.
*/
ReturnValue_t receiveMessage(MessageQueueMessageIF* message);
/**
* Deletes all pending messages in the queue.
* @param count The number of flushed messages.
* @return RETURN_OK on success.
*/
ReturnValue_t flush(uint32_t* count);
/**
* @brief This method returns the message queue id of the last communication partner.
*/
MessageQueueId_t getLastPartner() const;
/**
* @brief This method returns the message queue id of this class's message queue.
*/
MessageQueueId_t getId() const;
/**
* \brief With the sendMessage call, a queue message is sent to a receiving queue.
* \details This method takes the message provided, adds the sentFrom information and passes
* it on to the destination provided with an operating system call. The OS's
* return value is returned.
* \param sendTo This parameter specifies the message queue id to send the message to.
* \param message This is a pointer to a previously created message, which is sent.
* \param sentFrom The sentFrom information can be set to inject the sender's queue id into the
* message. This variable is set to zero by default. \param ignoreFault If set to true, the
* internal software fault counter is not incremented if queue is full.
*/
virtual ReturnValue_t sendMessageFrom(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
MessageQueueId_t sentFrom = NO_QUEUE,
bool ignoreFault = false);
/**
* \brief The sendToDefault method sends a queue message to the default destination.
* \details In all other aspects, it works identical to the sendMessage method.
* \param message This is a pointer to a previously created message, which is sent.
* \param sentFrom The sentFrom information can be set to inject the sender's queue id into the
* message. This variable is set to zero by default.
*/
virtual ReturnValue_t sendToDefaultFrom(MessageQueueMessageIF* message,
MessageQueueId_t sentFrom = NO_QUEUE,
bool ignoreFault = false);
/**
* \brief This method is a simple setter for the default destination.
*/
void setDefaultDestination(MessageQueueId_t defaultDestination);
/**
* \brief This method is a simple getter for the default destination.
*/
MessageQueueId_t getDefaultDestination() const;
bool isDefaultDestinationSet() const;
// Implement non-generic MessageQueueIF functions not handled by MessageQueueBase
ReturnValue_t flush(uint32_t* count) override;
ReturnValue_t sendMessageFrom(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
MessageQueueId_t sentFrom = NO_QUEUE,
bool ignoreFault = false) override;
private:
/**
* @brief The class stores the queue id it got assigned from the operating system in this
* attribute. If initialization fails, the queue id is set to zero.
*/
MessageQueueId_t id;
/**
* @brief In this attribute, the queue id of the last communication partner is stored
* to allow for replying.
*/
MessageQueueId_t lastPartner;
/**
* @brief The message queue's name -a user specific information for the operating system- is
* generated automatically with the help of this static counter.
*/
/**
* \brief This attribute stores a default destination to send messages to.
* \details It is stored to simplify sending to always-the-same receiver. The attribute may
* be set in the constructor or by a setter call to setDefaultDestination.
*/
MessageQueueId_t defaultDestination;
/**
* \brief This attribute stores a reference to the internal error reporter for reporting full
* queues. \details In the event of a full destination queue, the reporter will be notified. The

View File

@ -68,11 +68,15 @@ void PeriodicTask::taskFunctionality() {
ReturnValue_t PeriodicTask::addComponent(object_id_t object) {
ExecutableObjectIF* newObject = ObjectManager::instance()->get<ExecutableObjectIF>(object);
if (newObject == nullptr) {
return addComponent(newObject);
}
ReturnValue_t PeriodicTask::addComponent(ExecutableObjectIF* object) {
if (object == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
objectList.push_back(newObject);
newObject->setTaskIF(this);
objectList.push_back(object);
object->setTaskIF(this);
return HasReturnvaluesIF::RETURN_OK;
}

View File

@ -59,6 +59,14 @@ class PeriodicTask : public RTEMSTaskBase, public PeriodicTaskIF {
*/
ReturnValue_t addComponent(object_id_t object) override;
/**
* Adds an object to the list of objects to be executed.
* The objects are executed in the order added.
* @param object pointer to the object to add.
* @return RETURN_OK on success, RETURN_FAILED if the object could not be added.
*/
ReturnValue_t addComponent(ExecutableObjectIF *object) override;
uint32_t getPeriodMs() const override;
ReturnValue_t sleepFor(uint32_t ms) override;

View File

@ -1,7 +1,8 @@
target_sources(${LIB_FSFW_NAME}
PRIVATE
Fuse.cpp
PowerComponent.cpp
PowerSensor.cpp
PowerSwitcher.cpp
target_sources(${LIB_FSFW_NAME} PRIVATE
Fuse.cpp
PowerComponent.cpp
PowerSensor.cpp
PowerSwitcher.cpp
DummyPowerSwitcher.cpp
PowerSwitcherComponent.cpp
)

View File

@ -0,0 +1,46 @@
#include "DummyPowerSwitcher.h"
DummyPowerSwitcher::DummyPowerSwitcher(object_id_t objectId, size_t numberOfSwitches,
size_t numberOfFuses, uint32_t switchDelayMs)
: SystemObject(objectId),
switcherList(numberOfSwitches),
fuseList(numberOfFuses),
switchDelayMs(switchDelayMs) {}
void DummyPowerSwitcher::setInitialSwitcherList(std::vector<ReturnValue_t> switcherList) {
this->switcherList = switcherList;
}
void DummyPowerSwitcher::setInitialFusesList(std::vector<ReturnValue_t> fuseList) {
this->fuseList = fuseList;
}
ReturnValue_t DummyPowerSwitcher::sendSwitchCommand(power::Switch_t switchNr, ReturnValue_t onOff) {
if (switchNr < switcherList.capacity()) {
switcherList[switchNr] = onOff;
}
return RETURN_FAILED;
}
ReturnValue_t DummyPowerSwitcher::sendFuseOnCommand(uint8_t fuseNr) {
if (fuseNr < fuseList.capacity()) {
fuseList[fuseNr] = FUSE_ON;
}
return RETURN_FAILED;
}
ReturnValue_t DummyPowerSwitcher::getSwitchState(power::Switch_t switchNr) const {
if (switchNr < switcherList.capacity()) {
return switcherList[switchNr];
}
return HasReturnvaluesIF::RETURN_FAILED;
}
ReturnValue_t DummyPowerSwitcher::getFuseState(uint8_t fuseNr) const {
if (fuseNr < fuseList.capacity()) {
return fuseList[fuseNr];
}
return HasReturnvaluesIF::RETURN_FAILED;
}
uint32_t DummyPowerSwitcher::getSwitchDelayMs(void) const { return switchDelayMs; }

View File

@ -0,0 +1,31 @@
#ifndef FSFW_SRC_FSFW_POWER_DUMMYPOWERSWITCHER_H_
#define FSFW_SRC_FSFW_POWER_DUMMYPOWERSWITCHER_H_
#include <cstddef>
#include <vector>
#include "PowerSwitchIF.h"
#include "definitions.h"
#include "fsfw/objectmanager/SystemObject.h"
class DummyPowerSwitcher : public SystemObject, public PowerSwitchIF {
public:
DummyPowerSwitcher(object_id_t objectId, size_t numberOfSwitches, size_t numberOfFuses,
uint32_t switchDelayMs = 5000);
void setInitialSwitcherList(std::vector<ReturnValue_t> switcherList);
void setInitialFusesList(std::vector<ReturnValue_t> switcherList);
virtual ReturnValue_t sendSwitchCommand(power::Switch_t switchNr, ReturnValue_t onOff) override;
virtual ReturnValue_t sendFuseOnCommand(uint8_t fuseNr) override;
virtual ReturnValue_t getSwitchState(power::Switch_t switchNr) const override;
virtual ReturnValue_t getFuseState(uint8_t fuseNr) const override;
virtual uint32_t getSwitchDelayMs(void) const override;
private:
std::vector<ReturnValue_t> switcherList;
std::vector<ReturnValue_t> fuseList;
uint32_t switchDelayMs = 5000;
};
#endif /* FSFW_SRC_FSFW_POWER_DUMMYPOWERSWITCHER_H_ */

View File

@ -54,7 +54,7 @@ class Fuse : public SystemObject,
ReturnValue_t check();
uint8_t getFuseId() const;
ReturnValue_t initialize();
ReturnValue_t initialize() override;
DeviceList devices;
ReturnValue_t serialize(uint8_t **buffer, size_t *size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const override;

View File

@ -1,9 +1,9 @@
#ifndef FSFW_POWER_POWERSWITCHIF_H_
#define FSFW_POWER_POWERSWITCHIF_H_
#include "definitions.h"
#include "../events/Event.h"
#include "../returnvalues/HasReturnvaluesIF.h"
#include "definitions.h"
/**
*
* @brief This interface defines a connection to a device that is capable of
@ -38,11 +38,11 @@ class PowerSwitchIF : public HasReturnvaluesIF {
* @param switchNr
* @param onOff on == @c SWITCH_ON; off != @c SWITCH_ON
*/
virtual void sendSwitchCommand(power::Switch_t switchNr, ReturnValue_t onOff) const = 0;
virtual ReturnValue_t sendSwitchCommand(power::Switch_t switchNr, ReturnValue_t onOff) = 0;
/**
* Sends a command to the Power Unit to enable a certain fuse.
*/
virtual void sendFuseOnCommand(uint8_t fuseNr) const = 0;
virtual ReturnValue_t sendFuseOnCommand(uint8_t fuseNr) = 0;
/**
* get the state of the Switches.

View File

@ -1,6 +1,6 @@
#include "definitions.h"
#include "fsfw/power/PowerSwitcher.h"
#include "definitions.h"
#include "fsfw/objectmanager/ObjectManager.h"
#include "fsfw/serviceinterface/ServiceInterface.h"
@ -45,18 +45,30 @@ void PowerSwitcher::commandSwitches(ReturnValue_t onOff) {
return;
}
void PowerSwitcher::turnOn() {
void PowerSwitcher::turnOn(bool checkCurrentState) {
if (checkCurrentState) {
if (getStateOfSwitches() == PowerSwitchIF::SWITCH_ON) {
state = SWITCH_IS_ON;
return;
}
}
commandSwitches(PowerSwitchIF::SWITCH_ON);
state = WAIT_ON;
}
void PowerSwitcher::turnOff() {
void PowerSwitcher::turnOff(bool checkCurrentState) {
if (checkCurrentState) {
if (getStateOfSwitches() == PowerSwitchIF::SWITCH_OFF) {
state = SWITCH_IS_OFF;
return;
}
}
commandSwitches(PowerSwitchIF::SWITCH_OFF);
state = WAIT_OFF;
}
bool PowerSwitcher::active() {
if(state == WAIT_OFF or state == WAIT_ON) {
if (state == WAIT_OFF or state == WAIT_ON) {
return true;
}
return false;

View File

@ -20,8 +20,8 @@ class PowerSwitcher : public HasReturnvaluesIF {
static const ReturnValue_t SWITCH_STATE_MISMATCH = MAKE_RETURN_CODE(2);
PowerSwitcher(PowerSwitchIF* switcher, uint8_t setSwitch1, uint8_t setSwitch2 = power::NO_SWITCH,
State_t setStartState = SWITCH_IS_OFF);
void turnOn();
void turnOff();
void turnOn(bool checkCurrentState = true);
void turnOff(bool checkCurrentState = true);
bool active();
void doStateMachine();
State_t getState();

View File

@ -0,0 +1,107 @@
#include "PowerSwitcherComponent.h"
#include <fsfw/ipc/QueueFactory.h>
#include <fsfw/power/PowerSwitchIF.h>
PowerSwitcherComponent::PowerSwitcherComponent(object_id_t objectId, PowerSwitchIF *pwrSwitcher,
power::Switch_t pwrSwitch)
: SystemObject(objectId),
switcher(pwrSwitcher, pwrSwitch),
modeHelper(this),
healthHelper(this, objectId) {
queue = QueueFactory::instance()->createMessageQueue();
}
ReturnValue_t PowerSwitcherComponent::performOperation(uint8_t opCode) {
ReturnValue_t result;
CommandMessage command;
for (result = queue->receiveMessage(&command); result == RETURN_OK;
result = queue->receiveMessage(&command)) {
result = healthHelper.handleHealthCommand(&command);
if (result == RETURN_OK) {
continue;
}
result = modeHelper.handleModeCommand(&command);
if (result == RETURN_OK) {
continue;
}
}
if (switcher.active()) {
switcher.doStateMachine();
auto currState = switcher.getState();
if (currState == PowerSwitcher::SWITCH_IS_OFF) {
setMode(MODE_OFF, 0);
} else if (currState == PowerSwitcher::SWITCH_IS_ON) {
setMode(MODE_ON, 0);
}
}
return RETURN_OK;
}
ReturnValue_t PowerSwitcherComponent::initialize() {
ReturnValue_t result = modeHelper.initialize();
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
result = healthHelper.initialize();
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
return SystemObject::initialize();
}
MessageQueueId_t PowerSwitcherComponent::getCommandQueue() const { return queue->getId(); }
void PowerSwitcherComponent::getMode(Mode_t *mode, Submode_t *submode) {
*mode = this->mode;
*submode = this->submode;
}
ReturnValue_t PowerSwitcherComponent::setHealth(HealthState health) {
healthHelper.setHealth(health);
return RETURN_OK;
}
ReturnValue_t PowerSwitcherComponent::checkModeCommand(Mode_t mode, Submode_t submode,
uint32_t *msToReachTheMode) {
*msToReachTheMode = 5000;
if (mode != MODE_ON and mode != MODE_OFF) {
return TRANS_NOT_ALLOWED;
}
return RETURN_OK;
}
void PowerSwitcherComponent::startTransition(Mode_t mode, Submode_t submode) {
if (mode == MODE_OFF) {
switcher.turnOff(true);
switcher.doStateMachine();
if (switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) {
setMode(MODE_OFF, 0);
}
} else if (mode == MODE_ON) {
switcher.turnOn(true);
switcher.doStateMachine();
if (switcher.getState() == PowerSwitcher::SWITCH_IS_ON) {
setMode(MODE_ON, 0);
}
}
}
void PowerSwitcherComponent::setToExternalControl() {
healthHelper.setHealth(HasHealthIF::EXTERNAL_CONTROL);
}
void PowerSwitcherComponent::announceMode(bool recursive) {
triggerEvent(MODE_INFO, mode, submode);
}
void PowerSwitcherComponent::setMode(Mode_t newMode, Submode_t newSubmode) {
this->mode = newMode;
this->submode = newSubmode;
modeHelper.modeChanged(mode, submode);
announceMode(false);
}
HasHealthIF::HealthState PowerSwitcherComponent::getHealth() { return healthHelper.getHealth(); }

View File

@ -0,0 +1,62 @@
#ifndef _FSFW_POWER_POWERSWITCHERCOMPONENT_H_
#define _FSFW_POWER_POWERSWITCHERCOMPONENT_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/power/PowerSwitcher.h>
#include <fsfw/power/definitions.h>
#include <fsfw/tasks/ExecutableObjectIF.h>
class PowerSwitchIF;
/**
* @brief Allows to create an power switch object with its own mode and health
* @details
* This basic component allows to create an object which is solely responsible for managing a
* switch. It also has a mode and a health by implementing the respective interface components
* which allows integrating this component into a system mode tree.
*
* Commanding this component to MODE_OFF will cause the switcher to turn the switch off while
* commanding in to MODE_ON will cause the switcher to turn the switch on.
*/
class PowerSwitcherComponent : public SystemObject,
public HasReturnvaluesIF,
public ExecutableObjectIF,
public HasModesIF,
public HasHealthIF {
public:
PowerSwitcherComponent(object_id_t objectId, PowerSwitchIF *pwrSwitcher,
power::Switch_t pwrSwitch);
private:
MessageQueueIF *queue = nullptr;
PowerSwitcher switcher;
Mode_t mode = MODE_OFF;
Submode_t submode = 0;
ModeHelper modeHelper;
HealthHelper healthHelper;
void setMode(Mode_t newMode, Submode_t newSubmode);
virtual ReturnValue_t performOperation(uint8_t opCode) override;
ReturnValue_t initialize() override;
MessageQueueId_t getCommandQueue() const override;
void getMode(Mode_t *mode, Submode_t *submode) override;
ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode,
uint32_t *msToReachTheMode) override;
void startTransition(Mode_t mode, Submode_t submode) override;
void setToExternalControl() override;
void announceMode(bool recursive) override;
ReturnValue_t setHealth(HealthState health) override;
HasHealthIF::HealthState getHealth() override;
};
#endif /* _FSFW_POWER_POWERSWITCHERCOMPONENT_H_ */

View File

@ -8,6 +8,6 @@ namespace power {
using Switch_t = uint8_t;
static constexpr Switch_t NO_SWITCH = 0xFF;
}
} // namespace power
#endif /* FSFW_SRC_FSFW_POWER_DEFINITIONS_H_ */

View File

@ -214,11 +214,11 @@ ReturnValue_t Service3Housekeeping::handleReply(const CommandMessage* reply,
default:
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "Service3Housekeeping::handleReply: Invalid reply with "
<< "reply command " << command << "!" << std::endl;
<< "reply command " << command << std::endl;
#else
sif::printWarning(
"Service3Housekeeping::handleReply: Invalid reply with "
"reply command %hu!\n",
"reply command %hu\n",
command);
#endif
return CommandingServiceBase::INVALID_REPLY;

View File

@ -43,7 +43,7 @@ class Service3Housekeeping : public CommandingServiceBase, public AcceptsHkPacke
CommandMessage* optionalNextCommand, object_id_t objectId,
bool* isStep) override;
virtual MessageQueueId_t getHkQueue() const;
virtual MessageQueueId_t getHkQueue() const override;
private:
enum class Subservice {

View File

@ -30,11 +30,11 @@ ReturnValue_t Subsystem::checkSequence(HybridIterator<ModeListEntry> iter,
return FALLBACK_SEQUENCE_DOES_NOT_EXIST;
}
if (iter.value == NULL) {
if (iter.value == nullptr) {
return NO_TARGET_TABLE;
}
for (; iter.value != NULL; ++iter) {
for (; iter.value != nullptr; ++iter) {
if (!existsModeTable(iter->getTableId())) {
return TABLE_DOES_NOT_EXIST;
} else {
@ -66,13 +66,18 @@ HybridIterator<ModeListEntry> Subsystem::getCurrentTable() {
void Subsystem::performChildOperation() {
if (isInTransition) {
if (commandsOutstanding <= 0) { // all children of the current table were commanded and replied
if (currentSequenceIterator.value == NULL) { // we're through with this sequence
if (currentSequenceIterator.value == nullptr) { // we're through with this sequence
if (checkStateAgainstTable(currentTargetTable, targetSubmode) == RETURN_OK) {
setMode(targetMode, targetSubmode);
isInTransition = false;
return;
} else {
transitionFailed(TARGET_TABLE_NOT_REACHED, getSequence(targetMode)->getTableId());
Mode_t tableId = 0;
auto seq = getSequence(targetMode);
if (seq.value != nullptr) {
tableId = seq->getTableId();
}
transitionFailed(TARGET_TABLE_NOT_REACHED, tableId);
return;
}
}
@ -248,10 +253,13 @@ ReturnValue_t Subsystem::handleCommandMessage(CommandMessage *message) {
case ModeSequenceMessage::READ_TABLE: {
ReturnValue_t result;
Mode_t table = ModeSequenceMessage::getSequenceId(message);
EntryPointer *entry = NULL;
EntryPointer *entry = nullptr;
result = modeTables.find(table, &entry);
if (result != RETURN_OK) {
if (result != RETURN_OK or entry == nullptr) {
replyToCommand(result, 0);
if (entry == nullptr) {
return result;
}
}
SerializeIF *elements[2];
@ -299,6 +307,11 @@ void Subsystem::replyToCommand(ReturnValue_t status, uint32_t parameter) {
}
}
ReturnValue_t Subsystem::addSequence(SequenceEntry sequence) {
return addSequence(sequence.table, sequence.mode, sequence.fallbackMode, sequence.inStore,
sequence.preInit);
}
ReturnValue_t Subsystem::addSequence(ArrayList<ModeListEntry> *sequence, Mode_t id,
Mode_t fallbackSequence, bool inStore, bool preInit) {
ReturnValue_t result;
@ -342,6 +355,10 @@ ReturnValue_t Subsystem::addSequence(ArrayList<ModeListEntry> *sequence, Mode_t
return result;
}
ReturnValue_t Subsystem::addTable(TableEntry table) {
return addTable(table.table, table.mode, table.inStore, table.preInit);
}
ReturnValue_t Subsystem::addTable(ArrayList<ModeListEntry> *table, Mode_t id, bool inStore,
bool preInit) {
ReturnValue_t result;
@ -450,6 +467,7 @@ ReturnValue_t Subsystem::initialize() {
}
mode = initialMode;
submode = initSubmode;
return RETURN_OK;
}
@ -587,7 +605,10 @@ ReturnValue_t Subsystem::checkObjectConnections() {
return RETURN_OK;
}
void Subsystem::setInitialMode(Mode_t mode) { initialMode = mode; }
void Subsystem::setInitialMode(Mode_t mode, Submode_t submode) {
this->initialMode = mode;
this->initSubmode = submode;
}
void Subsystem::cantKeepMode() {
ReturnValue_t result;

View File

@ -1,16 +1,37 @@
#ifndef FSFW_SUBSYSTEM_SUBSYSTEM_H_
#define FSFW_SUBSYSTEM_SUBSYSTEM_H_
#include <FSFWConfig.h>
#include "../container/FixedArrayList.h"
#include "../container/FixedMap.h"
#include "../container/HybridIterator.h"
#include "../container/SinglyLinkedList.h"
#include "../serialize/SerialArrayListAdapter.h"
#include "SubsystemBase.h"
#include "fsfw/FSFW.h"
#include "modes/ModeDefinitions.h"
struct TableSequenceBase {
public:
TableSequenceBase(Mode_t mode, ArrayList<ModeListEntry> *table) : mode(mode), table(table){};
Mode_t mode;
ArrayList<ModeListEntry> *table;
bool inStore = false;
bool preInit = true;
};
struct TableEntry : public TableSequenceBase {
public:
TableEntry(Mode_t mode, ArrayList<ModeListEntry> *table) : TableSequenceBase(mode, table){};
};
struct SequenceEntry : public TableSequenceBase {
public:
SequenceEntry(Mode_t mode, ArrayList<ModeListEntry> *table, Mode_t fallbackMode)
: TableSequenceBase(mode, table), fallbackMode(fallbackMode) {}
Mode_t fallbackMode;
};
/**
* @brief This class extends the SubsystemBase to perform the management of mode tables
* and mode sequences
@ -49,13 +70,15 @@ class Subsystem : public SubsystemBase, public HasModeSequenceIF {
uint32_t maxNumberOfTables);
virtual ~Subsystem();
ReturnValue_t addSequence(SequenceEntry sequence);
ReturnValue_t addSequence(ArrayList<ModeListEntry> *sequence, Mode_t id, Mode_t fallbackSequence,
bool inStore = true, bool preInit = true);
ReturnValue_t addTable(TableEntry table);
ReturnValue_t addTable(ArrayList<ModeListEntry> *table, Mode_t id, bool inStore = true,
bool preInit = true);
void setInitialMode(Mode_t mode);
void setInitialMode(Mode_t mode, Submode_t submode = SUBMODE_NONE);
virtual ReturnValue_t initialize() override;
@ -94,6 +117,7 @@ class Subsystem : public SubsystemBase, public HasModeSequenceIF {
Submode_t targetSubmode;
Mode_t initialMode = 0;
Submode_t initSubmode = SUBMODE_NONE;
HybridIterator<ModeListEntry> currentSequenceIterator;
@ -131,18 +155,18 @@ class Subsystem : public SubsystemBase, public HasModeSequenceIF {
ReturnValue_t deleteTable(Mode_t id);
virtual void performChildOperation();
virtual void performChildOperation() override;
virtual ReturnValue_t handleCommandMessage(CommandMessage *message);
virtual ReturnValue_t handleCommandMessage(CommandMessage *message) override;
bool isFallbackSequence(Mode_t SequenceId);
bool isTableUsed(Mode_t tableId);
virtual ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode,
uint32_t *msToReachTheMode);
uint32_t *msToReachTheMode) override;
virtual void startTransition(Mode_t mode, Submode_t submode);
virtual void startTransition(Mode_t mode, Submode_t submode) override;
void sendSerializablesAsCommandMessage(Command_t command, SerializeIF **elements, uint8_t count);

View File

@ -137,9 +137,9 @@ class SubsystemBase : public SystemObject,
virtual void getMode(Mode_t *mode, Submode_t *submode) override;
virtual void setToExternalControl();
virtual void setToExternalControl() override;
virtual void announceMode(bool recursive);
virtual void announceMode(bool recursive) override;
virtual void modeChanged();
};

View File

@ -26,21 +26,25 @@ class PeriodicTaskIF {
virtual ReturnValue_t startTask() = 0;
/**
* Add a component (object) to a periodic task. The pointer to the
* task can be set optionally
* Add a component (object) to a periodic task.
* @param object
* Add an object to the task. The most important case is to add an
* executable object with a function which will be called regularly
* (see ExecutableObjectIF)
* @param setTaskIF
* Can be used to specify whether the task object pointer is passed
* to the component.
* Add an object to the task. The object needs to implement ExecutableObjectIF
* @return
*/
virtual ReturnValue_t addComponent(object_id_t object) {
return HasReturnvaluesIF::RETURN_FAILED;
};
/**
* Add an object to a periodic task.
* @param object
* Add an object to the task.
* @return
*/
virtual ReturnValue_t addComponent(ExecutableObjectIF* object) {
return HasReturnvaluesIF::RETURN_FAILED;
};
virtual ReturnValue_t sleepFor(uint32_t ms) = 0;
virtual uint32_t getPeriodMs() const = 0;

View File

@ -6,10 +6,6 @@
#include "fsfw/FSFW.h"
CCSDSTime::CCSDSTime() {}
CCSDSTime::~CCSDSTime() {}
ReturnValue_t CCSDSTime::convertToCcsds(Ccs_seconds* to, const Clock::TimeOfDay_t* from) {
ReturnValue_t result = checkTimeOfDay(from);
if (result != RETURN_OK) {
@ -91,7 +87,7 @@ ReturnValue_t CCSDSTime::convertFromCDS(Clock::TimeOfDay_t* to, const uint8_t* f
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
return convertTimevalToTimeOfDay(to, &time);
return Clock::convertTimevalToTimeOfDay(&time, to);
}
ReturnValue_t CCSDSTime::convertFromCCS(Clock::TimeOfDay_t* to, const uint8_t* from,
@ -428,7 +424,7 @@ ReturnValue_t CCSDSTime::convertFromCUC(timeval* to, const uint8_t* from, size_t
from++;
ReturnValue_t result = convertFromCUC(to, pField, from, foundLength, maxLength - 1);
if (result == HasReturnvaluesIF::RETURN_OK) {
if (foundLength != NULL) {
if (foundLength != nullptr) {
*foundLength += 1;
}
}
@ -489,11 +485,6 @@ ReturnValue_t CCSDSTime::checkTimeOfDay(const Clock::TimeOfDay_t* time) {
return RETURN_OK;
}
ReturnValue_t CCSDSTime::convertTimevalToTimeOfDay(Clock::TimeOfDay_t* to, timeval* from) {
// This is rather tricky. Implement only if needed. Also, if so, move to OSAL.
return UNSUPPORTED_TIME_FORMAT;
}
ReturnValue_t CCSDSTime::convertFromCDS(timeval* to, const uint8_t* from, size_t* foundLength,
size_t maxLength) {
uint8_t pField = *from;
@ -583,7 +574,7 @@ ReturnValue_t CCSDSTime::convertFromCDS(Clock::TimeOfDay_t* to, const CCSDSTime:
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
return CCSDSTime::convertTimevalToTimeOfDay(to, &tempTimeval);
return Clock::convertTimevalToTimeOfDay(&tempTimeval, to);
}
ReturnValue_t CCSDSTime::convertFromCUC(timeval* to, uint8_t pField, const uint8_t* from,
@ -593,18 +584,18 @@ ReturnValue_t CCSDSTime::convertFromCUC(timeval* to, uint8_t pField, const uint8
uint8_t nCoarse = ((pField & 0b1100) >> 2) + 1;
uint8_t nFine = (pField & 0b11);
size_t totalLength = nCoarse + nFine;
if (foundLength != NULL) {
if (foundLength != nullptr) {
*foundLength = totalLength;
}
if (totalLength > maxLength) {
return LENGTH_MISMATCH;
}
for (int count = 0; count < nCoarse; count++) {
secs += *from << ((nCoarse * 8 - 8) * (1 + count));
for (int count = nCoarse; count > 0; count--) {
secs += *from << (count * 8 - 8);
from++;
}
for (int count = 0; count < nFine; count++) {
subSeconds += *from << ((nFine * 8 - 8) * (1 + count));
for (int count = nFine; count > 0; count--) {
subSeconds += *from << (count * 8 - 8);
from++;
}
// Move to POSIX epoch.

View File

@ -161,18 +161,37 @@ class CCSDSTime : public HasReturnvaluesIF {
*/
static ReturnValue_t convertFromCcsds(timeval *to, uint8_t const *from, size_t *foundLength,
size_t maxLength);
/**
* @brief Currently unsupported conversion due to leapseconds
*
* @param to Time Of Day (UTC)
* @param from Buffer to take the CUC from
* @param length Length of buffer
* @return ReturnValue_t UNSUPPORTED_TIME_FORMAT in any case ATM
*/
static ReturnValue_t convertFromCUC(Clock::TimeOfDay_t *to, uint8_t const *from, uint8_t length);
/**
* @brief Converts from CCSDS CUC to timeval
*
* If input is CCSDS Epoch this is TAI! -> No leapsecond support.
*
* Currently, it only supports seconds + 2 Byte Subseconds (1/65536 seconds)
*
*
* @param to Timeval to write the result to
* @param from Buffer to read from
* @param foundLength Length found by this function (can be nullptr if unused)
* @param maxLength Max length of the buffer to be read
* @return ReturnValue_t - RETURN_OK if successful
* - LENGTH_MISMATCH if expected length is larger than maxLength
*/
static ReturnValue_t convertFromCUC(timeval *to, uint8_t const *from, size_t *foundLength,
size_t maxLength);
static ReturnValue_t convertFromCUC(timeval *to, uint8_t pField, uint8_t const *from,
size_t *foundLength, size_t maxLength);
static ReturnValue_t convertFromCCS(timeval *to, uint8_t const *from, size_t *foundLength,
size_t maxLength);
static ReturnValue_t convertFromCCS(timeval *to, uint8_t pField, uint8_t const *from,
size_t *foundLength, size_t maxLength);
@ -192,8 +211,8 @@ class CCSDSTime : public HasReturnvaluesIF {
static uint32_t subsecondsToMicroseconds(uint16_t subseconds);
private:
CCSDSTime();
virtual ~CCSDSTime();
CCSDSTime(){};
virtual ~CCSDSTime(){};
/**
* checks a ccs time stream for validity
*
@ -223,7 +242,6 @@ class CCSDSTime : public HasReturnvaluesIF {
uint8_t *day);
static bool isLeapYear(uint32_t year);
static ReturnValue_t convertTimevalToTimeOfDay(Clock::TimeOfDay_t *to, timeval *from);
};
#endif /* FSFW_TIMEMANAGER_CCSDSTIME_H_ */

View File

@ -173,6 +173,7 @@ class Clock {
static MutexIF *timeMutex;
static uint16_t leapSeconds;
static bool leapSecondsSet;
};
#endif /* FSFW_TIMEMANAGER_CLOCK_H_ */

View File

@ -3,6 +3,10 @@
#include "fsfw/ipc/MutexGuard.h"
#include "fsfw/timemanager/Clock.h"
uint16_t Clock::leapSeconds = 0;
MutexIF* Clock::timeMutex = nullptr;
bool Clock::leapSecondsSet = false;
ReturnValue_t Clock::convertUTCToTT(timeval utc, timeval* tt) {
uint16_t leapSeconds;
ReturnValue_t result = getLeapSeconds(&leapSeconds);
@ -29,12 +33,16 @@ ReturnValue_t Clock::setLeapSeconds(const uint16_t leapSeconds_) {
MutexGuard helper(timeMutex);
leapSeconds = leapSeconds_;
leapSecondsSet = true;
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t Clock::getLeapSeconds(uint16_t* leapSeconds_) {
if (timeMutex == nullptr) {
if (not leapSecondsSet) {
return HasReturnvaluesIF::RETURN_FAILED;
}
if (checkOrCreateClockMutex() != HasReturnvaluesIF::RETURN_OK) {
return HasReturnvaluesIF::RETURN_FAILED;
}
MutexGuard helper(timeMutex);
@ -46,6 +54,16 @@ ReturnValue_t Clock::getLeapSeconds(uint16_t* leapSeconds_) {
ReturnValue_t Clock::convertTimevalToTimeOfDay(const timeval* from, TimeOfDay_t* to) {
struct tm* timeInfo;
// According to https://en.cppreference.com/w/c/chrono/gmtime, the implementation of gmtime_s
// in the Windows CRT is incompatible with the C standard but this should not be an issue for
// this implementation
ReturnValue_t result = checkOrCreateClockMutex();
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
MutexGuard helper(timeMutex);
// gmtime writes its output in a global buffer which is not Thread Safe
// Therefore we have to use a Mutex here
timeInfo = gmtime(&from->tv_sec);
to->year = timeInfo->tm_year + 1900;
to->month = timeInfo->tm_mon + 1;

View File

@ -1,7 +1,12 @@
#include "fsfw/timemanager/Countdown.h"
Countdown::Countdown(uint32_t initialTimeout) : timeout(initialTimeout) {
setTimeout(initialTimeout);
Countdown::Countdown(uint32_t initialTimeout, bool startImmediately) : timeout(initialTimeout) {
if (startImmediately) {
setTimeout(initialTimeout);
}
else {
timeout = initialTimeout;
}
}
Countdown::~Countdown() {}

View File

@ -26,8 +26,9 @@ class Countdown {
* Otherwise a call to hasTimedOut might return True.
*
* @param initialTimeout Countdown duration in milliseconds
* @param startImmediately Set to false if countdown should not be started immediately
*/
Countdown(uint32_t initialTimeout = 0);
Countdown(uint32_t initialTimeout = 0, bool startImmediately = true);
~Countdown();
/**
* Call to set a new countdown duration.

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