Merge branch 'development' into mueller/possible-ring-buffer-fix

This commit is contained in:
Robin Müller 2022-04-27 08:42:24 +02:00
commit d3e7037759
32 changed files with 437 additions and 117 deletions

View File

@ -31,6 +31,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
creation call. It allows passing context information and an arbitrary user argument into 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 the message queue. Also streamlined and simplified `MessageQueue` implementation for all OSALs
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/583 PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/583
- Clock:
- `timeval` to `TimeOfDay_t`
- Added Mutex for gmtime calls: (compare http://www.opengate.at/blog/2020/01/timeless/)
- Moved the statics used by Clock in ClockCommon.cpp to this file
- Better check for leap seconds
- Added Unittests for Clock (only getter)
## Removed ## Removed
@ -51,6 +57,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Small bugfix in STM32 HAL for SPI - Small bugfix in STM32 HAL for SPI
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/599 PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/599
- HAL GPIO: Improved error checking in `LinuxLibgpioIF::configureGpios(...)`. If a GPIO
configuration fails, the function will exit prematurely with a dedicated error code
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/602
# [v4.0.0] # [v4.0.0]

View File

@ -1,9 +1,9 @@
#include "MgmLIS3MDLHandler.h" #include "MgmLIS3MDLHandler.h"
#include "fsfw/datapool/PoolReadGuard.h"
#include <cmath> #include <cmath>
#include "fsfw/datapool/PoolReadGuard.h"
MgmLIS3MDLHandler::MgmLIS3MDLHandler(object_id_t objectId, object_id_t deviceCommunication, MgmLIS3MDLHandler::MgmLIS3MDLHandler(object_id_t objectId, object_id_t deviceCommunication,
CookieIF *comCookie, uint32_t transitionDelay) CookieIF *comCookie, uint32_t transitionDelay)
: DeviceHandlerBase(objectId, deviceCommunication, comCookie), : DeviceHandlerBase(objectId, deviceCommunication, comCookie),

View File

@ -44,6 +44,7 @@ ReturnValue_t LinuxLibgpioIF::addGpios(GpioCookie* gpioCookie) {
} }
ReturnValue_t LinuxLibgpioIF::configureGpios(GpioMap& mapToAdd) { ReturnValue_t LinuxLibgpioIF::configureGpios(GpioMap& mapToAdd) {
ReturnValue_t result = RETURN_OK;
for (auto& gpioConfig : mapToAdd) { for (auto& gpioConfig : mapToAdd) {
auto& gpioType = gpioConfig.second->gpioType; auto& gpioType = gpioConfig.second->gpioType;
switch (gpioType) { switch (gpioType) {
@ -55,7 +56,7 @@ ReturnValue_t LinuxLibgpioIF::configureGpios(GpioMap& mapToAdd) {
if (regularGpio == nullptr) { if (regularGpio == nullptr) {
return GPIO_INVALID_INSTANCE; return GPIO_INVALID_INSTANCE;
} }
configureGpioByChip(gpioConfig.first, *regularGpio); result = configureGpioByChip(gpioConfig.first, *regularGpio);
break; break;
} }
case (gpio::GpioTypes::GPIO_REGULAR_BY_LABEL): { case (gpio::GpioTypes::GPIO_REGULAR_BY_LABEL): {
@ -63,7 +64,7 @@ ReturnValue_t LinuxLibgpioIF::configureGpios(GpioMap& mapToAdd) {
if (regularGpio == nullptr) { if (regularGpio == nullptr) {
return GPIO_INVALID_INSTANCE; return GPIO_INVALID_INSTANCE;
} }
configureGpioByLabel(gpioConfig.first, *regularGpio); result = configureGpioByLabel(gpioConfig.first, *regularGpio);
break; break;
} }
case (gpio::GpioTypes::GPIO_REGULAR_BY_LINE_NAME): { case (gpio::GpioTypes::GPIO_REGULAR_BY_LINE_NAME): {
@ -71,7 +72,7 @@ ReturnValue_t LinuxLibgpioIF::configureGpios(GpioMap& mapToAdd) {
if (regularGpio == nullptr) { if (regularGpio == nullptr) {
return GPIO_INVALID_INSTANCE; return GPIO_INVALID_INSTANCE;
} }
configureGpioByLineName(gpioConfig.first, *regularGpio); result = configureGpioByLineName(gpioConfig.first, *regularGpio);
break; break;
} }
case (gpio::GpioTypes::CALLBACK): { case (gpio::GpioTypes::CALLBACK): {
@ -83,8 +84,11 @@ ReturnValue_t LinuxLibgpioIF::configureGpios(GpioMap& mapToAdd) {
gpioCallback->initValue, gpioCallback->callbackArgs); gpioCallback->initValue, gpioCallback->callbackArgs);
} }
} }
if (result != RETURN_OK) {
return GPIO_INIT_FAILED;
} }
return RETURN_OK; }
return result;
} }
ReturnValue_t LinuxLibgpioIF::configureGpioByLabel(gpioId_t gpioId, ReturnValue_t LinuxLibgpioIF::configureGpioByLabel(gpioId_t gpioId,

View File

@ -29,6 +29,8 @@ class LinuxLibgpioIF : public GpioIF, public SystemObject {
HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 4); HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 4);
static constexpr ReturnValue_t GPIO_DUPLICATE_DETECTED = static constexpr ReturnValue_t GPIO_DUPLICATE_DETECTED =
HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 5); HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 5);
static constexpr ReturnValue_t GPIO_INIT_FAILED =
HasReturnvaluesIF::makeReturnCode(gpioRetvalId, 6);
LinuxLibgpioIF(object_id_t objectId); LinuxLibgpioIF(object_id_t objectId);
virtual ~LinuxLibgpioIF(); virtual ~LinuxLibgpioIF();

View File

@ -24,9 +24,7 @@ void UartCookie::setParityEven() { parity = Parity::EVEN; }
Parity UartCookie::getParity() const { return parity; } Parity UartCookie::getParity() const { return parity; }
void UartCookie::setBitsPerWord(BitsPerWord bitsPerWord_) { void UartCookie::setBitsPerWord(BitsPerWord bitsPerWord_) { bitsPerWord = bitsPerWord_; }
bitsPerWord = bitsPerWord_;
}
BitsPerWord UartCookie::getBitsPerWord() const { return bitsPerWord; } BitsPerWord UartCookie::getBitsPerWord() const { return bitsPerWord; }

View File

@ -459,8 +459,7 @@ size_t DeviceHandlerBase::getNextReplyLength(DeviceCommandId_t commandId) {
DeviceCommandMap::iterator command = cookieInfo.pendingCommand; DeviceCommandMap::iterator command = cookieInfo.pendingCommand;
if (command->second.useAlternativeReplyId) { if (command->second.useAlternativeReplyId) {
replyId = command->second.alternativeReplyId; replyId = command->second.alternativeReplyId;
} } else {
else {
replyId = commandId; replyId = commandId;
} }
DeviceReplyIter iter = deviceReplyMap.find(replyId); DeviceReplyIter iter = deviceReplyMap.find(replyId);

View File

@ -1,7 +1,7 @@
#include "MessageQueueBase.h" #include "MessageQueueBase.h"
MessageQueueBase::MessageQueueBase(MessageQueueId_t id, MessageQueueId_t defaultDest, MessageQueueBase::MessageQueueBase(MessageQueueId_t id, MessageQueueId_t defaultDest, MqArgs* args)
MqArgs* args): id(id) { : id(id) {
this->defaultDest = defaultDest; this->defaultDest = defaultDest;
if (args != nullptr) { if (args != nullptr) {
this->args = *args; this->args = *args;
@ -29,29 +29,19 @@ ReturnValue_t MessageQueueBase::receiveMessage(MessageQueueMessageIF* message,
return status; return status;
} }
MessageQueueId_t MessageQueueBase::getLastPartner() const { MessageQueueId_t MessageQueueBase::getLastPartner() const { return last; }
return last;
}
MessageQueueId_t MessageQueueBase::getId() const { MessageQueueId_t MessageQueueBase::getId() const { return id; }
return id;
}
MqArgs& MessageQueueBase::getMqArgs() { MqArgs& MessageQueueBase::getMqArgs() { return args; }
return args;
}
void MessageQueueBase::setDefaultDestination(MessageQueueId_t defaultDestination) { void MessageQueueBase::setDefaultDestination(MessageQueueId_t defaultDestination) {
this->defaultDest = defaultDestination; this->defaultDest = defaultDestination;
} }
MessageQueueId_t MessageQueueBase::getDefaultDestination() const { MessageQueueId_t MessageQueueBase::getDefaultDestination() const { return defaultDest; }
return defaultDest;
}
bool MessageQueueBase::isDefaultDestinationSet() const { bool MessageQueueBase::isDefaultDestinationSet() const { return (defaultDest != NO_QUEUE); }
return (defaultDest != NO_QUEUE);
}
ReturnValue_t MessageQueueBase::sendMessage(MessageQueueId_t sendTo, MessageQueueMessageIF* message, ReturnValue_t MessageQueueBase::sendMessage(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
bool ignoreFault) { bool ignoreFault) {

View File

@ -1,8 +1,8 @@
#ifndef FSFW_SRC_FSFW_IPC_MESSAGEQUEUEBASE_H_ #ifndef FSFW_SRC_FSFW_IPC_MESSAGEQUEUEBASE_H_
#define FSFW_SRC_FSFW_IPC_MESSAGEQUEUEBASE_H_ #define FSFW_SRC_FSFW_IPC_MESSAGEQUEUEBASE_H_
#include <fsfw/ipc/definitions.h>
#include <fsfw/ipc/MessageQueueIF.h> #include <fsfw/ipc/MessageQueueIF.h>
#include <fsfw/ipc/definitions.h>
class MessageQueueBase : public MessageQueueIF { class MessageQueueBase : public MessageQueueIF {
public: public:
@ -22,13 +22,14 @@ public:
virtual ReturnValue_t reply(MessageQueueMessageIF* message) override; virtual ReturnValue_t reply(MessageQueueMessageIF* message) override;
virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message, virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message,
MessageQueueId_t* receivedFrom) override; MessageQueueId_t* receivedFrom) override;
virtual ReturnValue_t sendToDefaultFrom(MessageQueueMessageIF* message, virtual ReturnValue_t sendToDefaultFrom(MessageQueueMessageIF* message, MessageQueueId_t sentFrom,
MessageQueueId_t sentFrom, bool ignoreFault = false) override; bool ignoreFault = false) override;
// OSAL specific, forward the abstract function // OSAL specific, forward the abstract function
virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message) = 0; virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message) = 0;
virtual ReturnValue_t sendMessageFrom(MessageQueueId_t sendTo, MessageQueueMessageIF* message, virtual ReturnValue_t sendMessageFrom(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
MessageQueueId_t sentFrom, bool ignoreFault = false) = 0; MessageQueueId_t sentFrom, bool ignoreFault = false) = 0;
protected: protected:
MessageQueueId_t id = MessageQueueIF::NO_QUEUE; MessageQueueId_t id = MessageQueueIF::NO_QUEUE;
MessageQueueId_t last = MessageQueueIF::NO_QUEUE; MessageQueueId_t last = MessageQueueIF::NO_QUEUE;
@ -36,6 +37,4 @@ protected:
MqArgs args = {}; MqArgs args = {};
}; };
#endif /* FSFW_SRC_FSFW_IPC_MESSAGEQUEUEBASE_H_ */ #endif /* FSFW_SRC_FSFW_IPC_MESSAGEQUEUEBASE_H_ */

View File

@ -2,6 +2,7 @@
#define FSFW_IPC_MESSAGEQUEUEIF_H_ #define FSFW_IPC_MESSAGEQUEUEIF_H_
#include <fsfw/ipc/definitions.h> #include <fsfw/ipc/definitions.h>
#include <cstdint> #include <cstdint>
#include "../returnvalues/HasReturnvaluesIF.h" #include "../returnvalues/HasReturnvaluesIF.h"
@ -45,7 +46,8 @@ class MessageQueueIF {
virtual ReturnValue_t reply(MessageQueueMessageIF* message) = 0; virtual ReturnValue_t reply(MessageQueueMessageIF* message) = 0;
/** /**
* @brief This function reads available messages from the message queue and returns the sender. * @brief This function reads available messages from the message queue and returns the
* sender.
* @details * @details
* It works identically to the other receiveMessage call, but in addition * It works identically to the other receiveMessage call, but in addition
* returns the sender's queue id. * returns the sender's queue id.

View File

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

View File

@ -2,6 +2,7 @@
#define FSFW_OSAL_FREERTOS_MESSAGEQUEUE_H_ #define FSFW_OSAL_FREERTOS_MESSAGEQUEUE_H_
#include <fsfw/ipc/MessageQueueBase.h> #include <fsfw/ipc/MessageQueueBase.h>
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include "TaskManagement.h" #include "TaskManagement.h"
#include "fsfw/internalerror/InternalErrorReporterIF.h" #include "fsfw/internalerror/InternalErrorReporterIF.h"

View File

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

View File

@ -1,17 +1,17 @@
#ifndef FRAMEWORK_OSAL_HOST_MESSAGEQUEUE_H_ #ifndef FRAMEWORK_OSAL_HOST_MESSAGEQUEUE_H_
#define FRAMEWORK_OSAL_HOST_MESSAGEQUEUE_H_ #define FRAMEWORK_OSAL_HOST_MESSAGEQUEUE_H_
#include "fsfw/ipc/MessageQueueBase.h" #include <memory>
#include <queue>
#include "fsfw/internalerror/InternalErrorReporterIF.h" #include "fsfw/internalerror/InternalErrorReporterIF.h"
#include "fsfw/ipc/MessageQueueBase.h"
#include "fsfw/ipc/MessageQueueIF.h" #include "fsfw/ipc/MessageQueueIF.h"
#include "fsfw/ipc/MessageQueueMessage.h" #include "fsfw/ipc/MessageQueueMessage.h"
#include "fsfw/ipc/MutexIF.h" #include "fsfw/ipc/MutexIF.h"
#include "fsfw/ipc/definitions.h" #include "fsfw/ipc/definitions.h"
#include "fsfw/timemanager/Clock.h" #include "fsfw/timemanager/Clock.h"
#include <memory>
#include <queue>
/** /**
* @brief This class manages sending and receiving of * @brief This class manages sending and receiving of
* message queue messages. * message queue messages.

View File

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

View File

@ -61,8 +61,7 @@ class MessageQueue : public MessageQueueBase {
ReturnValue_t receiveMessage(MessageQueueMessageIF* message) override; ReturnValue_t receiveMessage(MessageQueueMessageIF* message) override;
ReturnValue_t flush(uint32_t* count) override; ReturnValue_t flush(uint32_t* count) override;
ReturnValue_t sendMessageFrom(MessageQueueId_t sendTo, MessageQueueMessageIF* message, ReturnValue_t sendMessageFrom(MessageQueueId_t sendTo, MessageQueueMessageIF* message,
MessageQueueId_t sentFrom, MessageQueueId_t sentFrom, bool ignoreFault = false) override;
bool ignoreFault = false) override;
protected: protected:
/** /**

View File

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

View File

@ -2,6 +2,7 @@
#define FSFW_OSAL_RTEMS_MESSAGEQUEUE_H_ #define FSFW_OSAL_RTEMS_MESSAGEQUEUE_H_
#include <fsfw/ipc/MessageQueueBase.h> #include <fsfw/ipc/MessageQueueBase.h>
#include "RtemsBasic.h" #include "RtemsBasic.h"
#include "fsfw/internalerror/InternalErrorReporterIF.h" #include "fsfw/internalerror/InternalErrorReporterIF.h"
#include "fsfw/ipc/MessageQueueIF.h" #include "fsfw/ipc/MessageQueueIF.h"

View File

@ -67,7 +67,6 @@ class PeriodicTask : public RTEMSTaskBase, public PeriodicTaskIF {
*/ */
ReturnValue_t addComponent(ExecutableObjectIF *object) override; ReturnValue_t addComponent(ExecutableObjectIF *object) override;
uint32_t getPeriodMs() const override; uint32_t getPeriodMs() const override;
ReturnValue_t sleepFor(uint32_t ms) override; ReturnValue_t sleepFor(uint32_t ms) override;

View File

@ -91,7 +91,7 @@ ReturnValue_t CCSDSTime::convertFromCDS(Clock::TimeOfDay_t* to, const uint8_t* f
if (result != HasReturnvaluesIF::RETURN_OK) { if (result != HasReturnvaluesIF::RETURN_OK) {
return result; return result;
} }
return convertTimevalToTimeOfDay(to, &time); return Clock::convertTimevalToTimeOfDay(&time, to);
} }
ReturnValue_t CCSDSTime::convertFromCCS(Clock::TimeOfDay_t* to, const uint8_t* from, ReturnValue_t CCSDSTime::convertFromCCS(Clock::TimeOfDay_t* to, const uint8_t* from,
@ -489,11 +489,6 @@ ReturnValue_t CCSDSTime::checkTimeOfDay(const Clock::TimeOfDay_t* time) {
return RETURN_OK; return RETURN_OK;
} }
ReturnValue_t CCSDSTime::convertTimevalToTimeOfDay(Clock::TimeOfDay_t* to, timeval* from) {
// This is rather tricky. Implement only if needed. Also, if so, move to OSAL.
return UNSUPPORTED_TIME_FORMAT;
}
ReturnValue_t CCSDSTime::convertFromCDS(timeval* to, const uint8_t* from, size_t* foundLength, ReturnValue_t CCSDSTime::convertFromCDS(timeval* to, const uint8_t* from, size_t* foundLength,
size_t maxLength) { size_t maxLength) {
uint8_t pField = *from; uint8_t pField = *from;
@ -583,7 +578,7 @@ ReturnValue_t CCSDSTime::convertFromCDS(Clock::TimeOfDay_t* to, const CCSDSTime:
if (result != HasReturnvaluesIF::RETURN_OK) { if (result != HasReturnvaluesIF::RETURN_OK) {
return result; return result;
} }
return CCSDSTime::convertTimevalToTimeOfDay(to, &tempTimeval); return Clock::convertTimevalToTimeOfDay(&tempTimeval, to);
} }
ReturnValue_t CCSDSTime::convertFromCUC(timeval* to, uint8_t pField, const uint8_t* from, ReturnValue_t CCSDSTime::convertFromCUC(timeval* to, uint8_t pField, const uint8_t* from,

View File

@ -223,7 +223,6 @@ class CCSDSTime : public HasReturnvaluesIF {
uint8_t *day); uint8_t *day);
static bool isLeapYear(uint32_t year); static bool isLeapYear(uint32_t year);
static ReturnValue_t convertTimevalToTimeOfDay(Clock::TimeOfDay_t *to, timeval *from);
}; };
#endif /* FSFW_TIMEMANAGER_CCSDSTIME_H_ */ #endif /* FSFW_TIMEMANAGER_CCSDSTIME_H_ */

View File

@ -99,6 +99,13 @@ class Clock {
*/ */
static ReturnValue_t getDateAndTime(TimeOfDay_t *time); static ReturnValue_t getDateAndTime(TimeOfDay_t *time);
/**
* Convert to time of day struct given the POSIX timeval struct
* @param from
* @param to
* @return
*/
static ReturnValue_t convertTimevalToTimeOfDay(const timeval *from, TimeOfDay_t *to);
/** /**
* Converts a time of day struct to POSIX seconds. * Converts a time of day struct to POSIX seconds.
* @param time The time of day as input * @param time The time of day as input
@ -166,6 +173,7 @@ class Clock {
static MutexIF *timeMutex; static MutexIF *timeMutex;
static uint16_t leapSeconds; static uint16_t leapSeconds;
static bool leapSecondsSet;
}; };
#endif /* FSFW_TIMEMANAGER_CLOCK_H_ */ #endif /* FSFW_TIMEMANAGER_CLOCK_H_ */

View File

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

View File

@ -1,8 +1,9 @@
#include "version.h" #include "version.h"
#include "fsfw/FSFWVersion.h"
#include <cstdio> #include <cstdio>
#include "fsfw/FSFWVersion.h"
#ifdef major #ifdef major
#undef major #undef major
#endif #endif

View File

@ -3,4 +3,5 @@ target_sources(${FSFW_TEST_TGT} PRIVATE
testOpDivider.cpp testOpDivider.cpp
testBitutil.cpp testBitutil.cpp
testCRC.cpp testCRC.cpp
testTimevalOperations.cpp
) )

View File

@ -0,0 +1,124 @@
#include <fsfw/globalfunctions/timevalOperations.h>
#include <catch2/catch_approx.hpp>
#include <catch2/catch_test_macros.hpp>
#include "fsfw_tests/unit/CatchDefinitions.h"
TEST_CASE("TimevalTest", "[timevalOperations]") {
SECTION("Comparison") {
timeval t1;
t1.tv_sec = 1648227422;
t1.tv_usec = 123456;
timeval t2;
t2.tv_sec = 1648227422;
t2.tv_usec = 123456;
REQUIRE(t1 == t2);
REQUIRE(t2 == t1);
REQUIRE_FALSE(t1 != t2);
REQUIRE_FALSE(t2 != t1);
REQUIRE(t1 <= t2);
REQUIRE(t2 <= t1);
REQUIRE(t1 >= t2);
REQUIRE(t2 >= t1);
REQUIRE_FALSE(t1 < t2);
REQUIRE_FALSE(t2 < t1);
REQUIRE_FALSE(t1 > t2);
REQUIRE_FALSE(t2 > t1);
timeval t3;
t3.tv_sec = 1648227422;
t3.tv_usec = 123457;
REQUIRE_FALSE(t1 == t3);
REQUIRE(t1 != t3);
REQUIRE(t1 <= t3);
REQUIRE_FALSE(t3 <= t1);
REQUIRE_FALSE(t1 >= t3);
REQUIRE(t3 >= t1);
REQUIRE(t1 < t3);
REQUIRE_FALSE(t3 < t1);
REQUIRE_FALSE(t1 > t3);
REQUIRE(t3 > t1);
timeval t4;
t4.tv_sec = 1648227423;
t4.tv_usec = 123456;
REQUIRE_FALSE(t1 == t4);
REQUIRE(t1 != t4);
REQUIRE(t1 <= t4);
REQUIRE_FALSE(t4 <= t1);
REQUIRE_FALSE(t1 >= t4);
REQUIRE(t4 >= t1);
REQUIRE(t1 < t4);
REQUIRE_FALSE(t4 < t1);
REQUIRE_FALSE(t1 > t4);
REQUIRE(t4 > t1);
}
SECTION("Operators") {
timeval t1;
t1.tv_sec = 1648227422;
t1.tv_usec = 123456;
timeval t2;
t2.tv_sec = 1648227422;
t2.tv_usec = 123456;
timeval t3 = t1 - t2;
REQUIRE(t3.tv_sec == 0);
REQUIRE(t3.tv_usec == 0);
timeval t4 = t1 - t3;
REQUIRE(t4.tv_sec == 1648227422);
REQUIRE(t4.tv_usec == 123456);
timeval t5 = t3 - t1;
REQUIRE(t5.tv_sec == -1648227422);
REQUIRE(t5.tv_usec == -123456);
timeval t6;
t6.tv_sec = 1648227400;
t6.tv_usec = 999999;
timeval t7 = t6 + t1;
REQUIRE(t7.tv_sec == (1648227422ull + 1648227400ull + 1ull));
REQUIRE(t7.tv_usec == 123455);
timeval t8 = t1 - t6;
REQUIRE(t8.tv_sec == 1648227422 - 1648227400 - 1);
REQUIRE(t8.tv_usec == 123457);
double scalar = 2;
timeval t9 = t1 * scalar;
REQUIRE(t9.tv_sec == 3296454844);
REQUIRE(t9.tv_usec == 246912);
timeval t10 = scalar * t1;
REQUIRE(t10.tv_sec == 3296454844);
REQUIRE(t10.tv_usec == 246912);
timeval t11 = t6 * scalar;
REQUIRE(t11.tv_sec == (3296454800 + 1));
REQUIRE(t11.tv_usec == 999998);
timeval t12 = t1 / scalar;
REQUIRE(t12.tv_sec == 824113711);
REQUIRE(t12.tv_usec == 61728);
timeval t13 = t6 / scalar;
REQUIRE(t13.tv_sec == 824113700);
// Rounding issue
REQUIRE(t13.tv_usec == 499999);
double scalar2 = t9 / t1;
REQUIRE(scalar2 == Catch::Approx(2.0));
double scalar3 = t1 / t6;
REQUIRE(scalar3 == Catch::Approx(1.000000013));
double scalar4 = t3 / t1;
REQUIRE(scalar4 == Catch::Approx(0));
double scalar5 = t12 / t1;
REQUIRE(scalar5 == Catch::Approx(0.5));
}
SECTION("timevalOperations::toTimeval") {
double seconds = 1648227422.123456;
timeval t1 = timevalOperations::toTimeval(seconds);
REQUIRE(t1.tv_sec == 1648227422);
// Allow 1 usec rounding tolerance
REQUIRE(t1.tv_usec >= 123455);
REQUIRE(t1.tv_usec <= 123457);
}
}

View File

@ -4,8 +4,8 @@
#include <cstring> #include <cstring>
#include <queue> #include <queue>
#include "fsfw/ipc/MessageQueueBase.h"
#include "fsfw/ipc/CommandMessage.h" #include "fsfw/ipc/CommandMessage.h"
#include "fsfw/ipc/MessageQueueBase.h"
#include "fsfw/ipc/MessageQueueIF.h" #include "fsfw/ipc/MessageQueueIF.h"
#include "fsfw/ipc/MessageQueueMessage.h" #include "fsfw/ipc/MessageQueueMessage.h"
#include "fsfw_tests/unit/CatchDefinitions.h" #include "fsfw_tests/unit/CatchDefinitions.h"

View File

@ -1,4 +1,5 @@
target_sources(${FSFW_TEST_TGT} PRIVATE target_sources(${FSFW_TEST_TGT} PRIVATE
TestMessageQueue.cpp TestMessageQueue.cpp
TestSemaphore.cpp TestSemaphore.cpp
TestClock.cpp
) )

View File

@ -0,0 +1,86 @@
#include <fsfw/globalfunctions/timevalOperations.h>
#include <fsfw/timemanager/Clock.h>
#include <array>
#include <catch2/catch_approx.hpp>
#include <catch2/catch_test_macros.hpp>
#include "fsfw_tests/unit/CatchDefinitions.h"
TEST_CASE("OSAL::Clock Test", "[OSAL::Clock Test]") {
SECTION("Test getClock") {
timeval time;
ReturnValue_t result = Clock::getClock_timeval(&time);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
Clock::TimeOfDay_t timeOfDay;
result = Clock::getDateAndTime(&timeOfDay);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
timeval timeOfDayAsTimeval;
result = Clock::convertTimeOfDayToTimeval(&timeOfDay, &timeOfDayAsTimeval);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
// We require timeOfDayAsTimeval to be larger than time as it
// was request a few ns later
double difference = timevalOperations::toDouble(timeOfDayAsTimeval - time);
CHECK(difference >= 0.0);
CHECK(difference <= 0.005);
// Conversion in the other direction
Clock::TimeOfDay_t timevalAsTimeOfDay;
result = Clock::convertTimevalToTimeOfDay(&time, &timevalAsTimeOfDay);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
CHECK(timevalAsTimeOfDay.year <= timeOfDay.year);
// TODO We should write TimeOfDay operators!
}
SECTION("Leap seconds") {
uint16_t leapSeconds = 0;
ReturnValue_t result = Clock::getLeapSeconds(&leapSeconds);
REQUIRE(result == HasReturnvaluesIF::RETURN_FAILED);
REQUIRE(leapSeconds == 0);
result = Clock::setLeapSeconds(18);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
result = Clock::getLeapSeconds(&leapSeconds);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(leapSeconds == 18);
}
SECTION("usec Test") {
timeval timeAsTimeval;
ReturnValue_t result = Clock::getClock_timeval(&timeAsTimeval);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
uint64_t timeAsUsec = 0;
result = Clock::getClock_usecs(&timeAsUsec);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
double timeAsUsecDouble = static_cast<double>(timeAsUsec) / 1000000.0;
timeval timeAsUsecTimeval = timevalOperations::toTimeval(timeAsUsecDouble);
double difference = timevalOperations::toDouble(timeAsUsecTimeval - timeAsTimeval);
// We accept 5 ms difference
CHECK(difference >= 0.0);
CHECK(difference <= 0.005);
uint64_t timevalAsUint64 = static_cast<uint64_t>(timeAsTimeval.tv_sec) * 1000000ull +
static_cast<uint64_t>(timeAsTimeval.tv_usec);
CHECK((timeAsUsec - timevalAsUint64) >= 0);
CHECK((timeAsUsec - timevalAsUint64) <= (5 * 1000));
}
SECTION("Test j2000") {
double j2000;
timeval time;
time.tv_sec = 1648208539;
time.tv_usec = 0;
ReturnValue_t result = Clock::convertTimevalToJD2000(time, &j2000);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
double correctJ2000 = 2459663.98772 - 2451545.0;
CHECK(j2000 == Catch::Approx(correctJ2000).margin(1.2 * 1e-8));
}
SECTION("Convert to TT") {
timeval utcTime;
utcTime.tv_sec = 1648208539;
utcTime.tv_usec = 999000;
timeval tt;
ReturnValue_t result = Clock::setLeapSeconds(27);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
result = Clock::convertUTCToTT(utcTime, &tt);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
CHECK(tt.tv_usec == 183000);
// The plus 1 is a own forced overflow of usecs
CHECK(tt.tv_sec == (1648208539 + 27 + 10 + 32 + 1));
}
}

View File

@ -81,7 +81,8 @@ TEST_CASE("CCSDSTime Tests", "[TestCCSDSTime]") {
std::string timeAscii = "2022-12-31T23:59:59.123Z"; std::string timeAscii = "2022-12-31T23:59:59.123Z";
Clock::TimeOfDay_t timeTo; Clock::TimeOfDay_t timeTo;
const uint8_t* timeChar = reinterpret_cast<const uint8_t*>(timeAscii.c_str()); const uint8_t* timeChar = reinterpret_cast<const uint8_t*>(timeAscii.c_str());
CCSDSTime::convertFromASCII(&timeTo, timeChar, timeAscii.length()); auto result = CCSDSTime::convertFromASCII(&timeTo, timeChar, timeAscii.length());
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(timeTo.year == 2022); REQUIRE(timeTo.year == 2022);
REQUIRE(timeTo.month == 12); REQUIRE(timeTo.month == 12);
REQUIRE(timeTo.day == 31); REQUIRE(timeTo.day == 31);
@ -89,6 +90,19 @@ TEST_CASE("CCSDSTime Tests", "[TestCCSDSTime]") {
REQUIRE(timeTo.minute == 59); REQUIRE(timeTo.minute == 59);
REQUIRE(timeTo.second == 59); REQUIRE(timeTo.second == 59);
REQUIRE(timeTo.usecond == Catch::Approx(123000)); REQUIRE(timeTo.usecond == Catch::Approx(123000));
std::string timeAscii2 = "2022-365T23:59:59.123Z";
const uint8_t* timeChar2 = reinterpret_cast<const uint8_t*>(timeAscii2.c_str());
Clock::TimeOfDay_t timeTo2;
result = CCSDSTime::convertFromCcsds(&timeTo2, timeChar2, timeAscii2.length());
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(timeTo2.year == 2022);
REQUIRE(timeTo2.month == 12);
REQUIRE(timeTo2.day == 31);
REQUIRE(timeTo2.hour == 23);
REQUIRE(timeTo2.minute == 59);
REQUIRE(timeTo2.second == 59);
REQUIRE(timeTo2.usecond == Catch::Approx(123000));
} }
SECTION("CDS Conversions") { SECTION("CDS Conversions") {
@ -119,6 +133,7 @@ TEST_CASE("CCSDSTime Tests", "[TestCCSDSTime]") {
CHECK(cdsTime.msDay_h == 0xE0); CHECK(cdsTime.msDay_h == 0xE0);
CHECK(cdsTime.msDay_l == 0xC5); CHECK(cdsTime.msDay_l == 0xC5);
CHECK(cdsTime.msDay_ll == 0xC3); CHECK(cdsTime.msDay_ll == 0xC3);
CHECK(cdsTime.pField == CCSDSTime::P_FIELD_CDS_SHORT);
// Conversion back to timeval // Conversion back to timeval
timeval timeReturnAsTimeval; timeval timeReturnAsTimeval;
@ -128,5 +143,56 @@ TEST_CASE("CCSDSTime Tests", "[TestCCSDSTime]") {
timeval difference = timeAsTimeval - timeReturnAsTimeval; timeval difference = timeAsTimeval - timeReturnAsTimeval;
CHECK(difference.tv_usec == 456); CHECK(difference.tv_usec == 456);
CHECK(difference.tv_sec == 0); CHECK(difference.tv_sec == 0);
Clock::TimeOfDay_t timeReturnAsTimeOfDay;
result = CCSDSTime::convertFromCDS(&timeReturnAsTimeOfDay, &cdsTime);
CHECK(result == HasReturnvaluesIF::RETURN_OK);
CHECK(timeReturnAsTimeOfDay.year == 2020);
CHECK(timeReturnAsTimeOfDay.month == 2);
CHECK(timeReturnAsTimeOfDay.day == 29);
CHECK(timeReturnAsTimeOfDay.hour == 13);
CHECK(timeReturnAsTimeOfDay.minute == 24);
CHECK(timeReturnAsTimeOfDay.second == 45);
// micro seconds precision is lost
CHECK(timeReturnAsTimeOfDay.usecond == 123000);
Clock::TimeOfDay_t timeReturnAsTodFromBuffer;
const uint8_t* buffer = reinterpret_cast<const uint8_t*>(&cdsTime);
result = CCSDSTime::convertFromCDS(&timeReturnAsTodFromBuffer, buffer, sizeof(cdsTime));
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
CHECK(timeReturnAsTodFromBuffer.year == time.year);
CHECK(timeReturnAsTodFromBuffer.month == time.month);
CHECK(timeReturnAsTodFromBuffer.day == time.day);
CHECK(timeReturnAsTodFromBuffer.hour == time.hour);
CHECK(timeReturnAsTodFromBuffer.minute == time.minute);
CHECK(timeReturnAsTodFromBuffer.second == time.second);
CHECK(timeReturnAsTodFromBuffer.usecond == 123000);
Clock::TimeOfDay_t todFromCCSDS;
result = CCSDSTime::convertFromCcsds(&todFromCCSDS, buffer, sizeof(cdsTime));
CHECK(result == HasReturnvaluesIF::RETURN_OK);
CHECK(todFromCCSDS.year == time.year);
CHECK(todFromCCSDS.month == time.month);
CHECK(todFromCCSDS.day == time.day);
CHECK(todFromCCSDS.hour == time.hour);
CHECK(todFromCCSDS.minute == time.minute);
CHECK(todFromCCSDS.second == time.second);
CHECK(todFromCCSDS.usecond == 123000);
}
SECTION("CCSDS Failures") {
Clock::TimeOfDay_t time;
time.year = 2020;
time.month = 12;
time.day = 32;
time.hour = 13;
time.minute = 24;
time.second = 45;
time.usecond = 123456;
CCSDSTime::Ccs_mseconds to;
auto result = CCSDSTime::convertToCcsds(&to, &time);
REQUIRE(result == CCSDSTime::INVALID_TIME_FORMAT);
CCSDSTime::Ccs_seconds to2;
result = CCSDSTime::convertToCcsds(&to2, &time);
REQUIRE(result == CCSDSTime::INVALID_TIME_FORMAT);
} }
} }