Compare commits
142 Commits
meier/deve
...
bcd19045cc
Author | SHA1 | Date | |
---|---|---|---|
bcd19045cc | |||
dba08fed7a | |||
4841d5d92d | |||
ac78a79ca2 | |||
bf7388c059 | |||
c2de911efa | |||
fc2b709148 | |||
02473a0cd7 | |||
ab45aa1296 | |||
a83b86ccd2 | |||
c561271070 | |||
b99160e850 | |||
d11f898f70 | |||
c0ff84bb9d | |||
d1ff32bf96 | |||
dafcaa6007 | |||
5eb52133ac | |||
025e7647d3 | |||
0a97077a0e | |||
ab2d7ca98f | |||
56e4fca06f | |||
e06c457743 | |||
5941c21adf | |||
0e880de0d0 | |||
29c3a43760 | |||
e05c72b062 | |||
2ca8523215 | |||
25775614de | |||
0410ecd9e3 | |||
0fe1b70bae | |||
c5b4499d98 | |||
4499c9bf04 | |||
eb0223bc51 | |||
d45cda93b2 | |||
3448292e8a | |||
c83cc492c0 | |||
ece32f88f4 | |||
dd9e28fca1 | |||
46cfe65321 | |||
7b7f5d7e0a | |||
fd112ed597 | |||
96eb8fc21f | |||
88fa4f1d9d | |||
75132c1e39 | |||
281f91ec5d | |||
15352b539d | |||
118f1da8dd | |||
8b0508d50a | |||
8a40878eb5 | |||
83de5b4ec1 | |||
fe1c51ae6d | |||
10cc954d27 | |||
73ff9b97db | |||
b0d71597f0 | |||
226f28dc7b | |||
c78b7c432b | |||
6bfdace512 | |||
16e55a98ce | |||
79f17843d8 | |||
e5e163bdbf | |||
4e4820af05 | |||
637512ad77 | |||
a4bd5a2aaa | |||
a943e4eebb | |||
cb0c80d8dc | |||
d72b212fa6 | |||
43f0841d0a | |||
9e5fb64d0e | |||
71f704c980 | |||
a72cc487df | |||
de2d4da161 | |||
19bd26d998 | |||
f59b05c86c | |||
80cb0e682f | |||
8ee26f81f9 | |||
3556eca8e8 | |||
a9041b84a3 | |||
83d9dbc052 | |||
2220120d54 | |||
15eb22f9ee | |||
3332f68ce7 | |||
7f6c8b8b12 | |||
54feb77770 | |||
1a07864a5f | |||
3e9d6bdbb9 | |||
c295539c79 | |||
cddf16f941 | |||
a3dee05fe3 | |||
8edf4c3c8d | |||
7801c6effe | |||
8cc94a55ab | |||
daffb6b666 | |||
789668ae50 | |||
7cfb1e6076 | |||
cc36baff78 | |||
4c65109ac0 | |||
861bd15eda | |||
7b979eadff | |||
16714ceb40 | |||
fea301bcc9 | |||
77450eb4b7 | |||
28015c4735 | |||
7d61e67d20 | |||
afcbc8be0a | |||
7a2269262b | |||
9731dc1e61 | |||
43aad11859 | |||
bf2e0f2d73 | |||
e98857fab4 | |||
41682aab3f | |||
8642b13fd1 | |||
6aa72892ed | |||
70f0a72f1b | |||
b5d890eedd | |||
50b1b48678 | |||
0e0dbc74aa | |||
8c34051d8b | |||
b00d83cb1a | |||
17e609c3a5 | |||
64f0166b64 | |||
c80f06fbcb | |||
70eb8325a0 | |||
2c8531ea48 | |||
900ef5b912 | |||
b94685e045 | |||
572d602b72 | |||
88051c9302 | |||
80be937d9d | |||
07f5dbb9ac | |||
afce942bf8 | |||
a1d7a56dfa | |||
cb78fefbb3 | |||
4ed9cc933f | |||
9947a648df | |||
b764194ed0 | |||
2d0e4ba951 | |||
0d549b687d | |||
738f572043 | |||
cab508fd64 | |||
c20be13733 | |||
a3930dafc5 | |||
4f9797af3b |
83
CHANGELOG.md
83
CHANGELOG.md
@ -12,6 +12,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## Changes
|
||||
|
||||
- Bump C++ required version to C++17. Every project which uses the FSFW and every modern
|
||||
compiler supports it
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/622
|
||||
- HAL Linux SPI: Set the Clock Default State when setting new SPI speed
|
||||
and mode
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/573
|
||||
@ -22,8 +25,6 @@ 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
|
||||
@ -42,12 +43,39 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
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)
|
||||
|
||||
### HAL
|
||||
|
||||
- HAL Linux Uart: Baudrate and bits per word are enums now, avoiding misconfigurations
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/585
|
||||
- HAL Linux SPI: Set the Clock Default State when setting new SPI speed
|
||||
and mode
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/573
|
||||
- GPIO HAL: `Direction`, `GpioOperation` and `Levels` are enum classes now, which prevents
|
||||
name clashes with Windows defines.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/572
|
||||
- HAL Linux Uart: Baudrate and bits per word are enums now, avoiding misconfigurations
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/585
|
||||
|
||||
### Time
|
||||
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/584 and
|
||||
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/593
|
||||
|
||||
- `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)
|
||||
|
||||
### Power
|
||||
|
||||
- `PowerSwitchIF`: Remove `const` specifier from `sendSwitchCommand` and `sendFuseOnCommand` and
|
||||
also specify a `ReturnValue_t` return type
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/590
|
||||
- Extend `PowerSwitcher` module to optionally check current state when calling `turnOn` or
|
||||
`turnOff`. Tis can be helpful to avoid commanding switches which do not need commanding
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/590
|
||||
|
||||
## Removed
|
||||
|
||||
@ -57,15 +85,52 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## Additions
|
||||
|
||||
- Linux HAL: Add wiretapping option for I2C. Enabled with `FSFW_HAL_I2C_WIRETAPPING` defined to 1
|
||||
- LTO support: Allow using LTO/IPO by setting `FSFW_ENABLE_LTO=1`. CMake is able to detect whether
|
||||
the user compiler supports IPO/LPO. LTO is on by default now. Most modern compilers support it,
|
||||
can make good use of it and it usually makes the code faster and/or smaller.
|
||||
After some more research:
|
||||
Enabling LTO will actually cause the compiler to only produce thin LTO by adding
|
||||
`-flto -fno-fat-lto-objects` to the compiler options. I am not sure this is an ideal choice
|
||||
because if an application linking against the FSFW does not use LTO, there can be compile
|
||||
issues (e.g. observed when compiling the FSFW tests without LTO). This is a known issue as
|
||||
can be seen in the multiple CMake issues for it:
|
||||
- https://gitlab.kitware.com/cmake/cmake/-/issues/22913,
|
||||
- https://gitlab.kitware.com/cmake/cmake/-/issues/16808,
|
||||
- https://gitlab.kitware.com/cmake/cmake/-/issues/21696
|
||||
Easiest solution for now: Keep this option OFF by default.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/616
|
||||
- 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
|
||||
- Add a `DummyPowerSwitcher` module which can be useful for test setups when no PCDU is available
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/590
|
||||
- New typedef for switcher type
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/590
|
||||
- `Subsystem`: New API to add table and sequence entries
|
||||
|
||||
## HAL
|
||||
|
||||
- SPI: Cache the SPI device in the communication interface. Architecturally, this makes a
|
||||
lot more sense because each ComIF should be responsible for one SPI bus.
|
||||
- SPI: Move the empty transfer to update the line polarity to separate function. This means
|
||||
it is not automatically called when calling the setter function for SPI speed and mode.
|
||||
The user should call this function after locking the CS mutex if multiple SPI devices with
|
||||
differing speeds and modes are attached to one bus.
|
||||
- SPI: Getter functions for SPI speed and mode.
|
||||
- I2C: Add wiretapping option for I2C. Enabled with `FSFW_HAL_I2C_WIRETAPPING` defined to 1.
|
||||
|
||||
## Fixed
|
||||
|
||||
- TCP TMTC Server: `MutexGuard` was not created properly in
|
||||
`TcpTmTcServer::handleTmSending(socket_t connSocket, bool& tmSent)` call.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/618
|
||||
- Fix infinite recursion in `prepareHealthSetReply` of PUS Health Service 201.
|
||||
Is not currently used right now but might be used in the future
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/617
|
||||
- Move some CMake directives further up top so they are not ignored
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/621
|
||||
- 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
|
||||
|
@ -1,6 +1,14 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
set(LIB_FSFW_NAME fsfw)
|
||||
project(${LIB_FSFW_NAME})
|
||||
|
||||
if(NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
elseif(${CMAKE_CXX_STANDARD} LESS 17)
|
||||
message(FATAL_ERROR "Compiling the FSFW requires a minimum of C++17 support")
|
||||
endif()
|
||||
|
||||
set(FSFW_VERSION_IF_GIT_FAILS 4)
|
||||
set(FSFW_SUBVERSION_IF_GIT_FAILS 0)
|
||||
set(FSFW_REVISION_IF_GIT_FAILS 0)
|
||||
@ -10,33 +18,44 @@ 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_NAME etl)
|
||||
set(FSFW_ETL_LIB_MAJOR_VERSION 20 CACHE STRING
|
||||
"ETL library major version requirement"
|
||||
"ETL library major version requirement"
|
||||
)
|
||||
set(FSFW_ETL_LIB_VERSION ${FSFW_ETL_LIB_MAJOR_VERSION}.27.3 CACHE STRING
|
||||
"ETL library exact version requirement"
|
||||
"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-preview4 CACHE STRING
|
||||
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)
|
||||
# Keep this off by default for now. See PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/616
|
||||
# for information which keeping this on by default is problematic
|
||||
option(FSFW_ENABLE_IPO "Enable interprocedural optimization or link-time optimization if available" OFF)
|
||||
if(FSFW_ENABLE_IPO)
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT IPO_SUPPORTED OUTPUT IPO_ERROR)
|
||||
if(NOT IPO_SUPPORTED)
|
||||
message(STATUS "FSFW | IPO/LTO not supported: ${IPO_ERROR}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(FSFW_GENERATE_SECTIONS
|
||||
"Generate function and data sections. Required to remove unused code" ON
|
||||
"Generate function and data sections. Required to remove unused code" ON
|
||||
)
|
||||
if(FSFW_GENERATE_SECTIONS)
|
||||
option(FSFW_REMOVE_UNUSED_CODE "Remove unused code" ON)
|
||||
option(FSFW_REMOVE_UNUSED_CODE "Remove unused code" ON)
|
||||
endif()
|
||||
|
||||
option(FSFW_BUILD_UNITTESTS "Build unittest binary in addition to static library" OFF)
|
||||
option(FSFW_BUILD_DOCS "Build documentation with Sphinx and Doxygen" OFF)
|
||||
if(FSFW_BUILD_UNITTESTS)
|
||||
option(FSFW_TESTS_GEN_COV "Generate coverage data for unittests" ON)
|
||||
option(FSFW_TESTS_GEN_COV "Generate coverage data for unittests" ON)
|
||||
endif()
|
||||
|
||||
option(FSFW_WARNING_SHADOW_LOCAL_GCC "Enable -Wshadow=local warning in GCC" ON)
|
||||
@ -57,12 +76,16 @@ option(FSFW_ADD_TMSTORAGE "Compile with tm storage components" OFF)
|
||||
# Contrib sources
|
||||
option(FSFW_ADD_SGP4_PROPAGATOR "Add SGP4 propagator code" OFF)
|
||||
|
||||
set(LIB_FSFW_NAME fsfw)
|
||||
|
||||
set(FSFW_TEST_TGT fsfw-tests)
|
||||
set(FSFW_DUMMY_TGT fsfw-dummy)
|
||||
|
||||
add_library(${LIB_FSFW_NAME})
|
||||
|
||||
if(IPO_SUPPORTED AND FSFW_ENABLE_IPO)
|
||||
set_property(TARGET ${LIB_FSFW_NAME} PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
endif()
|
||||
|
||||
set(FSFW_GIT_VER_HANDLING_OK FALSE)
|
||||
# Version handling
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git)
|
||||
@ -110,9 +133,7 @@ if(FSFW_BUILD_UNITTESTS)
|
||||
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)
|
||||
@ -121,6 +142,9 @@ if(FSFW_BUILD_UNITTESTS)
|
||||
|
||||
project(${FSFW_TEST_TGT} CXX C)
|
||||
add_executable(${FSFW_TEST_TGT})
|
||||
if(IPO_SUPPORTED AND FSFW_ENABLE_IPO)
|
||||
set_property(TARGET ${FSFW_TEST_TGT} PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
endif()
|
||||
|
||||
if(FSFW_TESTS_GEN_COV)
|
||||
message(STATUS "${MSG_PREFIX} Generating coverage data for the library")
|
||||
@ -135,23 +159,36 @@ 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)
|
||||
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 FetchContent"
|
||||
)
|
||||
include(FetchContent)
|
||||
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()
|
||||
|
||||
FetchContent_Declare(
|
||||
${FSFW_ETL_LIB_NAME}
|
||||
GIT_REPOSITORY https://github.com/ETLCPP/etl
|
||||
GIT_TAG ${FSFW_ETL_LIB_VERSION}
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(${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")
|
||||
|
||||
@ -165,13 +202,6 @@ target_include_directories(${LIB_FSFW_NAME} INTERFACE
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
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 "${MSG_PREFIX} Compiling the FSFW requires a minimum of C++11 support")
|
||||
endif()
|
||||
|
||||
# Backwards comptability
|
||||
if(OS_FSFW AND NOT FSFW_OSAL)
|
||||
message(WARNING "${MSG_PREFIX} Please pass the FSFW OSAL as FSFW_OSAL instead of OS_FSFW")
|
||||
@ -414,7 +444,7 @@ target_compile_options(${LIB_FSFW_NAME} PRIVATE
|
||||
)
|
||||
|
||||
target_link_libraries(${LIB_FSFW_NAME} PRIVATE
|
||||
etl
|
||||
${FSFW_ETL_LINK_TARGET}
|
||||
${FSFW_ADDITIONAL_LINK_LIBS}
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
|
2
automation/Jenkinsfile
vendored
2
automation/Jenkinsfile
vendored
@ -3,7 +3,7 @@ pipeline {
|
||||
BUILDDIR = 'build-tests'
|
||||
}
|
||||
agent {
|
||||
docker { image 'fsfw-ci:d1'}
|
||||
docker { image 'fsfw-ci:d2'}
|
||||
}
|
||||
stages {
|
||||
stage('Clean') {
|
||||
|
@ -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_ */
|
||||
|
@ -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 };
|
||||
|
||||
|
@ -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)
|
||||
|
@ -32,6 +32,8 @@ ReturnValue_t CommandExecutor::execute() {
|
||||
} else if (state == States::PENDING) {
|
||||
return COMMAND_PENDING;
|
||||
}
|
||||
// Reset data in read vector
|
||||
std::memset(readVec.data(), 0, readVec.size());
|
||||
currentCmdFile = popen(currentCmd.c_str(), "r");
|
||||
if (currentCmdFile == nullptr) {
|
||||
lastError = errno;
|
||||
@ -205,3 +207,5 @@ ReturnValue_t CommandExecutor::executeBlocking() {
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
const std::vector<char>& CommandExecutor::getReadVector() const { return readVec; }
|
||||
|
@ -109,6 +109,8 @@ class CommandExecutor {
|
||||
*/
|
||||
void reset();
|
||||
|
||||
const std::vector<char>& getReadVector() const;
|
||||
|
||||
private:
|
||||
std::string currentCmd;
|
||||
bool blocking = true;
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "fsfw/FSFW.h"
|
||||
#include "fsfw/serviceinterface.h"
|
||||
|
||||
UnixFileGuard::UnixFileGuard(std::string device, int* fileDescriptor, int flags,
|
||||
UnixFileGuard::UnixFileGuard(const std::string& device, int* fileDescriptor, int flags,
|
||||
std::string diagnosticPrefix)
|
||||
: fileDescriptor(fileDescriptor) {
|
||||
if (fileDescriptor == nullptr) {
|
||||
|
@ -15,7 +15,7 @@ class UnixFileGuard {
|
||||
|
||||
static constexpr ReturnValue_t OPEN_FILE_FAILED = 1;
|
||||
|
||||
UnixFileGuard(std::string device, int* fileDescriptor, int flags,
|
||||
UnixFileGuard(const std::string& device, int* fileDescriptor, int flags,
|
||||
std::string diagnosticPrefix = "");
|
||||
|
||||
virtual ~UnixFileGuard();
|
||||
|
@ -280,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
|
||||
@ -299,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) {
|
||||
|
@ -31,14 +31,16 @@ class LinuxLibgpioIF : public GpioIF, public SystemObject {
|
||||
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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
42
hal/src/fsfw_hal/linux/spi/ManualCsLockGuard.h
Normal file
42
hal/src/fsfw_hal/linux/spi/ManualCsLockGuard.h
Normal file
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "fsfw/ipc/MutexIF.h"
|
||||
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
|
||||
#include "fsfw_hal/common/gpio/GpioIF.h"
|
||||
|
||||
class ManualCsLockWrapper : public HasReturnvaluesIF {
|
||||
public:
|
||||
ManualCsLockWrapper(MutexIF* lock, GpioIF* gpioIF, SpiCookie* cookie,
|
||||
MutexIF::TimeoutType type = MutexIF::TimeoutType::BLOCKING,
|
||||
uint32_t timeoutMs = 0)
|
||||
: lock(lock), gpioIF(gpioIF), cookie(cookie), type(type), timeoutMs(timeoutMs) {
|
||||
if (cookie == nullptr) {
|
||||
// TODO: Error? Or maybe throw exception..
|
||||
return;
|
||||
}
|
||||
cookie->setCsLockManual(true);
|
||||
lockResult = lock->lockMutex(type, timeoutMs);
|
||||
if (lockResult != RETURN_OK) {
|
||||
return;
|
||||
}
|
||||
gpioResult = gpioIF->pullLow(cookie->getChipSelectPin());
|
||||
}
|
||||
|
||||
~ManualCsLockWrapper() {
|
||||
if (lockResult == RETURN_OK) {
|
||||
lock->unlockMutex();
|
||||
}
|
||||
if (gpioResult == RETURN_OK) {
|
||||
gpioIF->pullHigh(cookie->getChipSelectPin());
|
||||
}
|
||||
}
|
||||
ReturnValue_t lockResult;
|
||||
ReturnValue_t gpioResult;
|
||||
|
||||
private:
|
||||
MutexIF* lock;
|
||||
GpioIF* gpioIF;
|
||||
SpiCookie* cookie;
|
||||
MutexIF::TimeoutType type;
|
||||
uint32_t timeoutMs = 0;
|
||||
};
|
@ -15,8 +15,8 @@
|
||||
#include "fsfw_hal/linux/spi/SpiCookie.h"
|
||||
#include "fsfw_hal/linux/utility.h"
|
||||
|
||||
SpiComIF::SpiComIF(object_id_t objectId, GpioIF* gpioComIF)
|
||||
: SystemObject(objectId), gpioComIF(gpioComIF) {
|
||||
SpiComIF::SpiComIF(object_id_t objectId, std::string devname, GpioIF* gpioComIF)
|
||||
: SystemObject(objectId), gpioComIF(gpioComIF), dev(std::move(devname)) {
|
||||
if (gpioComIF == nullptr) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
@ -27,7 +27,7 @@ SpiComIF::SpiComIF(object_id_t objectId, GpioIF* gpioComIF)
|
||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||
}
|
||||
|
||||
spiMutex = MutexFactory::instance()->createMutex();
|
||||
csMutex = MutexFactory::instance()->createMutex();
|
||||
}
|
||||
|
||||
ReturnValue_t SpiComIF::initializeInterface(CookieIF* cookie) {
|
||||
@ -85,8 +85,7 @@ ReturnValue_t SpiComIF::initializeInterface(CookieIF* cookie) {
|
||||
spiCookie->getSpiParameters(spiMode, spiSpeed, ¶ms);
|
||||
|
||||
int fileDescriptor = 0;
|
||||
UnixFileGuard fileHelper(spiCookie->getSpiDevice(), &fileDescriptor, O_RDWR,
|
||||
"SpiComIF::initializeInterface");
|
||||
UnixFileGuard fileHelper(dev, &fileDescriptor, O_RDWR, "SpiComIF::initializeInterface");
|
||||
if (fileHelper.getOpenResult() != HasReturnvaluesIF::RETURN_OK) {
|
||||
return fileHelper.getOpenResult();
|
||||
}
|
||||
@ -182,8 +181,7 @@ ReturnValue_t SpiComIF::performRegularSendOperation(SpiCookie* spiCookie, const
|
||||
int retval = 0;
|
||||
/* Prepare transfer */
|
||||
int fileDescriptor = 0;
|
||||
std::string device = spiCookie->getSpiDevice();
|
||||
UnixFileGuard fileHelper(device, &fileDescriptor, O_RDWR, "SpiComIF::sendMessage");
|
||||
UnixFileGuard fileHelper(dev, &fileDescriptor, O_RDWR, "SpiComIF::sendMessage");
|
||||
if (fileHelper.getOpenResult() != HasReturnvaluesIF::RETURN_OK) {
|
||||
return OPENING_FILE_FAILED;
|
||||
}
|
||||
@ -196,20 +194,27 @@ ReturnValue_t SpiComIF::performRegularSendOperation(SpiCookie* spiCookie, const
|
||||
|
||||
bool fullDuplex = spiCookie->isFullDuplex();
|
||||
gpioId_t gpioId = spiCookie->getChipSelectPin();
|
||||
bool csLockManual = spiCookie->getCsLockManual();
|
||||
|
||||
/* Pull SPI CS low. For now, no support for active high given */
|
||||
if (gpioId != gpio::NO_GPIO) {
|
||||
result = spiMutex->lockMutex(timeoutType, timeoutMs);
|
||||
MutexIF::TimeoutType csType;
|
||||
dur_millis_t csTimeout = 0;
|
||||
// Pull SPI CS low. For now, no support for active high given
|
||||
if (gpioId != gpio::NO_GPIO and not csLockManual) {
|
||||
spiCookie->getMutexParams(csType, csTimeout);
|
||||
result = csMutex->lockMutex(csType, csTimeout);
|
||||
if (result != RETURN_OK) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "SpiComIF::sendMessage: Failed to lock mutex" << std::endl;
|
||||
sif::error << "SpiComIF::sendMessage: Failed to lock mutex with code "
|
||||
<< "0x" << std::hex << std::setfill('0') << std::setw(4) << result << std::dec
|
||||
<< std::endl;
|
||||
#else
|
||||
sif::printError("SpiComIF::sendMessage: Failed to lock mutex\n");
|
||||
sif::printError("SpiComIF::sendMessage: Failed to lock mutex with code %d\n", result);
|
||||
#endif
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
updateLinePolarity(fileDescriptor);
|
||||
ReturnValue_t result = gpioComIF->pullLow(gpioId);
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
@ -221,6 +226,8 @@ ReturnValue_t SpiComIF::performRegularSendOperation(SpiCookie* spiCookie, const
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
updateLinePolarity(fileDescriptor);
|
||||
}
|
||||
|
||||
/* Execute transfer */
|
||||
@ -248,9 +255,9 @@ ReturnValue_t SpiComIF::performRegularSendOperation(SpiCookie* spiCookie, const
|
||||
}
|
||||
}
|
||||
|
||||
if (gpioId != gpio::NO_GPIO) {
|
||||
if (gpioId != gpio::NO_GPIO and not csLockManual) {
|
||||
gpioComIF->pullHigh(gpioId);
|
||||
result = spiMutex->unlockMutex();
|
||||
result = csMutex->unlockMutex();
|
||||
if (result != RETURN_OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "SpiComIF::sendMessage: Failed to unlock mutex" << std::endl;
|
||||
@ -278,9 +285,8 @@ ReturnValue_t SpiComIF::requestReceiveMessage(CookieIF* cookie, size_t requestLe
|
||||
|
||||
ReturnValue_t SpiComIF::performHalfDuplexReception(SpiCookie* spiCookie) {
|
||||
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
|
||||
std::string device = spiCookie->getSpiDevice();
|
||||
int fileDescriptor = 0;
|
||||
UnixFileGuard fileHelper(device, &fileDescriptor, O_RDWR, "SpiComIF::requestReceiveMessage");
|
||||
UnixFileGuard fileHelper(dev, &fileDescriptor, O_RDWR, "SpiComIF::requestReceiveMessage");
|
||||
if (fileHelper.getOpenResult() != HasReturnvaluesIF::RETURN_OK) {
|
||||
return OPENING_FILE_FAILED;
|
||||
}
|
||||
@ -292,12 +298,22 @@ ReturnValue_t SpiComIF::performHalfDuplexReception(SpiCookie* spiCookie) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool csLockManual = spiCookie->getCsLockManual();
|
||||
gpioId_t gpioId = spiCookie->getChipSelectPin();
|
||||
if (gpioId != gpio::NO_GPIO) {
|
||||
result = spiMutex->lockMutex(timeoutType, timeoutMs);
|
||||
MutexIF::TimeoutType csType;
|
||||
dur_millis_t csTimeout = 0;
|
||||
if (gpioId != gpio::NO_GPIO and not csLockManual) {
|
||||
spiCookie->getMutexParams(csType, csTimeout);
|
||||
result = csMutex->lockMutex(csType, csTimeout);
|
||||
if (result != RETURN_OK) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "SpiComIF::getSendSuccess: Failed to lock mutex" << std::endl;
|
||||
sif::error << "SpiComIF::sendMessage: Failed to lock mutex with code "
|
||||
<< "0x" << std::hex << std::setfill('0') << std::setw(4) << result << std::dec
|
||||
<< std::endl;
|
||||
#else
|
||||
sif::printError("SpiComIF::sendMessage: Failed to lock mutex with code %d\n", result);
|
||||
#endif
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
@ -315,9 +331,9 @@ ReturnValue_t SpiComIF::performHalfDuplexReception(SpiCookie* spiCookie) {
|
||||
result = HALF_DUPLEX_TRANSFER_FAILED;
|
||||
}
|
||||
|
||||
if (gpioId != gpio::NO_GPIO) {
|
||||
if (gpioId != gpio::NO_GPIO and not csLockManual) {
|
||||
gpioComIF->pullHigh(gpioId);
|
||||
result = spiMutex->unlockMutex();
|
||||
result = csMutex->unlockMutex();
|
||||
if (result != RETURN_OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "SpiComIF::getSendSuccess: Failed to unlock mutex" << std::endl;
|
||||
@ -346,15 +362,7 @@ ReturnValue_t SpiComIF::readReceivedMessage(CookieIF* cookie, uint8_t** buffer,
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
MutexIF* SpiComIF::getMutex(MutexIF::TimeoutType* timeoutType, uint32_t* timeoutMs) {
|
||||
if (timeoutType != nullptr) {
|
||||
*timeoutType = this->timeoutType;
|
||||
}
|
||||
if (timeoutMs != nullptr) {
|
||||
*timeoutMs = this->timeoutMs;
|
||||
}
|
||||
return spiMutex;
|
||||
}
|
||||
MutexIF* SpiComIF::getCsMutex() { return csMutex; }
|
||||
|
||||
void SpiComIF::performSpiWiretapping(SpiCookie* spiCookie) {
|
||||
if (spiCookie == nullptr) {
|
||||
@ -401,11 +409,27 @@ void SpiComIF::setSpiSpeedAndMode(int spiFd, spi::SpiModes mode, uint32_t speed)
|
||||
if (retval != 0) {
|
||||
utility::handleIoctlError("SpiComIF::setSpiSpeedAndMode: Setting SPI speed failed");
|
||||
}
|
||||
// This updates the SPI clock default polarity. Only setting the mode does not update
|
||||
// the line state, which can be an issue on mode switches because the clock line will
|
||||
// switch the state after the chip select is pulled low
|
||||
}
|
||||
|
||||
void SpiComIF::getSpiSpeedAndMode(int spiFd, spi::SpiModes& mode, uint32_t& speed) const {
|
||||
uint8_t tmpMode = 0;
|
||||
int retval = ioctl(spiFd, SPI_IOC_RD_MODE, &tmpMode);
|
||||
if (retval != 0) {
|
||||
utility::handleIoctlError("SpiComIF::getSpiSpeedAndMode: Reading SPI mode failed");
|
||||
}
|
||||
mode = static_cast<spi::SpiModes>(tmpMode);
|
||||
|
||||
retval = ioctl(spiFd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
|
||||
if (retval != 0) {
|
||||
utility::handleIoctlError("SpiComIF::getSpiSpeedAndMode: Getting SPI speed failed");
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& SpiComIF::getSpiDev() const { return dev; }
|
||||
|
||||
void SpiComIF::updateLinePolarity(int spiFd) {
|
||||
clockUpdateTransfer.len = 0;
|
||||
retval = ioctl(spiFd, SPI_IOC_MESSAGE(1), &clockUpdateTransfer);
|
||||
int retval = ioctl(spiFd, SPI_IOC_MESSAGE(1), &clockUpdateTransfer);
|
||||
if (retval != 0) {
|
||||
utility::handleIoctlError("SpiComIF::setSpiSpeedAndMode: Updating SPI default clock failed");
|
||||
}
|
||||
|
@ -22,17 +22,17 @@ class SpiCookie;
|
||||
*/
|
||||
class SpiComIF : public DeviceCommunicationIF, public SystemObject {
|
||||
public:
|
||||
static constexpr uint8_t spiRetvalId = CLASS_ID::HAL_SPI;
|
||||
static constexpr uint8_t CLASS_ID = CLASS_ID::HAL_SPI;
|
||||
static constexpr ReturnValue_t OPENING_FILE_FAILED =
|
||||
HasReturnvaluesIF::makeReturnCode(spiRetvalId, 0);
|
||||
HasReturnvaluesIF::makeReturnCode(CLASS_ID, 0);
|
||||
/* Full duplex (ioctl) transfer failure */
|
||||
static constexpr ReturnValue_t FULL_DUPLEX_TRANSFER_FAILED =
|
||||
HasReturnvaluesIF::makeReturnCode(spiRetvalId, 1);
|
||||
HasReturnvaluesIF::makeReturnCode(CLASS_ID, 1);
|
||||
/* Half duplex (read/write) transfer failure */
|
||||
static constexpr ReturnValue_t HALF_DUPLEX_TRANSFER_FAILED =
|
||||
HasReturnvaluesIF::makeReturnCode(spiRetvalId, 2);
|
||||
HasReturnvaluesIF::makeReturnCode(CLASS_ID, 2);
|
||||
|
||||
SpiComIF(object_id_t objectId, GpioIF* gpioComIF);
|
||||
SpiComIF(object_id_t objectId, std::string devname, GpioIF* gpioComIF);
|
||||
|
||||
ReturnValue_t initializeInterface(CookieIF* cookie) override;
|
||||
ReturnValue_t sendMessage(CookieIF* cookie, const uint8_t* sendData, size_t sendLen) override;
|
||||
@ -44,7 +44,8 @@ class SpiComIF : public DeviceCommunicationIF, public SystemObject {
|
||||
* @brief This function returns the mutex which can be used to protect the spi bus when
|
||||
* the chip select must be driven from outside of the com if.
|
||||
*/
|
||||
MutexIF* getMutex(MutexIF::TimeoutType* timeoutType = nullptr, uint32_t* timeoutMs = nullptr);
|
||||
MutexIF* getCsMutex();
|
||||
void setMutexParams(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs);
|
||||
|
||||
/**
|
||||
* Perform a regular send operation using Linux iotcl. This is public so it can be used
|
||||
@ -59,6 +60,20 @@ class SpiComIF : public DeviceCommunicationIF, public SystemObject {
|
||||
|
||||
GpioIF* getGpioInterface();
|
||||
void setSpiSpeedAndMode(int spiFd, spi::SpiModes mode, uint32_t speed);
|
||||
void getSpiSpeedAndMode(int spiFd, spi::SpiModes& mode, uint32_t& speed) const;
|
||||
|
||||
/**
|
||||
* This updates the SPI clock default polarity. Only setting the mode does not update
|
||||
* the line state, which can be an issue on mode switches because the clock line will
|
||||
* switch the state after the chip select is pulled low.
|
||||
*
|
||||
* It is recommended to call this function after #setSpiSpeedAndMode and after locking the
|
||||
* CS mutex if the SPI bus has multiple SPI devices with different speed and SPI modes attached.
|
||||
* @param spiFd
|
||||
*/
|
||||
void updateLinePolarity(int spiFd);
|
||||
|
||||
const std::string& getSpiDev() const;
|
||||
void performSpiWiretapping(SpiCookie* spiCookie);
|
||||
|
||||
ReturnValue_t getReadBuffer(address_t spiAddress, uint8_t** buffer);
|
||||
@ -70,10 +85,14 @@ class SpiComIF : public DeviceCommunicationIF, public SystemObject {
|
||||
};
|
||||
|
||||
GpioIF* gpioComIF = nullptr;
|
||||
|
||||
MutexIF* spiMutex = nullptr;
|
||||
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
|
||||
uint32_t timeoutMs = 20;
|
||||
std::string dev = "";
|
||||
/**
|
||||
* Protects the chip select operations. Lock when GPIO is pulled low, unlock after it was
|
||||
* pulled high
|
||||
*/
|
||||
MutexIF* csMutex = nullptr;
|
||||
// MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
|
||||
// uint32_t timeoutMs = DEFAULT_MUTEX_TIMEOUT;
|
||||
spi_ioc_transfer clockUpdateTransfer = {};
|
||||
|
||||
using SpiDeviceMap = std::unordered_map<address_t, SpiInstance>;
|
||||
|
@ -1,26 +1,25 @@
|
||||
#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)
|
||||
: SpiCookie(spi::SpiComIfModes::REGULAR, spiAddress, chipSelect, spiDev, maxSize, spiMode,
|
||||
spiSpeed, nullptr, nullptr) {}
|
||||
|
||||
SpiCookie::SpiCookie(address_t spiAddress, std::string spiDev, const size_t maxSize,
|
||||
SpiCookie::SpiCookie(address_t spiAddress, gpioId_t chipSelect, const size_t maxSize,
|
||||
spi::SpiModes spiMode, uint32_t spiSpeed)
|
||||
: SpiCookie(spiAddress, gpio::NO_GPIO, spiDev, maxSize, spiMode, spiSpeed) {}
|
||||
: SpiCookie(spi::SpiComIfModes::REGULAR, spiAddress, chipSelect, maxSize, spiMode, spiSpeed,
|
||||
nullptr, nullptr) {}
|
||||
|
||||
SpiCookie::SpiCookie(address_t spiAddress, gpioId_t chipSelect, std::string spiDev,
|
||||
const size_t maxSize, spi::SpiModes spiMode, uint32_t spiSpeed,
|
||||
SpiCookie::SpiCookie(address_t spiAddress, const size_t maxSize, spi::SpiModes spiMode,
|
||||
uint32_t spiSpeed)
|
||||
: SpiCookie(spiAddress, gpio::NO_GPIO, maxSize, spiMode, spiSpeed) {}
|
||||
|
||||
SpiCookie::SpiCookie(address_t spiAddress, gpioId_t chipSelect, const size_t maxSize,
|
||||
spi::SpiModes spiMode, uint32_t spiSpeed,
|
||||
spi::send_callback_function_t callback, void* args)
|
||||
: SpiCookie(spi::SpiComIfModes::CALLBACK, spiAddress, chipSelect, spiDev, maxSize, spiMode,
|
||||
spiSpeed, callback, args) {}
|
||||
: SpiCookie(spi::SpiComIfModes::CALLBACK, spiAddress, chipSelect, maxSize, spiMode, spiSpeed,
|
||||
callback, args) {}
|
||||
|
||||
SpiCookie::SpiCookie(spi::SpiComIfModes comIfMode, address_t spiAddress, gpioId_t chipSelect,
|
||||
std::string spiDev, const size_t maxSize, spi::SpiModes spiMode,
|
||||
uint32_t spiSpeed, spi::send_callback_function_t callback, void* args)
|
||||
const size_t maxSize, spi::SpiModes spiMode, uint32_t spiSpeed,
|
||||
spi::send_callback_function_t callback, void* args)
|
||||
: spiAddress(spiAddress),
|
||||
chipSelectPin(chipSelect),
|
||||
spiDevice(spiDev),
|
||||
comIfMode(comIfMode),
|
||||
maxSize(maxSize),
|
||||
spiMode(spiMode),
|
||||
@ -50,8 +49,6 @@ size_t SpiCookie::getMaxBufferSize() const { return maxSize; }
|
||||
|
||||
address_t SpiCookie::getSpiAddress() const { return spiAddress; }
|
||||
|
||||
std::string SpiCookie::getSpiDevice() const { return spiDevice; }
|
||||
|
||||
void SpiCookie::setThreeWireSpi(bool enable) { uncommonParameters.threeWireSpi = enable; }
|
||||
|
||||
void SpiCookie::setLsbFirst(bool enable) { uncommonParameters.lsbFirst = enable; }
|
||||
@ -107,3 +104,17 @@ void SpiCookie::getCallback(spi::send_callback_function_t* callback, void** args
|
||||
*callback = this->sendCallback;
|
||||
*args = this->callbackArgs;
|
||||
}
|
||||
|
||||
void SpiCookie::setCsLockManual(bool enable) { manualCsLock = enable; }
|
||||
|
||||
bool SpiCookie::getCsLockManual() const { return manualCsLock; }
|
||||
|
||||
void SpiCookie::getMutexParams(MutexIF::TimeoutType& csTimeoutType, dur_millis_t& csTimeout) const {
|
||||
csTimeoutType = this->csTimeoutType;
|
||||
csTimeout = this->csTimeout;
|
||||
}
|
||||
|
||||
void SpiCookie::setMutexParams(MutexIF::TimeoutType csTimeoutType, dur_millis_t csTimeout) {
|
||||
this->csTimeoutType = csTimeoutType;
|
||||
this->csTimeout = csTimeout;
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
#define LINUX_SPI_SPICOOKIE_H_
|
||||
|
||||
#include <fsfw/devicehandlers/CookieIF.h>
|
||||
#include <fsfw/ipc/MutexIF.h>
|
||||
#include <fsfw/timemanager/clockDefinitions.h>
|
||||
#include <linux/spi/spidev.h>
|
||||
|
||||
#include "../../common/gpio/gpioDefinitions.h"
|
||||
@ -20,6 +22,8 @@
|
||||
*/
|
||||
class SpiCookie : public CookieIF {
|
||||
public:
|
||||
static constexpr dur_millis_t DEFAULT_MUTEX_TIMEOUT = 20;
|
||||
|
||||
/**
|
||||
* Each SPI device will have a corresponding cookie. The cookie is used by the communication
|
||||
* interface and contains device specific information like the largest expected size to be
|
||||
@ -29,23 +33,22 @@ class SpiCookie : public CookieIF {
|
||||
* @param spiDev
|
||||
* @param maxSize
|
||||
*/
|
||||
SpiCookie(address_t spiAddress, gpioId_t chipSelect, std::string spiDev, const size_t maxSize,
|
||||
spi::SpiModes spiMode, uint32_t spiSpeed);
|
||||
SpiCookie(address_t spiAddress, gpioId_t chipSelect, const size_t maxSize, spi::SpiModes spiMode,
|
||||
uint32_t spiSpeed);
|
||||
|
||||
/**
|
||||
* Like constructor above, but without a dedicated GPIO CS. Can be used for hardware
|
||||
* slave select or if CS logic is performed with decoders.
|
||||
*/
|
||||
SpiCookie(address_t spiAddress, std::string spiDev, const size_t maxReplySize,
|
||||
spi::SpiModes spiMode, uint32_t spiSpeed);
|
||||
SpiCookie(address_t spiAddress, const size_t maxReplySize, spi::SpiModes spiMode,
|
||||
uint32_t spiSpeed);
|
||||
|
||||
/**
|
||||
* Use the callback mode of the SPI communication interface. The user can pass the callback
|
||||
* function here or by using the setter function #setCallbackMode
|
||||
*/
|
||||
SpiCookie(address_t spiAddress, gpioId_t chipSelect, std::string spiDev, const size_t maxSize,
|
||||
spi::SpiModes spiMode, uint32_t spiSpeed, spi::send_callback_function_t callback,
|
||||
void* args);
|
||||
SpiCookie(address_t spiAddress, gpioId_t chipSelect, const size_t maxSize, spi::SpiModes spiMode,
|
||||
uint32_t spiSpeed, spi::send_callback_function_t callback, void* args);
|
||||
|
||||
/**
|
||||
* Get the callback function
|
||||
@ -55,7 +58,6 @@ class SpiCookie : public CookieIF {
|
||||
void getCallback(spi::send_callback_function_t* callback, void** args);
|
||||
|
||||
address_t getSpiAddress() const;
|
||||
std::string getSpiDevice() const;
|
||||
gpioId_t getChipSelectPin() const;
|
||||
size_t getMaxBufferSize() const;
|
||||
|
||||
@ -139,9 +141,42 @@ class SpiCookie : public CookieIF {
|
||||
*/
|
||||
void activateCsDeselect(bool deselectCs, uint16_t delayUsecs);
|
||||
|
||||
void getMutexParams(MutexIF::TimeoutType& csTimeoutType, dur_millis_t& csTimeout) const;
|
||||
void setMutexParams(MutexIF::TimeoutType csTimeoutType, dur_millis_t csTimeout);
|
||||
|
||||
void setCsLockManual(bool enable);
|
||||
bool getCsLockManual() const;
|
||||
|
||||
spi_ioc_transfer* getTransferStructHandle();
|
||||
|
||||
private:
|
||||
address_t spiAddress;
|
||||
gpioId_t chipSelectPin;
|
||||
|
||||
spi::SpiComIfModes comIfMode;
|
||||
|
||||
// Required for regular mode
|
||||
const size_t maxSize;
|
||||
spi::SpiModes spiMode;
|
||||
/**
|
||||
* If this is set to true, the SPI ComIF will not perform any mutex locking for the
|
||||
* CS mechanism. The user is responsible to locking and unlocking the mutex for the
|
||||
* whole duration of the transfers.
|
||||
*/
|
||||
bool manualCsLock = false;
|
||||
uint32_t spiSpeed;
|
||||
bool halfDuplex = false;
|
||||
|
||||
MutexIF::TimeoutType csTimeoutType = MutexIF::TimeoutType::WAITING;
|
||||
dur_millis_t csTimeout = DEFAULT_MUTEX_TIMEOUT;
|
||||
|
||||
// Required for callback mode
|
||||
spi::send_callback_function_t sendCallback = nullptr;
|
||||
void* callbackArgs = nullptr;
|
||||
|
||||
struct spi_ioc_transfer spiTransferStruct = {};
|
||||
UncommonParameters uncommonParameters;
|
||||
|
||||
/**
|
||||
* Internal constructor which initializes every field
|
||||
* @param spiAddress
|
||||
@ -154,27 +189,8 @@ class SpiCookie : public CookieIF {
|
||||
* @param args
|
||||
*/
|
||||
SpiCookie(spi::SpiComIfModes comIfMode, address_t spiAddress, gpioId_t chipSelect,
|
||||
std::string spiDev, const size_t maxSize, spi::SpiModes spiMode, uint32_t spiSpeed,
|
||||
const size_t maxSize, spi::SpiModes spiMode, uint32_t spiSpeed,
|
||||
spi::send_callback_function_t callback, void* args);
|
||||
|
||||
address_t spiAddress;
|
||||
gpioId_t chipSelectPin;
|
||||
std::string spiDevice;
|
||||
|
||||
spi::SpiComIfModes comIfMode;
|
||||
|
||||
// Required for regular mode
|
||||
const size_t maxSize;
|
||||
spi::SpiModes spiMode;
|
||||
uint32_t spiSpeed;
|
||||
bool halfDuplex = false;
|
||||
|
||||
// Required for callback mode
|
||||
spi::send_callback_function_t sendCallback = nullptr;
|
||||
void* callbackArgs = nullptr;
|
||||
|
||||
struct spi_ioc_transfer spiTransferStruct = {};
|
||||
UncommonParameters uncommonParameters;
|
||||
};
|
||||
|
||||
#endif /* LINUX_SPI_SPICOOKIE_H_ */
|
||||
|
@ -265,6 +265,7 @@ void UartComIF::configureBaudrate(struct termios* options, UartCookie* uartCooki
|
||||
cfsetispeed(options, B230400);
|
||||
cfsetospeed(options, B230400);
|
||||
break;
|
||||
#ifndef __APPLE__
|
||||
case UartBaudRate::RATE_460800:
|
||||
cfsetispeed(options, B460800);
|
||||
cfsetospeed(options, B460800);
|
||||
@ -313,6 +314,7 @@ void UartComIF::configureBaudrate(struct termios* options, UartCookie* uartCooki
|
||||
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;
|
||||
|
@ -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;
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define FIXEDARRAYLIST_H_
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
#include "ArrayList.h"
|
||||
/**
|
||||
@ -9,10 +10,9 @@
|
||||
*/
|
||||
template <typename T, size_t MAX_SIZE, typename count_t = uint8_t>
|
||||
class FixedArrayList : public ArrayList<T, count_t> {
|
||||
#if !defined(_MSC_VER)
|
||||
static_assert(MAX_SIZE <= (std::pow(2, sizeof(count_t) * 8) - 1),
|
||||
static_assert(MAX_SIZE <= std::numeric_limits<count_t>::max(),
|
||||
"count_t is not large enough to hold MAX_SIZE");
|
||||
#endif
|
||||
|
||||
private:
|
||||
T data[MAX_SIZE];
|
||||
|
||||
|
@ -10,7 +10,7 @@ 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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -243,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -416,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, 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;
|
||||
@ -437,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;
|
||||
@ -472,7 +489,8 @@ size_t DeviceHandlerBase::getNextReplyLength(DeviceCommandId_t commandId) {
|
||||
}
|
||||
DeviceReplyIter iter = deviceReplyMap.find(replyId);
|
||||
if (iter != deviceReplyMap.end()) {
|
||||
if (iter->second.delayCycles != 0) {
|
||||
if ((iter->second.delayCycles != 0 && iter->second.countdown == nullptr) ||
|
||||
(iter->second.active && iter->second.countdown != nullptr)) {
|
||||
return iter->second.replyLen;
|
||||
}
|
||||
}
|
||||
@ -816,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) {
|
||||
@ -845,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;
|
||||
@ -970,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;
|
||||
@ -1204,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;
|
||||
}
|
||||
}
|
||||
@ -1359,6 +1401,8 @@ uint8_t DeviceHandlerBase::getReplyDelayCycles(DeviceCommandId_t deviceCommand)
|
||||
DeviceReplyMap::iterator iter = deviceReplyMap.find(deviceCommand);
|
||||
if (iter == deviceReplyMap.end()) {
|
||||
return 0;
|
||||
} else if (iter->second.countdown != nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return iter->second.delayCycles;
|
||||
}
|
||||
|
@ -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,22 +461,26 @@ 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.
|
||||
* @param maxDelayCycles The maximum number of delay cycles the reply waits
|
||||
* until it times out.
|
||||
* until it times out.
|
||||
* @param periodic Indicates if the command is periodic (i.e. it is sent
|
||||
* by the device repeatedly without request) or not. Default is aperiodic (0).
|
||||
* Please note that periodic replies are disabled by default. You can enable them with
|
||||
* #updatePeriodicReply
|
||||
* by the device repeatedly without request) or not. Default is aperiodic (0).
|
||||
* Please note that periodic replies are disabled by default. You can enable them with
|
||||
* #updatePeriodicReply
|
||||
* @param countdown Instead of using maxDelayCycles to timeout a device reply it is also possible
|
||||
* to provide a pointer to a Countdown object which will signal the timeout
|
||||
* when expired
|
||||
* @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.
|
||||
@ -776,11 +783,18 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
* This is used to keep track of pending replies.
|
||||
*/
|
||||
struct DeviceReplyInfo {
|
||||
//! For Command-Reply combinations:
|
||||
//! The maximum number of cycles the handler should wait for a reply
|
||||
//! to this command.
|
||||
//!
|
||||
//! Reply Only:
|
||||
//! For periodic replies, this variable will be the number of delay cycles between the replies.
|
||||
//! For the non-periodic variant, this variable is not used as there is no meaningful
|
||||
//! definition for delay
|
||||
uint16_t maxDelayCycles;
|
||||
//! The currently remaining cycles the handler should wait for a reply,
|
||||
//! 0 means there is no reply expected
|
||||
//! This variable will be set to #maxDelayCycles if a reply is expected.
|
||||
//! For non-periodic replies without a command, this variable is unused.
|
||||
//! A runtime value of 0 means there is no reply is currently expected.
|
||||
uint16_t delayCycles;
|
||||
size_t replyLen = 0; //!< Expected size of the reply.
|
||||
//! if this is !=0, the delayCycles will not be reset to 0 but to
|
||||
@ -792,6 +806,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>;
|
||||
@ -1068,11 +1087,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.
|
||||
@ -1253,6 +1273,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.
|
||||
*
|
||||
|
@ -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()) {
|
||||
|
@ -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 {
|
||||
|
@ -16,10 +16,15 @@ class HasHealthIF {
|
||||
};
|
||||
|
||||
static const uint8_t INTERFACE_ID = CLASS_ID::HAS_HEALTH_IF;
|
||||
static const ReturnValue_t OBJECT_NOT_HEALTHY = MAKE_RETURN_CODE(1);
|
||||
static const ReturnValue_t INVALID_HEALTH_STATE = MAKE_RETURN_CODE(2);
|
||||
static constexpr ReturnValue_t OBJECT_NOT_HEALTHY =
|
||||
HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 1);
|
||||
static constexpr ReturnValue_t INVALID_HEALTH_STATE =
|
||||
HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 2);
|
||||
static constexpr ReturnValue_t IS_EXTERNALLY_CONTROLLED =
|
||||
HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 3);
|
||||
|
||||
static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::SYSTEM_MANAGER_1;
|
||||
//! P1: New Health, P2: Old Health
|
||||
static const Event HEALTH_INFO = MAKE_EVENT(6, severity::INFO);
|
||||
static const Event CHILD_CHANGED_HEALTH = MAKE_EVENT(7, severity::INFO);
|
||||
static const Event CHILD_PROBLEMS = MAKE_EVENT(8, severity::LOW);
|
||||
|
@ -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();
|
||||
|
@ -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_ */
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include <ws2tcpip.h>
|
||||
#elif defined(PLATFORM_UNIX)
|
||||
#include <netdb.h>
|
||||
|
||||
#include <utility>
|
||||
#endif
|
||||
|
||||
const std::string TcpTmTcServer::DEFAULT_SERVER_PORT = tcpip::DEFAULT_SERVER_PORT;
|
||||
@ -29,7 +31,7 @@ TcpTmTcServer::TcpTmTcServer(object_id_t objectId, object_id_t tmtcTcpBridge,
|
||||
: SystemObject(objectId),
|
||||
tmtcBridgeId(tmtcTcpBridge),
|
||||
receptionMode(receptionMode),
|
||||
tcpConfig(customTcpServerPort),
|
||||
tcpConfig(std::move(customTcpServerPort)),
|
||||
receptionBuffer(receptionBufferSize),
|
||||
ringBuffer(ringBufferSize, true) {}
|
||||
|
||||
@ -103,7 +105,7 @@ ReturnValue_t TcpTmTcServer::initialize() {
|
||||
|
||||
TcpTmTcServer::~TcpTmTcServer() { closeSocket(listenerTcpSocket); }
|
||||
|
||||
ReturnValue_t TcpTmTcServer::performOperation(uint8_t opCode) {
|
||||
[[noreturn]] ReturnValue_t TcpTmTcServer::performOperation(uint8_t opCode) {
|
||||
using namespace tcpip;
|
||||
// If a connection is accepted, the corresponding socket will be assigned to the new socket
|
||||
socket_t connSocket = 0;
|
||||
@ -138,7 +140,6 @@ ReturnValue_t TcpTmTcServer::performOperation(uint8_t opCode) {
|
||||
closeSocket(connSocket);
|
||||
connSocket = 0;
|
||||
}
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t TcpTmTcServer::initializeAfterTaskCreation() {
|
||||
@ -159,8 +160,8 @@ void TcpTmTcServer::handleServerOperation(socket_t& connSocket) {
|
||||
#endif
|
||||
|
||||
while (true) {
|
||||
int retval = recv(connSocket, reinterpret_cast<char*>(receptionBuffer.data()),
|
||||
receptionBuffer.capacity(), tcpConfig.tcpFlags);
|
||||
ssize_t retval = recv(connSocket, reinterpret_cast<char*>(receptionBuffer.data()),
|
||||
receptionBuffer.capacity(), tcpConfig.tcpFlags);
|
||||
if (retval == 0) {
|
||||
size_t availableReadData = ringBuffer.getAvailableReadData();
|
||||
if (availableReadData > lastRingBufferSize) {
|
||||
@ -252,17 +253,17 @@ ReturnValue_t TcpTmTcServer::handleTcReception(uint8_t* spacePacket, size_t pack
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string TcpTmTcServer::getTcpPort() const { return tcpConfig.tcpPort; }
|
||||
const std::string& TcpTmTcServer::getTcpPort() const { return tcpConfig.tcpPort; }
|
||||
|
||||
void TcpTmTcServer::setSpacePacketParsingOptions(std::vector<uint16_t> validPacketIds) {
|
||||
this->validPacketIds = validPacketIds;
|
||||
void TcpTmTcServer::setSpacePacketParsingOptions(std::vector<uint16_t> validPacketIds_) {
|
||||
this->validPacketIds = std::move(validPacketIds_);
|
||||
}
|
||||
|
||||
TcpTmTcServer::TcpConfig& TcpTmTcServer::getTcpConfigStruct() { return tcpConfig; }
|
||||
|
||||
ReturnValue_t TcpTmTcServer::handleTmSending(socket_t connSocket, bool& tmSent) {
|
||||
// Access to the FIFO is mutex protected because it is filled by the bridge
|
||||
MutexGuard(tmtcBridge->mutex, tmtcBridge->timeoutType, tmtcBridge->mutexTimeoutMs);
|
||||
MutexGuard mg(tmtcBridge->mutex, tmtcBridge->timeoutType, tmtcBridge->mutexTimeoutMs);
|
||||
store_address_t storeId;
|
||||
while ((not tmtcBridge->tmFifo->empty()) and
|
||||
(tmtcBridge->packetSentCounter < tmtcBridge->sentPacketsPerCycle)) {
|
||||
@ -283,8 +284,8 @@ ReturnValue_t TcpTmTcServer::handleTmSending(socket_t connSocket, bool& tmSent)
|
||||
#endif
|
||||
arrayprinter::print(storeAccessor.data(), storeAccessor.size());
|
||||
}
|
||||
int retval = send(connSocket, reinterpret_cast<const char*>(storeAccessor.data()),
|
||||
storeAccessor.size(), tcpConfig.tcpTmFlags);
|
||||
ssize_t retval = send(connSocket, reinterpret_cast<const char*>(storeAccessor.data()),
|
||||
storeAccessor.size(), tcpConfig.tcpTmFlags);
|
||||
if (retval == static_cast<int>(storeAccessor.size())) {
|
||||
// Packet sent, clear FIFO entry
|
||||
tmtcBridge->tmFifo->pop();
|
||||
@ -339,6 +340,9 @@ ReturnValue_t TcpTmTcServer::handleTcRingBufferData(size_t availableReadData) {
|
||||
size_t foundSize = 0;
|
||||
size_t readLen = 0;
|
||||
while (readLen < readAmount) {
|
||||
if (spacePacketParser == nullptr) {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
result =
|
||||
spacePacketParser->parseSpacePackets(bufPtrPtr, readAmount, startIdx, foundSize, readLen);
|
||||
switch (result) {
|
||||
|
@ -17,6 +17,7 @@
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class TcpTmTcBridge;
|
||||
@ -44,7 +45,7 @@ class TcpTmTcServer : public SystemObject, public TcpIpBase, public ExecutableOb
|
||||
|
||||
struct TcpConfig {
|
||||
public:
|
||||
TcpConfig(std::string tcpPort) : tcpPort(tcpPort) {}
|
||||
explicit TcpConfig(std::string tcpPort) : tcpPort(std::move(tcpPort)) {}
|
||||
|
||||
/**
|
||||
* Passed to the recv call
|
||||
@ -84,7 +85,7 @@ class TcpTmTcServer : public SystemObject, public TcpIpBase, public ExecutableOb
|
||||
size_t ringBufferSize = RING_BUFFER_SIZE,
|
||||
std::string customTcpServerPort = DEFAULT_SERVER_PORT,
|
||||
ReceptionModes receptionMode = ReceptionModes::SPACE_PACKETS);
|
||||
virtual ~TcpTmTcServer();
|
||||
~TcpTmTcServer() override;
|
||||
|
||||
void enableWiretapping(bool enable);
|
||||
|
||||
@ -97,10 +98,10 @@ class TcpTmTcServer : public SystemObject, public TcpIpBase, public ExecutableOb
|
||||
void setSpacePacketParsingOptions(std::vector<uint16_t> validPacketIds);
|
||||
|
||||
ReturnValue_t initialize() override;
|
||||
ReturnValue_t performOperation(uint8_t opCode) override;
|
||||
[[noreturn]] ReturnValue_t performOperation(uint8_t opCode) override;
|
||||
ReturnValue_t initializeAfterTaskCreation() override;
|
||||
|
||||
std::string getTcpPort() const;
|
||||
[[nodiscard]] const std::string& getTcpPort() const;
|
||||
|
||||
protected:
|
||||
StorageManagerIF* tcStore = nullptr;
|
||||
@ -115,7 +116,7 @@ class TcpTmTcServer : public SystemObject, public TcpIpBase, public ExecutableOb
|
||||
|
||||
ReceptionModes receptionMode;
|
||||
TcpConfig tcpConfig;
|
||||
struct sockaddr tcpAddress;
|
||||
struct sockaddr tcpAddress = {};
|
||||
socket_t listenerTcpSocket = 0;
|
||||
|
||||
MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE;
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -46,8 +46,8 @@ void FixedTimeslotTask::missedDeadlineCounter() {
|
||||
FixedTimeslotTask::deadlineMissedCount++;
|
||||
if (FixedTimeslotTask::deadlineMissedCount % 10 == 0) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "PST missed " << FixedTimeslotTask::deadlineMissedCount << " deadlines."
|
||||
<< std::endl;
|
||||
sif::warning << "PST missed " << FixedTimeslotTask::deadlineMissedCount << " deadlines"
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
@ -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()
|
||||
|
@ -14,6 +14,8 @@ FixedTimeslotTask::FixedTimeslotTask(const char* name_, int priority_, size_t st
|
||||
|
||||
FixedTimeslotTask::~FixedTimeslotTask() {}
|
||||
|
||||
bool FixedTimeslotTask::isEmpty() const { return pst.isEmpty(); }
|
||||
|
||||
void* FixedTimeslotTask::taskEntryPoint(void* arg) {
|
||||
// The argument is re-interpreted as PollingTask.
|
||||
FixedTimeslotTask* originalTask(reinterpret_cast<FixedTimeslotTask*>(arg));
|
||||
@ -50,7 +52,7 @@ ReturnValue_t FixedTimeslotTask::addSlot(object_id_t componentId, uint32_t slotT
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
|
||||
ReturnValue_t FixedTimeslotTask::checkSequence() const { return pst.checkSequence(); }
|
||||
ReturnValue_t FixedTimeslotTask::checkSequence() { return pst.checkSequence(); }
|
||||
|
||||
void FixedTimeslotTask::taskFunctionality() {
|
||||
// Like FreeRTOS pthreads are running as soon as they are created
|
||||
@ -87,8 +89,8 @@ void FixedTimeslotTask::missedDeadlineCounter() {
|
||||
FixedTimeslotTask::deadlineMissedCount++;
|
||||
if (FixedTimeslotTask::deadlineMissedCount % 10 == 0) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "PST missed " << FixedTimeslotTask::deadlineMissedCount << " deadlines."
|
||||
<< std::endl;
|
||||
sif::warning << "PST missed " << FixedTimeslotTask::deadlineMissedCount << " deadlines"
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -24,15 +24,18 @@ class FixedTimeslotTask : public FixedTimeslotTaskIF, public PosixThread {
|
||||
FixedTimeslotTask(const char* name_, int priority_, size_t stackSize_, uint32_t periodMs_);
|
||||
virtual ~FixedTimeslotTask();
|
||||
|
||||
virtual ReturnValue_t startTask();
|
||||
ReturnValue_t startTask() override;
|
||||
|
||||
virtual ReturnValue_t sleepFor(uint32_t ms);
|
||||
ReturnValue_t sleepFor(uint32_t ms) override;
|
||||
|
||||
virtual uint32_t getPeriodMs() const;
|
||||
uint32_t getPeriodMs() const override;
|
||||
|
||||
virtual ReturnValue_t addSlot(object_id_t componentId, uint32_t slotTimeMs, int8_t executionStep);
|
||||
ReturnValue_t addSlot(object_id_t componentId, uint32_t slotTimeMs,
|
||||
int8_t executionStep) override;
|
||||
|
||||
virtual ReturnValue_t checkSequence() const;
|
||||
ReturnValue_t checkSequence() override;
|
||||
|
||||
bool isEmpty() const override;
|
||||
|
||||
/**
|
||||
* This static function can be used as #deadlineMissedFunc.
|
||||
|
@ -26,12 +26,12 @@ void* PeriodicPosixTask::taskEntryPoint(void* arg) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ReturnValue_t PeriodicPosixTask::addComponent(object_id_t object) {
|
||||
ReturnValue_t PeriodicPosixTask::addComponent(object_id_t object, uint8_t opCode) {
|
||||
ExecutableObjectIF* newObject = ObjectManager::instance()->get<ExecutableObjectIF>(object);
|
||||
return addComponent(newObject);
|
||||
return addComponent(newObject, opCode);
|
||||
}
|
||||
|
||||
ReturnValue_t PeriodicPosixTask::addComponent(ExecutableObjectIF* object) {
|
||||
ReturnValue_t PeriodicPosixTask::addComponent(ExecutableObjectIF* object, uint8_t opCode) {
|
||||
if (object == nullptr) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "PeriodicTask::addComponent: Invalid object. Make sure"
|
||||
@ -43,7 +43,7 @@ ReturnValue_t PeriodicPosixTask::addComponent(ExecutableObjectIF* object) {
|
||||
#endif
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
objectList.push_back(object);
|
||||
objectList.emplace(object, opCode);
|
||||
object->setTaskIF(this);
|
||||
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
@ -54,6 +54,9 @@ ReturnValue_t PeriodicPosixTask::sleepFor(uint32_t ms) {
|
||||
}
|
||||
|
||||
ReturnValue_t PeriodicPosixTask::startTask(void) {
|
||||
if (isEmpty()) {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
}
|
||||
started = true;
|
||||
PosixThread::createTask(&taskEntryPoint, this);
|
||||
return HasReturnvaluesIF::RETURN_OK;
|
||||
@ -64,15 +67,13 @@ void PeriodicPosixTask::taskFunctionality(void) {
|
||||
suspend();
|
||||
}
|
||||
|
||||
for (auto const& object : objectList) {
|
||||
object->initializeAfterTaskCreation();
|
||||
}
|
||||
initObjsAfterTaskCreation();
|
||||
|
||||
uint64_t lastWakeTime = getCurrentMonotonicTimeMs();
|
||||
// The task's "infinite" inner loop is entered.
|
||||
while (1) {
|
||||
for (auto const& object : objectList) {
|
||||
object->performOperation();
|
||||
for (auto const& objOpCodePair : objectList) {
|
||||
objOpCodePair.first->performOperation(objOpCodePair.second);
|
||||
}
|
||||
|
||||
if (not PosixThread::delayUntil(&lastWakeTime, periodMs)) {
|
||||
@ -84,3 +85,25 @@ void PeriodicPosixTask::taskFunctionality(void) {
|
||||
}
|
||||
|
||||
uint32_t PeriodicPosixTask::getPeriodMs() const { return periodMs; }
|
||||
|
||||
bool PeriodicPosixTask::isEmpty() const { return objectList.empty(); }
|
||||
|
||||
ReturnValue_t PeriodicPosixTask::initObjsAfterTaskCreation() {
|
||||
std::multiset<ExecutableObjectIF*> uniqueObjects;
|
||||
ReturnValue_t status = HasReturnvaluesIF::RETURN_OK;
|
||||
uint32_t count = 0;
|
||||
for (const auto& obj : objectList) {
|
||||
// Ensure that each unique object is initialized once.
|
||||
if (uniqueObjects.find(obj.first) == uniqueObjects.end()) {
|
||||
ReturnValue_t result = obj.first->initializeAfterTaskCreation();
|
||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||
count++;
|
||||
status = result;
|
||||
}
|
||||
uniqueObjects.emplace(obj.first);
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#ifndef FRAMEWORK_OSAL_LINUX_PERIODICPOSIXTASK_H_
|
||||
#define FRAMEWORK_OSAL_LINUX_PERIODICPOSIXTASK_H_
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#include "../../objectmanager/ObjectManagerIF.h"
|
||||
#include "../../tasks/ExecutableObjectIF.h"
|
||||
@ -40,7 +40,7 @@ class PeriodicPosixTask : public PosixThread, public PeriodicTaskIF {
|
||||
* @param object Id of the object to add.
|
||||
* @return RETURN_OK on success, RETURN_FAILED if the object could not be added.
|
||||
*/
|
||||
ReturnValue_t addComponent(object_id_t object) override;
|
||||
ReturnValue_t addComponent(object_id_t object, uint8_t opCode) override;
|
||||
|
||||
/**
|
||||
* Adds an object to the list of objects to be executed.
|
||||
@ -48,14 +48,20 @@ class PeriodicPosixTask : public PosixThread, public PeriodicTaskIF {
|
||||
* @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;
|
||||
ReturnValue_t addComponent(ExecutableObjectIF* object, uint8_t opCode) override;
|
||||
|
||||
uint32_t getPeriodMs() const override;
|
||||
|
||||
ReturnValue_t sleepFor(uint32_t ms) override;
|
||||
|
||||
ReturnValue_t initObjsAfterTaskCreation();
|
||||
|
||||
bool isEmpty() const override;
|
||||
|
||||
private:
|
||||
typedef std::vector<ExecutableObjectIF*> ObjectList; //!< Typedef for the List of objects.
|
||||
//! Typedef for the List of objects. Will contain the objects to execute and their respective
|
||||
//! op codes
|
||||
using ObjectList = std::multiset<std::pair<ExecutableObjectIF*, uint8_t>>;
|
||||
/**
|
||||
* @brief This attribute holds a list of objects to be executed.
|
||||
*/
|
||||
|
@ -50,8 +50,8 @@ void FixedTimeslotTask::missedDeadlineCounter() {
|
||||
FixedTimeslotTask::deadlineMissedCount++;
|
||||
if (FixedTimeslotTask::deadlineMissedCount % 10 == 0) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "PST missed " << FixedTimeslotTask::deadlineMissedCount << " deadlines."
|
||||
<< std::endl;
|
||||
sif::warning << "PST missed " << FixedTimeslotTask::deadlineMissedCount << " deadlines"
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
#include "DummyPowerSwitcher.h"
|
||||
|
||||
DummyPowerSwitcher::DummyPowerSwitcher(object_id_t objectId, size_t numberOfSwitches,
|
||||
size_t numberOfFuses, uint32_t switchDelayMs)
|
||||
: SystemObject(objectId),
|
||||
size_t numberOfFuses, bool registerGlobally,
|
||||
uint32_t switchDelayMs)
|
||||
: SystemObject(objectId, registerGlobally),
|
||||
switcherList(numberOfSwitches),
|
||||
fuseList(numberOfFuses),
|
||||
switchDelayMs(switchDelayMs) {}
|
||||
|
@ -8,10 +8,17 @@
|
||||
#include "definitions.h"
|
||||
#include "fsfw/objectmanager/SystemObject.h"
|
||||
|
||||
/**
|
||||
* @brief This component can be used to simulate a power switcher like a
|
||||
* Power Control Distribution Unit (PCDU)
|
||||
* @details
|
||||
* The dummy switcher will simply cache the commanded fuse and switch states and return them
|
||||
* in the according switch getter functions. In that sense, it simulates an ideal PCDU.
|
||||
*/
|
||||
class DummyPowerSwitcher : public SystemObject, public PowerSwitchIF {
|
||||
public:
|
||||
DummyPowerSwitcher(object_id_t objectId, size_t numberOfSwitches, size_t numberOfFuses,
|
||||
uint32_t switchDelayMs = 5000);
|
||||
bool registerGlobally = true, uint32_t switchDelayMs = 5000);
|
||||
|
||||
void setInitialSwitcherList(std::vector<ReturnValue_t> switcherList);
|
||||
void setInitialFusesList(std::vector<ReturnValue_t> switcherList);
|
||||
|
@ -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;
|
||||
|
@ -18,7 +18,8 @@ class PowerSwitcher : public HasReturnvaluesIF {
|
||||
static const uint8_t INTERFACE_ID = CLASS_ID::POWER_SWITCHER;
|
||||
static const ReturnValue_t IN_POWER_TRANSITION = MAKE_RETURN_CODE(1);
|
||||
static const ReturnValue_t SWITCH_STATE_MISMATCH = MAKE_RETURN_CODE(2);
|
||||
PowerSwitcher(PowerSwitchIF* switcher, uint8_t setSwitch1, uint8_t setSwitch2 = power::NO_SWITCH,
|
||||
PowerSwitcher(PowerSwitchIF* switcher, power::Switch_t setSwitch1,
|
||||
power::Switch_t setSwitch2 = power::NO_SWITCH,
|
||||
State_t setStartState = SWITCH_IS_OFF);
|
||||
void turnOn(bool checkCurrentState = true);
|
||||
void turnOff(bool checkCurrentState = true);
|
||||
|
@ -13,8 +13,6 @@ CService201HealthCommanding::CService201HealthCommanding(object_id_t objectId, u
|
||||
: CommandingServiceBase(objectId, apid, serviceId, numParallelCommands, commandTimeoutSeconds) {
|
||||
}
|
||||
|
||||
CService201HealthCommanding::~CService201HealthCommanding() {}
|
||||
|
||||
ReturnValue_t CService201HealthCommanding::isValidSubservice(uint8_t subservice) {
|
||||
switch (subservice) {
|
||||
case (Subservice::COMMAND_SET_HEALTH):
|
||||
@ -43,8 +41,8 @@ ReturnValue_t CService201HealthCommanding::getMessageQueueAndObject(uint8_t subs
|
||||
}
|
||||
|
||||
ReturnValue_t CService201HealthCommanding::checkInterfaceAndAcquireMessageQueue(
|
||||
MessageQueueId_t *messageQueueToSet, object_id_t *objectId) {
|
||||
HasHealthIF *destination = ObjectManager::instance()->get<HasHealthIF>(*objectId);
|
||||
MessageQueueId_t *messageQueueToSet, const object_id_t *objectId) {
|
||||
auto *destination = ObjectManager::instance()->get<HasHealthIF>(*objectId);
|
||||
if (destination == nullptr) {
|
||||
return CommandingServiceBase::INVALID_OBJECT;
|
||||
}
|
||||
@ -77,6 +75,10 @@ ReturnValue_t CService201HealthCommanding::prepareCommand(CommandMessage *messag
|
||||
HealthMessage::setHealthMessage(message, HealthMessage::HEALTH_ANNOUNCE_ALL);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// Should never happen, subservice was already checked
|
||||
result = RETURN_FAILED;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -95,10 +97,10 @@ ReturnValue_t CService201HealthCommanding::handleReply(const CommandMessage *rep
|
||||
}
|
||||
|
||||
// Not used for now, health state already reported by event
|
||||
ReturnValue_t CService201HealthCommanding::prepareHealthSetReply(const CommandMessage *reply) {
|
||||
prepareHealthSetReply(reply);
|
||||
uint8_t health = static_cast<uint8_t>(HealthMessage::getHealth(reply));
|
||||
uint8_t oldHealth = static_cast<uint8_t>(HealthMessage::getOldHealth(reply));
|
||||
[[maybe_unused]] ReturnValue_t CService201HealthCommanding::prepareHealthSetReply(
|
||||
const CommandMessage *reply) {
|
||||
auto health = static_cast<uint8_t>(HealthMessage::getHealth(reply));
|
||||
auto oldHealth = static_cast<uint8_t>(HealthMessage::getOldHealth(reply));
|
||||
HealthSetReply healthSetReply(health, oldHealth);
|
||||
return sendTmPacket(Subservice::REPLY_HEALTH_SET, &healthSetReply);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#ifndef FSFW_PUS_CSERVICE201HEALTHCOMMANDING_H_
|
||||
#define FSFW_PUS_CSERVICE201HEALTHCOMMANDING_H_
|
||||
|
||||
#include "../tmtcservices/CommandingServiceBase.h"
|
||||
#include "fsfw/tmtcservices/CommandingServiceBase.h"
|
||||
|
||||
/**
|
||||
* @brief Custom PUS service to set health of all objects
|
||||
@ -21,7 +21,7 @@ class CService201HealthCommanding : public CommandingServiceBase {
|
||||
public:
|
||||
CService201HealthCommanding(object_id_t objectId, uint16_t apid, uint8_t serviceId,
|
||||
uint8_t numParallelCommands = 4, uint16_t commandTimeoutSeconds = 60);
|
||||
virtual ~CService201HealthCommanding();
|
||||
~CService201HealthCommanding() override = default;
|
||||
|
||||
protected:
|
||||
/* CSB abstract function implementations */
|
||||
@ -38,12 +38,10 @@ class CService201HealthCommanding : public CommandingServiceBase {
|
||||
bool *isStep) override;
|
||||
|
||||
private:
|
||||
ReturnValue_t checkAndAcquireTargetID(object_id_t *objectIdToSet, const uint8_t *tcData,
|
||||
size_t tcDataLen);
|
||||
ReturnValue_t checkInterfaceAndAcquireMessageQueue(MessageQueueId_t *MessageQueueToSet,
|
||||
object_id_t *objectId);
|
||||
static ReturnValue_t checkInterfaceAndAcquireMessageQueue(MessageQueueId_t *MessageQueueToSet,
|
||||
const object_id_t *objectId);
|
||||
|
||||
ReturnValue_t prepareHealthSetReply(const CommandMessage *reply);
|
||||
[[maybe_unused]] ReturnValue_t prepareHealthSetReply(const CommandMessage *reply);
|
||||
|
||||
enum Subservice {
|
||||
//! [EXPORT] : [TC] Set health of target object
|
||||
|
@ -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 {
|
||||
|
@ -155,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);
|
||||
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -164,3 +164,5 @@ ReturnValue_t FixedSlotSequence::intializeSequenceAfterTaskCreation() const {
|
||||
void FixedSlotSequence::addCustomCheck(ReturnValue_t (*customCheckFunction)(const SlotList&)) {
|
||||
this->customCheckFunction = customCheckFunction;
|
||||
}
|
||||
|
||||
bool FixedSlotSequence::isEmpty() const { return slotList.empty(); }
|
||||
|
@ -159,6 +159,8 @@ class FixedSlotSequence {
|
||||
*/
|
||||
ReturnValue_t intializeSequenceAfterTaskCreation() const;
|
||||
|
||||
bool isEmpty() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief This list contains all PollingSlot objects, defining order and
|
||||
|
@ -30,7 +30,7 @@ class FixedTimeslotTaskIF : public PeriodicTaskIF {
|
||||
* Check whether the sequence is valid and perform all other required
|
||||
* initialization steps which are needed after task creation
|
||||
*/
|
||||
virtual ReturnValue_t checkSequence() const = 0;
|
||||
virtual ReturnValue_t checkSequence() = 0;
|
||||
};
|
||||
|
||||
#endif /* FRAMEWORK_TASKS_FIXEDTIMESLOTTASKIF_H_ */
|
||||
|
@ -31,7 +31,7 @@ class PeriodicTaskIF {
|
||||
* Add an object to the task. The object needs to implement ExecutableObjectIF
|
||||
* @return
|
||||
*/
|
||||
virtual ReturnValue_t addComponent(object_id_t object) {
|
||||
virtual ReturnValue_t addComponent(object_id_t object, uint8_t opCode = 0) {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
};
|
||||
|
||||
@ -41,13 +41,15 @@ class PeriodicTaskIF {
|
||||
* Add an object to the task.
|
||||
* @return
|
||||
*/
|
||||
virtual ReturnValue_t addComponent(ExecutableObjectIF* object) {
|
||||
virtual ReturnValue_t addComponent(ExecutableObjectIF* object, uint8_t opCode = 0) {
|
||||
return HasReturnvaluesIF::RETURN_FAILED;
|
||||
};
|
||||
|
||||
virtual ReturnValue_t sleepFor(uint32_t ms) = 0;
|
||||
|
||||
virtual uint32_t getPeriodMs() const = 0;
|
||||
|
||||
virtual bool isEmpty() const = 0;
|
||||
};
|
||||
|
||||
#endif /* PERIODICTASKIF_H_ */
|
||||
|
@ -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) {
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -588,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.
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -1,7 +1,11 @@
|
||||
#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() {}
|
||||
|
@ -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.
|
||||
|
@ -19,8 +19,8 @@ SpacePacket::SpacePacket(uint16_t packetDataLength, bool isTelecommand, uint16_t
|
||||
SpacePacket::~SpacePacket(void) {}
|
||||
|
||||
bool SpacePacket::addWholeData(const uint8_t* p_Data, uint32_t packet_size) {
|
||||
if (packet_size <= sizeof(this->data)) {
|
||||
memcpy(&this->localData.byteStream, p_Data, packet_size);
|
||||
if (packet_size <= sizeof(this->localData)) {
|
||||
memcpy(this->localData.byteStream, p_Data, packet_size);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -95,7 +95,7 @@ class CommandingServiceBase : public SystemObject,
|
||||
*/
|
||||
virtual ReturnValue_t performOperation(uint8_t opCode) override;
|
||||
|
||||
virtual uint16_t getIdentifier();
|
||||
virtual uint16_t getIdentifier() override;
|
||||
|
||||
/**
|
||||
* Returns the requestQueue MessageQueueId_t
|
||||
@ -104,7 +104,7 @@ class CommandingServiceBase : public SystemObject,
|
||||
*
|
||||
* @return requestQueue messageQueueId_t
|
||||
*/
|
||||
virtual MessageQueueId_t getRequestQueue();
|
||||
virtual MessageQueueId_t getRequestQueue() override;
|
||||
|
||||
/**
|
||||
* Returns the commandQueue MessageQueueId_t
|
||||
@ -166,7 +166,7 @@ class CommandingServiceBase : public SystemObject,
|
||||
* @param objectId Target object ID
|
||||
* @return
|
||||
* - @c RETURN_OK to generate a verification start message
|
||||
* - @c EXECUTION_COMPELTE Fire-and-forget command. Generate a completion
|
||||
* - @c EXECUTION_COMPLETE Fire-and-forget command. Generate a completion
|
||||
* verification message.
|
||||
* - @c Anything else rejects the packets and generates a start failure
|
||||
* verification.
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
class SourceSequenceCounter {
|
||||
private:
|
||||
uint16_t sequenceCount;
|
||||
uint16_t sequenceCount = 0;
|
||||
|
||||
public:
|
||||
SourceSequenceCounter(uint16_t initialSequenceCount = 0) : sequenceCount(initialSequenceCount) {}
|
||||
|
@ -11,7 +11,10 @@ target_sources(${FSFW_TEST_TGT} PRIVATE
|
||||
)
|
||||
|
||||
add_subdirectory(testcfg)
|
||||
add_subdirectory(mocks)
|
||||
|
||||
add_subdirectory(action)
|
||||
add_subdirectory(power)
|
||||
add_subdirectory(container)
|
||||
add_subdirectory(osal)
|
||||
add_subdirectory(serialize)
|
||||
|
3
tests/src/fsfw_tests/unit/mocks/CMakeLists.txt
Normal file
3
tests/src/fsfw_tests/unit/mocks/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
target_sources(${FSFW_TEST_TGT} PRIVATE
|
||||
PowerSwitcherMock.cpp
|
||||
)
|
77
tests/src/fsfw_tests/unit/mocks/PowerSwitcherMock.cpp
Normal file
77
tests/src/fsfw_tests/unit/mocks/PowerSwitcherMock.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
#include "PowerSwitcherMock.h"
|
||||
|
||||
static uint32_t SWITCH_REQUEST_UPDATE_VALUE = 0;
|
||||
|
||||
PowerSwitcherMock::PowerSwitcherMock() {}
|
||||
|
||||
ReturnValue_t PowerSwitcherMock::sendSwitchCommand(power::Switch_t switchNr, ReturnValue_t onOff) {
|
||||
if (switchMap.count(switchNr) == 0) {
|
||||
switchMap.emplace(switchNr, SwitchInfo(switchNr, onOff));
|
||||
} else {
|
||||
SwitchInfo& info = switchMap.at(switchNr);
|
||||
info.currentState = onOff;
|
||||
if (onOff == PowerSwitchIF::SWITCH_ON) {
|
||||
info.timesCalledOn++;
|
||||
} else {
|
||||
info.timesCalledOff++;
|
||||
}
|
||||
}
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t PowerSwitcherMock::sendFuseOnCommand(uint8_t fuseNr) {
|
||||
if (fuseMap.count(fuseNr) == 0) {
|
||||
fuseMap.emplace(fuseNr, FuseInfo(fuseNr));
|
||||
} else {
|
||||
FuseInfo& info = fuseMap.at(fuseNr);
|
||||
info.timesCalled++;
|
||||
}
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
ReturnValue_t PowerSwitcherMock::getSwitchState(power::Switch_t switchNr) const {
|
||||
if (switchMap.count(switchNr) == 1) {
|
||||
auto& info = switchMap.at(switchNr);
|
||||
SWITCH_REQUEST_UPDATE_VALUE++;
|
||||
return info.currentState;
|
||||
}
|
||||
return RETURN_FAILED;
|
||||
}
|
||||
|
||||
ReturnValue_t PowerSwitcherMock::getFuseState(uint8_t fuseNr) const {
|
||||
if (fuseMap.count(fuseNr) == 1) {
|
||||
return FUSE_ON;
|
||||
} else {
|
||||
return FUSE_OFF;
|
||||
}
|
||||
return RETURN_FAILED;
|
||||
}
|
||||
|
||||
uint32_t PowerSwitcherMock::getSwitchDelayMs(void) const { return 5000; }
|
||||
|
||||
SwitchInfo::SwitchInfo() : switcher(0) {}
|
||||
|
||||
SwitchInfo::SwitchInfo(power::Switch_t switcher, ReturnValue_t initState)
|
||||
: switcher(switcher), currentState(initState) {}
|
||||
|
||||
FuseInfo::FuseInfo(uint8_t fuse) : fuse(fuse) {}
|
||||
|
||||
void PowerSwitcherMock::getSwitchInfo(power::Switch_t switcher, SwitchInfo& info) {
|
||||
if (switchMap.count(switcher) == 1) {
|
||||
info = switchMap.at(switcher);
|
||||
}
|
||||
}
|
||||
|
||||
void PowerSwitcherMock::getFuseInfo(uint8_t fuse, FuseInfo& info) {
|
||||
if (fuseMap.count(fuse) == 1) {
|
||||
info = fuseMap.at(fuse);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t PowerSwitcherMock::getAmountSwitchStatWasRequested() {
|
||||
return SWITCH_REQUEST_UPDATE_VALUE;
|
||||
}
|
||||
|
||||
void PowerSwitcherMock::initSwitch(power::Switch_t switchNr) {
|
||||
switchMap.emplace(switchNr, SwitchInfo(switchNr, PowerSwitchIF::SWITCH_OFF));
|
||||
}
|
52
tests/src/fsfw_tests/unit/mocks/PowerSwitcherMock.h
Normal file
52
tests/src/fsfw_tests/unit/mocks/PowerSwitcherMock.h
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef FSFW_TESTS_SRC_FSFW_TESTS_UNIT_MOCKS_POWERSWITCHERMOCK_H_
|
||||
#define FSFW_TESTS_SRC_FSFW_TESTS_UNIT_MOCKS_POWERSWITCHERMOCK_H_
|
||||
|
||||
#include <fsfw/power/PowerSwitchIF.h>
|
||||
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
struct SwitchInfo {
|
||||
public:
|
||||
SwitchInfo();
|
||||
SwitchInfo(power::Switch_t switcher, ReturnValue_t initState);
|
||||
|
||||
power::Switch_t switcher;
|
||||
ReturnValue_t currentState = PowerSwitchIF::SWITCH_OFF;
|
||||
uint32_t timesCalledOn = 0;
|
||||
uint32_t timesCalledOff = 0;
|
||||
uint32_t timesStatusRequested = 0;
|
||||
};
|
||||
|
||||
struct FuseInfo {
|
||||
public:
|
||||
FuseInfo(uint8_t fuse);
|
||||
uint8_t fuse;
|
||||
uint32_t timesCalled = 0;
|
||||
};
|
||||
|
||||
class PowerSwitcherMock : public PowerSwitchIF {
|
||||
public:
|
||||
PowerSwitcherMock();
|
||||
|
||||
ReturnValue_t sendSwitchCommand(power::Switch_t switchNr, ReturnValue_t onOff) override;
|
||||
ReturnValue_t sendFuseOnCommand(uint8_t fuseNr) override;
|
||||
ReturnValue_t getSwitchState(power::Switch_t switchNr) const override;
|
||||
ReturnValue_t getFuseState(uint8_t fuseNr) const override;
|
||||
uint32_t getSwitchDelayMs(void) const override;
|
||||
|
||||
void getSwitchInfo(power::Switch_t switcher, SwitchInfo& info);
|
||||
void getFuseInfo(uint8_t fuse, FuseInfo& info);
|
||||
|
||||
uint32_t getAmountSwitchStatWasRequested();
|
||||
|
||||
void initSwitch(power::Switch_t switchNr);
|
||||
|
||||
private:
|
||||
using SwitchOnOffPair = std::pair<power::Switch_t, ReturnValue_t>;
|
||||
using FuseOnOffPair = std::pair<uint8_t, ReturnValue_t>;
|
||||
std::map<power::Switch_t, SwitchInfo> switchMap;
|
||||
std::map<uint8_t, FuseInfo> fuseMap;
|
||||
};
|
||||
|
||||
#endif /* FSFW_TESTS_SRC_FSFW_TESTS_UNIT_MOCKS_POWERSWITCHERMOCK_H_ */
|
3
tests/src/fsfw_tests/unit/power/CMakeLists.txt
Normal file
3
tests/src/fsfw_tests/unit/power/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
target_sources(${FSFW_TEST_TGT} PRIVATE
|
||||
testPowerSwitcher.cpp
|
||||
)
|
71
tests/src/fsfw_tests/unit/power/testPowerSwitcher.cpp
Normal file
71
tests/src/fsfw_tests/unit/power/testPowerSwitcher.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
#include <fsfw/power/DummyPowerSwitcher.h>
|
||||
#include <fsfw/power/PowerSwitcher.h>
|
||||
#include <fsfw_tests/unit/mocks/PowerSwitcherMock.h>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "objects/systemObjectList.h"
|
||||
|
||||
TEST_CASE("Power Switcher", "[power-switcher]") {
|
||||
PowerSwitcherMock mock;
|
||||
PowerSwitcher switcher(&mock, 1);
|
||||
DummyPowerSwitcher dummySwitcher(objects::DUMMY_POWER_SWITCHER, 5, 5, false);
|
||||
PowerSwitcher switcherUsingDummy(&dummySwitcher, 1);
|
||||
SwitchInfo switchInfo;
|
||||
mock.initSwitch(1);
|
||||
|
||||
SECTION("Basic Tests") {
|
||||
REQUIRE(switcher.getFirstSwitch() == 1);
|
||||
REQUIRE(switcher.getSecondSwitch() == power::NO_SWITCH);
|
||||
// Default start state
|
||||
REQUIRE(switcher.getState() == PowerSwitcher::SWITCH_IS_OFF);
|
||||
switcher.turnOn(true);
|
||||
REQUIRE(mock.getAmountSwitchStatWasRequested() == 1);
|
||||
REQUIRE(switcher.getState() == PowerSwitcher::WAIT_ON);
|
||||
REQUIRE(switcher.checkSwitchState() == PowerSwitcher::IN_POWER_TRANSITION);
|
||||
REQUIRE(switcher.active());
|
||||
switcher.doStateMachine();
|
||||
REQUIRE(switcher.getState() == PowerSwitcher::SWITCH_IS_ON);
|
||||
mock.getSwitchInfo(1, switchInfo);
|
||||
REQUIRE(switchInfo.timesCalledOn == 1);
|
||||
REQUIRE(not switcher.active());
|
||||
REQUIRE(mock.getAmountSwitchStatWasRequested() == 2);
|
||||
REQUIRE(switcher.checkSwitchState() == HasReturnvaluesIF::RETURN_OK);
|
||||
REQUIRE(mock.getAmountSwitchStatWasRequested() == 3);
|
||||
switcher.turnOff(false);
|
||||
REQUIRE(mock.getAmountSwitchStatWasRequested() == 3);
|
||||
REQUIRE(switcher.getState() == PowerSwitcher::WAIT_OFF);
|
||||
REQUIRE(switcher.active());
|
||||
REQUIRE(switcher.getState() == PowerSwitcher::WAIT_OFF);
|
||||
switcher.doStateMachine();
|
||||
mock.getSwitchInfo(1, switchInfo);
|
||||
REQUIRE(switcher.getState() == PowerSwitcher::SWITCH_IS_OFF);
|
||||
REQUIRE(switchInfo.timesCalledOn == 1);
|
||||
REQUIRE(switchInfo.timesCalledOff == 1);
|
||||
REQUIRE(not switcher.active());
|
||||
REQUIRE(mock.getAmountSwitchStatWasRequested() == 4);
|
||||
}
|
||||
|
||||
SECTION("Dummy Test") {
|
||||
// Same tests, but we can't really check the dummy
|
||||
REQUIRE(switcherUsingDummy.getFirstSwitch() == 1);
|
||||
REQUIRE(switcherUsingDummy.getSecondSwitch() == power::NO_SWITCH);
|
||||
REQUIRE(switcherUsingDummy.getState() == PowerSwitcher::SWITCH_IS_OFF);
|
||||
switcherUsingDummy.turnOn(true);
|
||||
REQUIRE(switcherUsingDummy.getState() == PowerSwitcher::WAIT_ON);
|
||||
REQUIRE(switcherUsingDummy.active());
|
||||
switcherUsingDummy.doStateMachine();
|
||||
REQUIRE(switcherUsingDummy.getState() == PowerSwitcher::SWITCH_IS_ON);
|
||||
REQUIRE(not switcherUsingDummy.active());
|
||||
|
||||
switcherUsingDummy.turnOff(false);
|
||||
REQUIRE(switcherUsingDummy.getState() == PowerSwitcher::WAIT_OFF);
|
||||
REQUIRE(switcherUsingDummy.active());
|
||||
REQUIRE(switcherUsingDummy.getState() == PowerSwitcher::WAIT_OFF);
|
||||
switcherUsingDummy.doStateMachine();
|
||||
REQUIRE(switcherUsingDummy.getState() == PowerSwitcher::SWITCH_IS_OFF);
|
||||
REQUIRE(not switcherUsingDummy.active());
|
||||
}
|
||||
|
||||
SECTION("More Dummy Tests") {}
|
||||
}
|
@ -21,8 +21,9 @@ enum sourceObjects : uint32_t {
|
||||
HK_RECEIVER_MOCK = 22,
|
||||
TEST_LOCAL_POOL_OWNER_BASE = 25,
|
||||
|
||||
SHARED_SET_ID = 26
|
||||
SHARED_SET_ID = 26,
|
||||
|
||||
DUMMY_POWER_SWITCHER = 27
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -179,6 +179,27 @@ TEST_CASE("CCSDSTime Tests", "[TestCCSDSTime]") {
|
||||
CHECK(todFromCCSDS.second == time.second);
|
||||
CHECK(todFromCCSDS.usecond == 123000);
|
||||
}
|
||||
SECTION("CUC") {
|
||||
timeval to;
|
||||
// seconds = 0x771E960F, microseconds = 0x237
|
||||
// microseconds = 567000
|
||||
// This gives 37158.912 1/65536 seconds -> rounded to 37159 -> 0x9127
|
||||
// This results in -> 567001 us
|
||||
std::array<uint8_t, 7> cucBuffer = {
|
||||
CCSDSTime::P_FIELD_CUC_6B_CCSDS, 0x77, 0x1E, 0x96, 0x0F, 0x91, 0x27};
|
||||
size_t foundLength = 0;
|
||||
auto result = CCSDSTime::convertFromCUC(&to, cucBuffer.data(), &foundLength, cucBuffer.size());
|
||||
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
|
||||
REQUIRE(foundLength == 7);
|
||||
REQUIRE(to.tv_sec == 1619801999); // TAI (no leap seconds)
|
||||
REQUIRE(to.tv_usec == 567001);
|
||||
|
||||
Clock::TimeOfDay_t tod;
|
||||
result = CCSDSTime::convertFromCUC(&tod, cucBuffer.data(), cucBuffer.size());
|
||||
// This test must be changed if this is ever going to be implemented
|
||||
REQUIRE(result == CCSDSTime::UNSUPPORTED_TIME_FORMAT);
|
||||
}
|
||||
|
||||
SECTION("CCSDS Failures") {
|
||||
Clock::TimeOfDay_t time;
|
||||
time.year = 2020;
|
||||
|
Reference in New Issue
Block a user