diff --git a/CHANGELOG.md b/CHANGELOG.md index 26df4486d..c7d3b90b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,20 +12,29 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## Changes -- GPIO HAL: Renamed entries of enumerations to avoid nameclashes with Windows defines. - `IN` and `OUT` in `Direction` changed to `DIR_IN` and `DIR_OUT`. - `CALLBACK` in `GpioTypes` changed to `TYPE_CALLBACK` +- 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 +- New CMake option `FSFW_HAL_LINUX_ADD_LIBGPIOD` to specifically exclude `gpiod` code. + 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 ## Removed - Removed the `HkSwitchHelper`. This module should not be needed anymore, now that the local - datapools have been implemented + datapools have been implemented. + PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/557 ## Additions - 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 # [v4.0.0] diff --git a/src/fsfw/FSFW.h.in b/src/fsfw/FSFW.h.in index 953677bde..0eeb00eb0 100644 --- a/src/fsfw/FSFW.h.in +++ b/src/fsfw/FSFW.h.in @@ -31,7 +31,7 @@ #endif /* FSFW_VERBOSE_LEVEL */ #ifndef FSFW_DISABLE_PRINTOUT -#define FSFW_DISABLE_PRINTOUT 0 +#define FSFW_DISABLE_PRINTOUT 0 #endif #ifndef FSFW_USE_REALTIME_FOR_LINUX diff --git a/src/fsfw/FSFWVersion.h.in b/src/fsfw/FSFWVersion.h.in index 7935b2f61..19a562143 100644 --- a/src/fsfw/FSFWVersion.h.in +++ b/src/fsfw/FSFWVersion.h.in @@ -2,8 +2,8 @@ #define FSFW_VERSION_H_ // Versioning is kept in project CMakeLists.txt file -#define FSFW_VERSION @FSFW_VERSION@ -#define FSFW_SUBVERSION @FSFW_SUBVERSION@ -#define FSFW_REVISION @FSFW_REVISION@ +#define FSFW_VERSION_MAJOR @FSFW_VERSION@ +#define FSFW_VERSION_MINOR @FSFW_SUBVERSION@ +#define FSFW_VERSION_REVISION @FSFW_REVISION@ #endif /* FSFW_VERSION_H_ */ diff --git a/src/fsfw/events/Event.h b/src/fsfw/events/Event.h index 20b66679e..ecab04932 100644 --- a/src/fsfw/events/Event.h +++ b/src/fsfw/events/Event.h @@ -7,8 +7,14 @@ // could be moved to more suitable location #include -typedef uint16_t EventId_t; -typedef uint8_t EventSeverity_t; +using EventId_t = uint16_t; +using EventSeverity_t = uint8_t; +using UniqueEventId_t = uint8_t; + +namespace severity { +enum Severity : EventSeverity_t { INFO = 1, LOW = 2, MEDIUM = 3, HIGH = 4 }; + +} // namespace severity #define MAKE_EVENT(id, severity) (((severity) << 16) + (SUBSYSTEM_ID * 100) + (id)) @@ -20,18 +26,11 @@ constexpr EventId_t getEventId(Event event) { return (event & 0xFFFF); } constexpr EventSeverity_t getSeverity(Event event) { return ((event >> 16) & 0xFF); } -constexpr Event makeEvent(uint8_t subsystemId, uint8_t uniqueEventId, +constexpr Event makeEvent(uint8_t subsystemId, UniqueEventId_t uniqueEventId, EventSeverity_t eventSeverity) { return (eventSeverity << 16) + (subsystemId * 100) + uniqueEventId; } } // namespace event -namespace severity { -static constexpr EventSeverity_t INFO = 1; -static constexpr EventSeverity_t LOW = 2; -static constexpr EventSeverity_t MEDIUM = 3; -static constexpr EventSeverity_t HIGH = 4; -} // namespace severity - #endif /* EVENTOBJECT_EVENT_H_ */ diff --git a/src/fsfw/osal/host/Clock.cpp b/src/fsfw/osal/host/Clock.cpp index 62f9d9d5f..d0acdfdf0 100644 --- a/src/fsfw/osal/host/Clock.cpp +++ b/src/fsfw/osal/host/Clock.cpp @@ -150,17 +150,14 @@ ReturnValue_t Clock::convertTimeOfDayToTimeval(const TimeOfDay_t* from, timeval* time_tm.tm_hour = from->hour; time_tm.tm_min = from->minute; time_tm.tm_sec = from->second; + time_tm.tm_isdst = 0; - time_t seconds = mktime(&time_tm); + time_t seconds = timegm(&time_tm); to->tv_sec = seconds; to->tv_usec = from->usecond; // Fails in 2038.. return HasReturnvaluesIF::RETURN_OK; -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << "Clock::convertTimeBla: not implemented yet" << std::endl; -#endif - return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t Clock::convertTimevalToJD2000(timeval time, double* JD2000) { diff --git a/src/fsfw/osal/linux/Clock.cpp b/src/fsfw/osal/linux/Clock.cpp index 7092e6b2b..61eb970ff 100644 --- a/src/fsfw/osal/linux/Clock.cpp +++ b/src/fsfw/osal/linux/Clock.cpp @@ -140,8 +140,9 @@ ReturnValue_t Clock::convertTimeOfDayToTimeval(const TimeOfDay_t* from, timeval* fromTm.tm_hour = from->hour; fromTm.tm_min = from->minute; fromTm.tm_sec = from->second; + fromTm.tm_isdst = 0; - to->tv_sec = mktime(&fromTm); + to->tv_sec = timegm(&fromTm); to->tv_usec = from->usecond; return HasReturnvaluesIF::RETURN_OK; } diff --git a/src/fsfw/timemanager/CCSDSTime.cpp b/src/fsfw/timemanager/CCSDSTime.cpp index 290d58dce..c5132cbb0 100644 --- a/src/fsfw/timemanager/CCSDSTime.cpp +++ b/src/fsfw/timemanager/CCSDSTime.cpp @@ -557,6 +557,35 @@ ReturnValue_t CCSDSTime::convertFromCDS(timeval* to, const uint8_t* from, size_t return RETURN_OK; } +ReturnValue_t CCSDSTime::convertFromCDS(timeval* to, const CCSDSTime::CDS_short* from) { + if (to == nullptr or from == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + uint16_t days = (from->dayMSB << 8) + from->dayLSB; + if (days <= DAYS_CCSDS_TO_UNIX_EPOCH) { + return INVALID_TIME_FORMAT; + } + days -= DAYS_CCSDS_TO_UNIX_EPOCH; + to->tv_sec = days * SECONDS_PER_DAY; + uint32_t msDay = + (from->msDay_hh << 24) + (from->msDay_h << 16) + (from->msDay_l << 8) + from->msDay_ll; + to->tv_sec += (msDay / 1000); + to->tv_usec = (msDay % 1000) * 1000; + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t CCSDSTime::convertFromCDS(Clock::TimeOfDay_t* to, const CCSDSTime::CDS_short* from) { + if (to == nullptr or from == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + timeval tempTimeval; + ReturnValue_t result = convertFromCDS(&tempTimeval, from); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + return CCSDSTime::convertTimevalToTimeOfDay(to, &tempTimeval); +} + ReturnValue_t CCSDSTime::convertFromCUC(timeval* to, uint8_t pField, const uint8_t* from, size_t* foundLength, size_t maxLength) { uint32_t secs = 0; diff --git a/src/fsfw/timemanager/CCSDSTime.h b/src/fsfw/timemanager/CCSDSTime.h index 4a0e1bfc4..9de41e097 100644 --- a/src/fsfw/timemanager/CCSDSTime.h +++ b/src/fsfw/timemanager/CCSDSTime.h @@ -180,6 +180,8 @@ class CCSDSTime : public HasReturnvaluesIF { static ReturnValue_t convertFromCDS(timeval *to, uint8_t const *from, size_t *foundLength, size_t maxLength); + static ReturnValue_t convertFromCDS(timeval *to, const CCSDSTime::CDS_short *from); + static ReturnValue_t convertFromCDS(Clock::TimeOfDay_t *to, const CCSDSTime::CDS_short *from); static ReturnValue_t convertFromCCS(Clock::TimeOfDay_t *to, uint8_t const *from, size_t *foundLength, size_t maxLength); diff --git a/src/fsfw/version.cpp b/src/fsfw/version.cpp index 1bdd6cc73..926e465ff 100644 --- a/src/fsfw/version.cpp +++ b/src/fsfw/version.cpp @@ -1,8 +1,22 @@ #include "version.h" #include "fsfw/FSFWVersion.h" -void fsfw::getVersion(Version& v) { - v.major = FSFW_VERSION; - v.minor = FSFW_SUBVERSION; - v.revision = FSFW_REVISION; +#include + +#ifdef major +#undef major +#endif + +#ifdef minor +#undef minor +#endif + +const fsfw::Version fsfw::FSFW_VERSION = {FSFW_VERSION_MAJOR, FSFW_VERSION_MINOR, + FSFW_VERSION_REVISION}; + +fsfw::Version::Version(uint32_t major, uint32_t minor, uint32_t revision) + : major(major), minor(minor), revision(revision) {} + +void fsfw::Version::getVersion(char* str, size_t maxLen) const { + snprintf(str, maxLen, "%d.%d.%d", major, minor, revision); } diff --git a/src/fsfw/version.h b/src/fsfw/version.h index 04b7e3ce4..7cddf1938 100644 --- a/src/fsfw/version.h +++ b/src/fsfw/version.h @@ -1,18 +1,64 @@ #ifndef FSFW_SRC_FSFW_VERSION_H_ #define FSFW_SRC_FSFW_VERSION_H_ +#include "fsfw/FSFW.h" + +#if FSFW_CPP_OSTREAM_ENABLED == 1 +#include +#endif #include namespace fsfw { -struct Version { +class Version { + public: + Version(uint32_t major, uint32_t minor, uint32_t revision); uint32_t major = 0; uint32_t minor = 0; uint32_t revision = 0; + + friend bool operator==(const Version& v1, const Version& v2) { + return (v1.major == v2.major and v1.minor == v2.minor and v1.revision == v2.revision); + } + + friend bool operator!=(const Version& v1, const Version& v2) { return not(v1 == v2); } + + friend bool operator<(const Version& v1, const Version& v2) { + return ((v1.major < v2.major) or (v1.major == v2.major and v1.minor < v2.minor) or + (v1.major == v2.major and v1.minor == v2.minor and v1.revision < v2.revision)); + } + + friend bool operator>(const Version& v1, const Version& v2) { + return not (v1 < v2) and not (v1 == v2); + } + + friend bool operator<=(const Version& v1, const Version& v2) { return ((v1 == v2) or (v1 < v2)); } + + friend bool operator>=(const Version& v1, const Version& v2) { return ((v1 == v2) or (v1 > v2)); } + +#if FSFW_CPP_OSTREAM_ENABLED == 1 + /** + * Print format to given ostream using format "major.minor.revision" + * @param os + * @param v + * @return + */ + friend std::ostream& operator<<(std::ostream& os, const Version& v) { + os << v.major << "." << v.minor << "." << v.revision; + return os; + } +#endif + + /** + * Get version as format "major.minor.revision" + * @param str + * @param maxLen + */ + void getVersion(char* str, size_t maxLen) const; }; -void getVersion(Version& version); +extern const fsfw::Version FSFW_VERSION; -} +} // namespace fsfw #endif /* FSFW_SRC_FSFW_VERSION_H_ */ diff --git a/tests/src/fsfw_tests/unit/CMakeLists.txt b/tests/src/fsfw_tests/unit/CMakeLists.txt index a83e2f7f8..a8d31d88a 100644 --- a/tests/src/fsfw_tests/unit/CMakeLists.txt +++ b/tests/src/fsfw_tests/unit/CMakeLists.txt @@ -2,14 +2,13 @@ target_sources(${FSFW_TEST_TGT} PRIVATE CatchDefinitions.cpp CatchFactory.cpp printChar.cpp + version.cpp ) -# if(FSFW_CUSTOM_UNITTEST_RUNNER) - target_sources(${FSFW_TEST_TGT} PRIVATE - CatchRunner.cpp - CatchSetup.cpp - ) -# endif() +target_sources(${FSFW_TEST_TGT} PRIVATE + CatchRunner.cpp + CatchSetup.cpp +) add_subdirectory(testcfg) add_subdirectory(action) diff --git a/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolManagerTest.cpp b/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolManagerTest.cpp index fb22972e3..077625959 100644 --- a/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolManagerTest.cpp +++ b/tests/src/fsfw_tests/unit/datapoollocal/LocalPoolManagerTest.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -93,10 +94,8 @@ TEST_CASE("LocalPoolManagerTest", "[LocManTest]") { poolOwner->dataset.setChanged(true); /* Store current time, we are going to check the (approximate) time equality later */ - CCSDSTime::CDS_short timeCdsNow; timeval now; Clock::getClock_timeval(&now); - CCSDSTime::convertToCcsds(&timeCdsNow, &now); /* Trigger generation of snapshot */ REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK); @@ -131,13 +130,11 @@ TEST_CASE("LocalPoolManagerTest", "[LocManTest]") { CHECK(newSet.localPoolUint16Vec.value[2] == 42932); /* Now we check that both times are equal */ - CHECK(cdsShort.pField == timeCdsNow.pField); - CHECK(cdsShort.dayLSB == Catch::Approx(timeCdsNow.dayLSB).margin(1)); - CHECK(cdsShort.dayMSB == Catch::Approx(timeCdsNow.dayMSB).margin(1)); - CHECK(cdsShort.msDay_h == Catch::Approx(timeCdsNow.msDay_h).margin(1)); - CHECK(cdsShort.msDay_hh == Catch::Approx(timeCdsNow.msDay_hh).margin(1)); - CHECK(cdsShort.msDay_l == Catch::Approx(timeCdsNow.msDay_l).margin(1)); - CHECK(cdsShort.msDay_ll == Catch::Approx(timeCdsNow.msDay_ll).margin(5)); + timeval timeFromHK; + auto result = CCSDSTime::convertFromCDS(&timeFromHK, &cdsShort); + CHECK(result == HasReturnvaluesIF::RETURN_OK); + timeval difference = timeFromHK - now; + CHECK(timevalOperations::toDouble(difference) < 1.0); } SECTION("VariableSnapshotTest") { @@ -158,13 +155,10 @@ TEST_CASE("LocalPoolManagerTest", "[LocManTest]") { } poolVar->setChanged(true); - /* Store current time, we are going to check the (approximate) time equality later */ CCSDSTime::CDS_short timeCdsNow; timeval now; Clock::getClock_timeval(&now); - CCSDSTime::convertToCcsds(&timeCdsNow, &now); - REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK); /* Check update snapshot was sent. */ @@ -193,13 +187,11 @@ TEST_CASE("LocalPoolManagerTest", "[LocManTest]") { CHECK(varCopy.value == 25); /* Now we check that both times are equal */ - CHECK(cdsShort.pField == timeCdsNow.pField); - CHECK(cdsShort.dayLSB == Catch::Approx(timeCdsNow.dayLSB).margin(1)); - CHECK(cdsShort.dayMSB == Catch::Approx(timeCdsNow.dayMSB).margin(1)); - CHECK(cdsShort.msDay_h == Catch::Approx(timeCdsNow.msDay_h).margin(1)); - CHECK(cdsShort.msDay_hh == Catch::Approx(timeCdsNow.msDay_hh).margin(1)); - CHECK(cdsShort.msDay_l == Catch::Approx(timeCdsNow.msDay_l).margin(1)); - CHECK(cdsShort.msDay_ll == Catch::Approx(timeCdsNow.msDay_ll).margin(5)); + timeval timeFromHK; + auto result = CCSDSTime::convertFromCDS(&timeFromHK, &cdsShort); + CHECK(result == HasReturnvaluesIF::RETURN_OK); + timeval difference = timeFromHK - now; + CHECK(timevalOperations::toDouble(difference) < 1.0); } SECTION("VariableNotificationTest") { diff --git a/tests/src/fsfw_tests/unit/timemanager/TestCCSDSTime.cpp b/tests/src/fsfw_tests/unit/timemanager/TestCCSDSTime.cpp index 89713488b..2e80487aa 100644 --- a/tests/src/fsfw_tests/unit/timemanager/TestCCSDSTime.cpp +++ b/tests/src/fsfw_tests/unit/timemanager/TestCCSDSTime.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -89,4 +90,43 @@ TEST_CASE("CCSDSTime Tests", "[TestCCSDSTime]") { REQUIRE(timeTo.second == 59); REQUIRE(timeTo.usecond == Catch::Approx(123000)); } + + SECTION("CDS Conversions") { + // Preperation + Clock::TimeOfDay_t time; + time.year = 2020; + time.month = 2; + time.day = 29; + time.hour = 13; + time.minute = 24; + time.second = 45; + time.usecond = 123456; + timeval timeAsTimeval; + auto result = Clock::convertTimeOfDayToTimeval(&time, &timeAsTimeval); + CHECK(result == HasReturnvaluesIF::RETURN_OK); + CHECK(timeAsTimeval.tv_sec == 1582982685); + CHECK(timeAsTimeval.tv_usec == 123456); + + // Conversion to CDS Short + CCSDSTime::CDS_short cdsTime; + result = CCSDSTime::convertToCcsds(&cdsTime, &timeAsTimeval); + CHECK(result == HasReturnvaluesIF::RETURN_OK); + // Days in CCSDS Epoch 22704 (0x58B0) + CHECK(cdsTime.dayMSB == 0x58); + CHECK(cdsTime.dayLSB == 0xB0); + // MS of day 48285123.456 (floored here) + CHECK(cdsTime.msDay_hh == 0x2); + CHECK(cdsTime.msDay_h == 0xE0); + CHECK(cdsTime.msDay_l == 0xC5); + CHECK(cdsTime.msDay_ll == 0xC3); + + // Conversion back to timeval + timeval timeReturnAsTimeval; + result = CCSDSTime::convertFromCDS(&timeReturnAsTimeval, &cdsTime); + CHECK(result == HasReturnvaluesIF::RETURN_OK); + // micro seconds precision is lost + timeval difference = timeAsTimeval - timeReturnAsTimeval; + CHECK(difference.tv_usec == 456); + CHECK(difference.tv_sec == 0); + } } \ No newline at end of file diff --git a/tests/src/fsfw_tests/unit/version.cpp b/tests/src/fsfw_tests/unit/version.cpp new file mode 100644 index 000000000..92a930dcc --- /dev/null +++ b/tests/src/fsfw_tests/unit/version.cpp @@ -0,0 +1,92 @@ + +#include "fsfw/version.h" + +#include + +#include "fsfw/serviceinterface.h" +#include "fsfw_tests/unit/CatchDefinitions.h" + +TEST_CASE("Version API Tests", "[TestVersionAPI]") { + // Check that major version is non-zero + REQUIRE(fsfw::FSFW_VERSION.major > 0); + uint32_t fsfwMajor = fsfw::FSFW_VERSION.major; + REQUIRE(fsfw::Version(255, 0, 0) > fsfw::FSFW_VERSION); + REQUIRE(fsfw::Version(255, 0, 0) >= fsfw::FSFW_VERSION); + REQUIRE(fsfw::Version(0, 0, 0) < fsfw::FSFW_VERSION); + REQUIRE(fsfw::Version(0, 0, 0) <= fsfw::FSFW_VERSION); + fsfw::Version v1 = fsfw::Version(1, 1, 1); + fsfw::Version v2 = fsfw::Version(1, 1, 1); + REQUIRE(v1 == v2); + REQUIRE(not (v1 < v2)); + REQUIRE(not (v1 > v2)); + REQUIRE(v1 <= v2); + REQUIRE(v1 >= v2); + v1.revision -= 1; + REQUIRE(v1 != v2); + REQUIRE(not (v1 == v2)); + REQUIRE(not (v1 > v2)); + REQUIRE(not (v1 >= v2)); + REQUIRE(v1 < v2); + REQUIRE(v1 <= v2); + v1.revision += 1; + v1.minor -= 1; + REQUIRE(v1 != v2); + REQUIRE(v1 < v2); + REQUIRE(v1 <= v2); + REQUIRE(not (v1 == v2)); + REQUIRE(not (v1 > v2)); + REQUIRE(not (v1 >= v2)); + v1.minor += 1; + v1.major -= 1; + REQUIRE(v1 != v2); + REQUIRE(v1 < v2); + REQUIRE(v1 <= v2); + REQUIRE(not (v1 == v2)); + REQUIRE(not (v1 > v2)); + REQUIRE(not (v1 >= v2)); + v1.major += 1; + REQUIRE(v1 == v2); + REQUIRE(v1 <= v2); + REQUIRE(v1 >= v2); + REQUIRE(not (v1 != v2)); + REQUIRE(not (v1 > v2)); + REQUIRE(not (v1 < v2)); + v1.major += 1; + v1.minor -= 1; + REQUIRE(v1 != v2); + REQUIRE(v1 > v2); + REQUIRE(v1 >= v2); + REQUIRE(not (v1 == v2)); + REQUIRE(not (v1 < v2)); + REQUIRE(not (v1 <= v2)); + v1.major -= 1; + v1.minor += 2; + v1.revision -= 1; + REQUIRE(v1 != v2); + REQUIRE(v1 > v2); + REQUIRE(v1 >= v2); + REQUIRE(not (v1 == v2)); + REQUIRE(not (v1 < v2)); + REQUIRE(not (v1 <= v2)); + v1.minor -= 1; + v1.revision += 2; + REQUIRE(v1 != v2); + REQUIRE(v1 > v2); + REQUIRE(v1 >= v2); + REQUIRE(not (v1 == v2)); + REQUIRE(not (v1 < v2)); + REQUIRE(not (v1 <= v2)); + v1.revision -= 1; + REQUIRE(v1 == v2); + REQUIRE(v1 <= v2); + REQUIRE(v1 >= v2); + REQUIRE(not (v1 != v2)); +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "v" << fsfw::FSFW_VERSION << std::endl; +#endif + char verString[10] = {}; + fsfw::FSFW_VERSION.getVersion(verString, sizeof(verString)); +#if FSFW_DISABLE_PRINTOUT == 0 + printf("v%s\n",verString); +#endif +}