Merge remote-tracking branch 'upstream/development' into mueller/possible-ring-buffer-fix
fsfw/fsfw/pipeline/pr-development There was a failure building this commit Details

This commit is contained in:
Robin Müller 2022-05-13 11:30:32 +02:00
commit ff40a71582
No known key found for this signature in database
GPG Key ID: 11D4952C8CCEF814
18 changed files with 442 additions and 57 deletions

View File

@ -28,18 +28,43 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- `SimpleRingBuffer::writeData` now checks if the amount is larger than the total size of the
Buffer and rejects such writeData calls with `HasReturnvaluesIF::RETURN_FAILED`
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/586
- HAL Linux Uart: Baudrate and bits per word are enums now, avoiding misconfigurations
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/585
- IPC Message Queue Handling: Allow passing an optional `MqArgs` argument into the MessageQueue
creation call. It allows passing context information and an arbitrary user argument into
the message queue. Also streamlined and simplified `MessageQueue` implementation for all OSALs
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/583
- Clock:
- `timeval` to `TimeOfDay_t`
- Added Mutex for gmtime calls: (compare http://www.opengate.at/blog/2020/01/timeless/)
- Moved the statics used by Clock in ClockCommon.cpp to this file
- Better check for leap seconds
- Added Unittests for Clock (only getter)
### 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
@ -49,12 +74,30 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## Additions
- 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
- Linux HAL: Add wiretapping option for I2C. Enabled with `FSFW_HAL_I2C_WIRETAPPING` defined to 1
- Dedicated Version class and constant `fsfw::FSFW_VERSION` containing version information
inside `fsfw/version.h`
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/559
- 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
## Fixed

View File

@ -17,11 +17,12 @@ set(FSFW_REVISION 0)
# Add the cmake folder so the FindSphinx module is found
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
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)
@ -32,19 +33,28 @@ set(FSFW_CATCH2_LIB_VERSION v${FSFW_CATCH2_LIB_MAJOR_VERSION}.0.0-preview5 CACHE
"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)
@ -71,6 +81,10 @@ 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()
if(FSFW_BUILD_UNITTESTS)
message(STATUS "Building the FSFW unittests in addition to the static library")
# Check whether the user has already installed Catch2 first
@ -95,6 +109,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 "Generating coverage data for the library")

View File

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

View File

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

View File

@ -0,0 +1,38 @@
#ifndef FSFW_SRC_FSFW_POWER_DUMMYPOWERSWITCHER_H_
#define FSFW_SRC_FSFW_POWER_DUMMYPOWERSWITCHER_H_
#include <cstddef>
#include <vector>
#include "PowerSwitchIF.h"
#include "definitions.h"
#include "fsfw/objectmanager/SystemObject.h"
/**
* @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,
bool registerGlobally = true, uint32_t switchDelayMs = 5000);
void setInitialSwitcherList(std::vector<ReturnValue_t> switcherList);
void setInitialFusesList(std::vector<ReturnValue_t> switcherList);
virtual ReturnValue_t sendSwitchCommand(power::Switch_t switchNr, ReturnValue_t onOff) override;
virtual ReturnValue_t sendFuseOnCommand(uint8_t fuseNr) override;
virtual ReturnValue_t getSwitchState(power::Switch_t switchNr) const override;
virtual ReturnValue_t getFuseState(uint8_t fuseNr) const override;
virtual uint32_t getSwitchDelayMs(void) const override;
private:
std::vector<ReturnValue_t> switcherList;
std::vector<ReturnValue_t> fuseList;
uint32_t switchDelayMs = 5000;
};
#endif /* FSFW_SRC_FSFW_POWER_DUMMYPOWERSWITCHER_H_ */

View File

@ -34,14 +34,14 @@ class Fuse : public SystemObject,
};
static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::PCDU_1;
static const Event FUSE_CURRENT_HIGH = MAKE_EVENT(
1, severity::LOW); //!< PSS detected that current on a fuse is totally out of bounds.
static const Event FUSE_WENT_OFF =
MAKE_EVENT(2, severity::LOW); //!< PSS detected a fuse that went off.
static const Event POWER_ABOVE_HIGH_LIMIT =
MAKE_EVENT(4, severity::LOW); //!< PSS detected a fuse that violates its limits.
static const Event POWER_BELOW_LOW_LIMIT =
MAKE_EVENT(5, severity::LOW); //!< PSS detected a fuse that violates its limits.
//! PSS detected that current on a fuse is totally out of bounds.
static const Event FUSE_CURRENT_HIGH = MAKE_EVENT(1, severity::LOW);
//! PSS detected a fuse that went off.
static const Event FUSE_WENT_OFF = MAKE_EVENT(2, severity::LOW);
//! PSS detected a fuse that violates its limits.
static const Event POWER_ABOVE_HIGH_LIMIT = MAKE_EVENT(4, severity::LOW);
//! PSS detected a fuse that violates its limits.
static const Event POWER_BELOW_LOW_LIMIT = MAKE_EVENT(5, severity::LOW);
typedef std::list<PowerComponentIF *> DeviceList;
Fuse(object_id_t fuseObjectId, uint8_t fuseId, sid_t variableSet, VariableIds ids,

View File

@ -15,7 +15,8 @@ PowerSensor::PowerSensor(object_id_t objectId, sid_t setId, VariableIds ids, Def
limits.currentMin, limits.currentMax, events.currentLow, events.currentHigh),
voltageLimit(objectId, MODULE_ID_VOLTAGE, ids.pidVoltage, confirmationCount,
limits.voltageMin, limits.voltageMax, events.voltageLow, events.voltageHigh) {
commandQueue = QueueFactory::instance()->createMessageQueue();
commandQueue =
QueueFactory::instance()->createMessageQueue(3, MessageQueueMessage::MAX_MESSAGE_SIZE);
}
PowerSensor::~PowerSensor() { QueueFactory::instance()->deleteMessageQueue(commandQueue); }

View File

@ -3,6 +3,7 @@
#include "../events/Event.h"
#include "../returnvalues/HasReturnvaluesIF.h"
#include "definitions.h"
/**
*
* @brief This interface defines a connection to a device that is capable of
@ -37,11 +38,11 @@ class PowerSwitchIF : public HasReturnvaluesIF {
* @param switchNr
* @param onOff on == @c SWITCH_ON; off != @c SWITCH_ON
*/
virtual void sendSwitchCommand(uint8_t switchNr, ReturnValue_t onOff) const = 0;
virtual ReturnValue_t sendSwitchCommand(power::Switch_t switchNr, ReturnValue_t onOff) = 0;
/**
* Sends a command to the Power Unit to enable a certain fuse.
*/
virtual void sendFuseOnCommand(uint8_t fuseNr) const = 0;
virtual ReturnValue_t sendFuseOnCommand(uint8_t fuseNr) = 0;
/**
* get the state of the Switches.
@ -51,7 +52,7 @@ class PowerSwitchIF : public HasReturnvaluesIF {
* - @c SWITCH_OFF if the specified switch is off.
* - @c RETURN_FAILED if an error occured
*/
virtual ReturnValue_t getSwitchState(uint8_t switchNr) const = 0;
virtual ReturnValue_t getSwitchState(power::Switch_t switchNr) const = 0;
/**
* get state of a fuse.
* @param fuseNr

View File

@ -1,19 +1,12 @@
#include "fsfw/power/PowerSwitcher.h"
#include "definitions.h"
#include "fsfw/objectmanager/ObjectManager.h"
#include "fsfw/serviceinterface/ServiceInterface.h"
PowerSwitcher::PowerSwitcher(uint8_t setSwitch1, uint8_t setSwitch2,
PowerSwitcher::State_t setStartState)
: state(setStartState), firstSwitch(setSwitch1), secondSwitch(setSwitch2) {}
ReturnValue_t PowerSwitcher::initialize(object_id_t powerSwitchId) {
power = ObjectManager::instance()->get<PowerSwitchIF>(powerSwitchId);
if (power == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
return HasReturnvaluesIF::RETURN_OK;
}
PowerSwitcher::PowerSwitcher(PowerSwitchIF* switcher, power::Switch_t setSwitch1,
power::Switch_t setSwitch2, PowerSwitcher::State_t setStartState)
: power(switcher), state(setStartState), firstSwitch(setSwitch1), secondSwitch(setSwitch2) {}
ReturnValue_t PowerSwitcher::getStateOfSwitches() {
SwitchReturn_t result = howManySwitches();
@ -52,18 +45,37 @@ void PowerSwitcher::commandSwitches(ReturnValue_t onOff) {
return;
}
void PowerSwitcher::turnOn() {
void PowerSwitcher::turnOn(bool checkCurrentState) {
if (checkCurrentState) {
if (getStateOfSwitches() == PowerSwitchIF::SWITCH_ON) {
state = SWITCH_IS_ON;
return;
}
}
commandSwitches(PowerSwitchIF::SWITCH_ON);
state = WAIT_ON;
}
void PowerSwitcher::turnOff() {
void PowerSwitcher::turnOff(bool checkCurrentState) {
if (checkCurrentState) {
if (getStateOfSwitches() == PowerSwitchIF::SWITCH_OFF) {
state = SWITCH_IS_OFF;
return;
}
}
commandSwitches(PowerSwitchIF::SWITCH_OFF);
state = WAIT_OFF;
}
bool PowerSwitcher::active() {
if (state == WAIT_OFF or state == WAIT_ON) {
return true;
}
return false;
}
PowerSwitcher::SwitchReturn_t PowerSwitcher::howManySwitches() {
if (secondSwitch == NO_SWITCH) {
if (secondSwitch == power::NO_SWITCH) {
return ONE_SWITCH;
} else {
return TWO_SWITCHES;

View File

@ -14,28 +14,29 @@ class PowerSwitcher : public HasReturnvaluesIF {
SWITCH_IS_OFF,
SWITCH_IS_ON,
};
State_t state;
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(uint8_t setSwitch1, uint8_t setSwitch2 = NO_SWITCH,
PowerSwitcher(PowerSwitchIF* switcher, power::Switch_t setSwitch1,
power::Switch_t setSwitch2 = power::NO_SWITCH,
State_t setStartState = SWITCH_IS_OFF);
ReturnValue_t initialize(object_id_t powerSwitchId);
void turnOn();
void turnOff();
void turnOn(bool checkCurrentState = true);
void turnOff(bool checkCurrentState = true);
bool active();
void doStateMachine();
State_t getState();
ReturnValue_t checkSwitchState();
uint32_t getSwitchDelay();
uint8_t getFirstSwitch() const;
uint8_t getSecondSwitch() const;
power::Switch_t getFirstSwitch() const;
power::Switch_t getSecondSwitch() const;
private:
uint8_t firstSwitch;
uint8_t secondSwitch;
PowerSwitchIF* power = nullptr;
State_t state;
power::Switch_t firstSwitch = power::NO_SWITCH;
power::Switch_t secondSwitch = power::NO_SWITCH;
static const uint8_t NO_SWITCH = 0xFF;
enum SwitchReturn_t { ONE_SWITCH = 1, TWO_SWITCHES = 2 };
ReturnValue_t getStateOfSwitches();
void commandSwitches(ReturnValue_t onOff);

View File

@ -0,0 +1,13 @@
#ifndef FSFW_SRC_FSFW_POWER_DEFINITIONS_H_
#define FSFW_SRC_FSFW_POWER_DEFINITIONS_H_
#include <cstdint>
namespace power {
using Switch_t = uint8_t;
static constexpr Switch_t NO_SWITCH = 0xFF;
} // namespace power
#endif /* FSFW_SRC_FSFW_POWER_DEFINITIONS_H_ */

View File

@ -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)

View File

@ -0,0 +1,3 @@
target_sources(${FSFW_TEST_TGT} PRIVATE
PowerSwitcherMock.cpp
)

View 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));
}

View 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_ */

View File

@ -0,0 +1,3 @@
target_sources(${FSFW_TEST_TGT} PRIVATE
testPowerSwitcher.cpp
)

View File

@ -0,0 +1,73 @@
#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") {
}
}

View File

@ -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
};
}