Merge pull request 'timeval: Use system clock' (#126) from feature_timeval_use_sysclock into develop

Reviewed-on: eive/fsfw#126
This commit is contained in:
Robin Müller 2023-03-03 15:25:38 +01:00
commit 6e17e45506
7 changed files with 87 additions and 36 deletions

View File

@ -68,7 +68,7 @@ ReturnValue_t FaultCounter::getParameter(uint8_t domainId, uint8_t uniqueId,
parameterWrapper->set(faultCount); parameterWrapper->set(faultCount);
break; break;
case ParameterIds::TIMEOUT: case ParameterIds::TIMEOUT:
parameterWrapper->set(timer.timeout); parameterWrapper->set(timer.getTimeoutMs());
break; break;
default: default:
return INVALID_IDENTIFIER_ID; return INVALID_IDENTIFIER_ID;

View File

@ -26,11 +26,11 @@ class MutexGuard {
#if FSFW_VERBOSE_LEVEL >= 1 #if FSFW_VERBOSE_LEVEL >= 1
if (result == MutexIF::MUTEX_TIMEOUT) { if (result == MutexIF::MUTEX_TIMEOUT) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "MutexGuard: Lock of mutex failed with timeout of " << timeoutMs sif::error << "MutexGuard::" << context << ": Lock of mutex failed with timeout of "
<< " milliseconds!" << std::endl; << timeoutMs << " milliseconds!" << std::endl;
#else #else
sif::printError("MutexGuard: Lock of mutex failed with timeout of %lu milliseconds\n", sif::printError("MutexGuard::%s: Lock of mutex failed with timeout of %lu milliseconds\n",
timeoutMs); context, timeoutMs);
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ #endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
} else if (result != returnvalue::OK) { } else if (result != returnvalue::OK) {

View File

@ -79,11 +79,16 @@ ReturnValue_t Clock::getUptime(timeval* uptime) {
// TODO This is not posix compatible and delivers only seconds precision // TODO This is not posix compatible and delivers only seconds precision
// Linux specific file read but more precise. // Linux specific file read but more precise.
double uptimeSeconds; double uptimeSeconds;
if (std::ifstream("/proc/uptime", std::ios::in) >> uptimeSeconds) { std::ifstream ifile("/proc/uptime");
if (ifile.bad()) {
return returnvalue::FAILED;
}
if (ifile >> uptimeSeconds) {
uptime->tv_sec = uptimeSeconds; uptime->tv_sec = uptimeSeconds;
uptime->tv_usec = uptimeSeconds * (double)1e6 - (uptime->tv_sec * 1e6); uptime->tv_usec = uptimeSeconds * (double)1e6 - (uptime->tv_sec * 1e6);
return returnvalue::OK;
} }
return returnvalue::OK; return returnvalue::FAILED;
} }
// Wait for new FSFW Clock function delivering seconds uptime. // Wait for new FSFW Clock function delivering seconds uptime.

View File

@ -283,7 +283,7 @@ ReturnValue_t Heater::getParameter(uint8_t domainId, uint8_t uniqueId,
} }
switch (uniqueId) { switch (uniqueId) {
case 0: case 0:
parameterWrapper->set(heaterOnCountdown.timeout); parameterWrapper->set(heaterOnCountdown.getTimeoutMs());
break; break;
default: default:
return INVALID_IDENTIFIER_ID; return INVALID_IDENTIFIER_ID;

View File

@ -1,49 +1,62 @@
#include "fsfw/timemanager/Countdown.h" #include "fsfw/timemanager/Countdown.h"
Countdown::Countdown(uint32_t initialTimeout, bool startImmediately) : timeout(initialTimeout) { #include "fsfw/globalfunctions/timevalOperations.h"
Countdown::Countdown(uint32_t initialTimeout, bool startImmediately) {
if (startImmediately) { if (startImmediately) {
setTimeout(initialTimeout); setTimeout(initialTimeout);
} else { } else {
timeout = initialTimeout; timeout.tv_sec = initialTimeout / 1000;
timeout.tv_usec = (initialTimeout % 1000) * 1000;
} }
} }
Countdown::~Countdown() {} Countdown::~Countdown() = default;
ReturnValue_t Countdown::setTimeout(uint32_t milliseconds) { ReturnValue_t Countdown::setTimeout(uint32_t milliseconds) {
ReturnValue_t returnValue = Clock::getUptime(&startTime); timeout.tv_sec = milliseconds / 1000;
timeout = milliseconds; timeout.tv_usec = (milliseconds % 1000) * 1000;
return returnValue; return Clock::getClock_timeval(&startTime);
} }
bool Countdown::hasTimedOut() const { bool Countdown::hasTimedOut() const {
if (uint32_t(this->getCurrentTime() - startTime) >= timeout) { // Account for system clock going back in time.
if (getCurrentTime() < startTime) {
return true; return true;
} else {
return false;
} }
if (getCurrentTime() - startTime >= timeout) {
return true;
}
return false;
} }
bool Countdown::isBusy() const { return !hasTimedOut(); } bool Countdown::isBusy() const { return !hasTimedOut(); }
ReturnValue_t Countdown::resetTimer() { return setTimeout(timeout); } ReturnValue_t Countdown::resetTimer() { return setTimeoutTv(timeout); }
void Countdown::timeOut() { startTime = this->getCurrentTime() - timeout; } void Countdown::timeOut() { startTime = this->getCurrentTime() - timeout; }
uint32_t Countdown::getRemainingMillis() const { uint32_t Countdown::getRemainingMillis() const {
// We fetch the time before the if-statement
// to be sure that the return is in
// range 0 <= number <= timeout
uint32_t currentTime = this->getCurrentTime();
if (this->hasTimedOut()) { if (this->hasTimedOut()) {
return 0; return 0;
} else {
return (startTime + timeout) - currentTime;
} }
timeval remainingMillisTv = (startTime + timeout) - this->getCurrentTime();
return remainingMillisTv.tv_sec * 1000 + remainingMillisTv.tv_usec / 1000;
} }
uint32_t Countdown::getCurrentTime() const { uint32_t Countdown::timevalToMs(timeval &tv) { return tv.tv_sec * 1000 + tv.tv_usec / 1000; }
uint32_t currentTime;
Clock::getUptime(&currentTime); ReturnValue_t Countdown::setTimeoutTv(timeval tv) {
timeout = tv;
return Clock::getClock_timeval(&startTime);
}
uint32_t Countdown::getTimeoutMs() const { return timeout.tv_sec * 1000 + timeout.tv_usec / 1000; }
timeval Countdown::getTimeout() const { return timeout; }
timeval Countdown::getCurrentTime() const {
timeval currentTime{};
Clock::getClock_timeval(&currentTime);
return currentTime; return currentTime;
} }

View File

@ -6,6 +6,10 @@
/** /**
* *
* Countdown keeps track of a timespan. * Countdown keeps track of a timespan.
* This class uses the system clock internally to achieve
* a high resolution. This means that the API is only partially
* resistant against time jumps. The user must take care to account
* for time jumps in some from if this relevant.
* *
* Countdown::resetTimer restarts the timer. * Countdown::resetTimer restarts the timer.
* Countdown::setTimeout sets a new countdown duration and resets. * Countdown::setTimeout sets a new countdown duration and resets.
@ -39,6 +43,8 @@ class Countdown {
* @return Returnvalue from Clock::getUptime * @return Returnvalue from Clock::getUptime
*/ */
ReturnValue_t setTimeout(uint32_t milliseconds); ReturnValue_t setTimeout(uint32_t milliseconds);
ReturnValue_t setTimeoutTv(timeval tv);
/** /**
* Returns true if the countdown duration has passed. * Returns true if the countdown duration has passed.
* *
@ -61,22 +67,31 @@ class Countdown {
* Returns the remaining milliseconds (0 if timeout) * Returns the remaining milliseconds (0 if timeout)
*/ */
uint32_t getRemainingMillis() const; uint32_t getRemainingMillis() const;
uint32_t getTimeoutMs() const;
timeval getTimeout() const;
/** /**
* Makes hasTimedOut() return true * Makes hasTimedOut() return true
*/ */
void timeOut(); void timeOut();
/**
* Internal countdown duration in milliseconds static inline uint32_t timevalToMs(timeval& tv);
*/
uint32_t timeout;
private: private:
/** /**
* Last time the timer was started (uptime) * Start time of the countdown.
*/ */
uint32_t startTime = 0; timeval startTime{};
uint32_t getCurrentTime() const; /**
* Timeout as timeval type. The countdown has timed out when the
* current time exceeds the start time plus the timeout.
*/
timeval timeout{};
timeval getCurrentTime() const;
}; };
#endif /* FSFW_TIMEMANAGER_COUNTDOWN_H_ */ #endif /* FSFW_TIMEMANAGER_COUNTDOWN_H_ */

View File

@ -1,15 +1,18 @@
#include <fsfw/timemanager/Countdown.h> #include <fsfw/timemanager/Countdown.h>
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <chrono>
#include <thread>
#include "CatchDefinitions.h" #include "CatchDefinitions.h"
static constexpr bool TEST_LONGER_CD = false;
TEST_CASE("Countdown Tests", "[TestCountdown]") { TEST_CASE("Countdown Tests", "[TestCountdown]") {
INFO("Countdown Tests"); INFO("Countdown Tests");
Countdown count(20); Countdown count(20);
REQUIRE(count.timeout == 20); REQUIRE(count.getTimeoutMs() == 20);
REQUIRE(count.setTimeout(100) == static_cast<uint16_t>(returnvalue::OK)); REQUIRE(count.setTimeout(100) == static_cast<uint16_t>(returnvalue::OK));
REQUIRE(count.timeout == 100); REQUIRE(count.getTimeoutMs() == 100);
REQUIRE(count.setTimeout(150) == static_cast<uint16_t>(returnvalue::OK)); REQUIRE(count.setTimeout(150) == static_cast<uint16_t>(returnvalue::OK));
REQUIRE(count.isBusy()); REQUIRE(count.isBusy());
REQUIRE(not count.hasTimedOut()); REQUIRE(not count.hasTimedOut());
@ -25,4 +28,19 @@ TEST_CASE("Countdown Tests", "[TestCountdown]") {
count.resetTimer(); count.resetTimer();
REQUIRE(not count.hasTimedOut()); REQUIRE(not count.hasTimedOut());
REQUIRE(count.isBusy()); REQUIRE(count.isBusy());
count.setTimeout(100);
REQUIRE(not count.hasTimedOut());
std::this_thread::sleep_for(std::chrono::milliseconds(50));
REQUIRE(not count.hasTimedOut());
std::this_thread::sleep_for(std::chrono::milliseconds(50));
REQUIRE(count.hasTimedOut());
// Takes longer, disabled by default
if (TEST_LONGER_CD) {
count.setTimeout(2500);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
REQUIRE(not count.hasTimedOut());
std::this_thread::sleep_for(std::chrono::milliseconds(1500));
REQUIRE(count.hasTimedOut());
}
} }