timeval: Use system clock #126
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
|
@ -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(¤tTime);
|
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(¤tTime);
|
||||||
return currentTime;
|
return currentTime;
|
||||||
}
|
}
|
||||||
|
@ -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_ */
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user