From a495f7c2713c1a2da2c056669f3b6ff468b689e9 Mon Sep 17 00:00:00 2001
From: thodkatz <thodkatz@gmail.com>
Date: Mon, 4 Mar 2019 02:43:44 +0200
Subject: [PATCH] Added a function that converts a UTC date to elapsed seconds
 since Unix epoch

---
 inc/Helpers/TimeHelper.hpp              | 61 +++++++++++++++++++++++--
 inc/MessageParser.hpp                   |  2 +-
 inc/Services/TimeManagementService.hpp  | 16 +++++--
 src/Helpers/TimeHelper.cpp              | 40 +++++++++++-----
 src/Services/TimeManagementService.cpp  | 15 ++----
 src/main.cpp                            | 10 +++-
 test/Helpers/TimeHelper.cpp             | 35 ++++++++++++--
 test/Services/TimeManagementService.cpp | 53 ++++++++++++++++++---
 8 files changed, 188 insertions(+), 44 deletions(-)

diff --git a/inc/Helpers/TimeHelper.hpp b/inc/Helpers/TimeHelper.hpp
index eae6d844..42de6983 100644
--- a/inc/Helpers/TimeHelper.hpp
+++ b/inc/Helpers/TimeHelper.hpp
@@ -2,9 +2,26 @@
 #define ECSS_SERVICES_TIMEHELPER_HPP
 
 #include <cstdint>
-#include <ctime>
 #include <Message.hpp>
 
+/**
+ * The time and date provided from Real Time Clock(Real Time Clock)
+ *
+ * Note:
+ * This struct is similar to the `struct tm` of <ctime> library but it is more embedded-friendly
+ *
+ * For the current implementation this struct takes dummy values, because RTC hasn't been
+ * implemented
+ */
+struct TimeAndDate {
+	uint16_t year;
+	uint8_t month;
+	uint8_t day;
+	uint8_t hour;
+	uint8_t minute;
+	uint8_t second;
+};
+
 /**
  * This class formats the spacecraft time and cooperates closely with the ST[09] time management.
  * The ECSS standard supports two time formats: the CUC and CSD that are described in
@@ -12,11 +29,34 @@
  * Universal Time)
  *
  * Note:
- * Since this code is UTC-based, the leap second correction must be made and leap seconds should
- * be considered in the difference between timestamps if a critical time-difference is needed
+ * Since this code is UTC-based, the leap second correction must be made. The leap seconds that 
+ * have been occured between timestamps should be considered if a critical time-difference is needed
  *
  */
 class TimeHelper {
+private:
+	int SecondsPerMinute = 60;
+	int SecondsPerHour = 3600;
+	int SecondsPerDay = 86400;
+	int DaysOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+	/**
+	 * @param year The year that will be examined if it is a leap year(366 days)
+	 * @return if the /p is a leap year returns true and if it isn't returns false
+	 */
+	bool IsLeapYear(uint16_t year);
+
+	/**
+     * Make GMT(UTC) time. This is a reimplemented mktime() of <ctime> library in an
+     * embedded systems way
+     *
+     * @param timeInfo the time information/data from the RTC(UTC format)
+     * @return the elapsed seconds between a given UTC date(after the Unix epoch) and Unix epoch
+     * (1/1/1970 00:00:00)
+     * @todo change the epoch for computer-efficiency
+     */
+	uint32_t mkgmtime(struct TimeAndDate &timeInfo);
+
 public:
 
 	/**
@@ -33,7 +73,20 @@ public:
  	 * @todo declare the implicit P-field
  	 * @todo check if we need milliseconds
 	 */
-	static uint64_t implementCDSTimeFormat(struct tm* timeInfo);
+	static uint64_t implementCDSTimeFormat(struct TimeAndDate &timeInfo);
+
+	/**
+	 * Dummy function created only to access mkgmtime for testing
+	 *
+	 * @todo Delete this function
+	 */
+	uint32_t get_mkgmtime(struct TimeAndDate &timeInfo) {
+		return mkgmtime(timeInfo);
+	}
+
 };
 
+// used to access `mkgmtime` function in the static `implementCDSTimeFormat` function
+static TimeHelper mkgmtimeAccess;
+
 #endif //ECSS_SERVICES_TIMEHELPER_HPP
diff --git a/inc/MessageParser.hpp b/inc/MessageParser.hpp
index 0af0afbf..922ac97d 100644
--- a/inc/MessageParser.hpp
+++ b/inc/MessageParser.hpp
@@ -32,7 +32,7 @@ public:
 	 * @param length The size of the message
 	 * @return A new object that represents the parsed message
 	 */
-	Message parse(uint8_t * data, uint32_t length);
+	Message parse(uint8_t *data, uint32_t length);
 
 private:
 	/**
diff --git a/inc/Services/TimeManagementService.hpp b/inc/Services/TimeManagementService.hpp
index d75c3776..090a04e5 100644
--- a/inc/Services/TimeManagementService.hpp
+++ b/inc/Services/TimeManagementService.hpp
@@ -1,6 +1,7 @@
 #ifndef ECSS_SERVICES_TIMEMANAGEMENTSERVICE_HPP
 #define ECSS_SERVICES_TIMEMANAGEMENTSERVICE_HPP
 
+
 #include <Service.hpp>
 #include "Helpers/TimeHelper.hpp"
 
@@ -26,27 +27,30 @@
  * @todo Declare the time accuracy that the standard claims in the spacecraft
  * time reference section(6.9.3.d,e)
  */
+
 class TimeManagementService : public Service {
 public:
 	TimeManagementService() {
 		serviceType = 9;
 	}
-
-	/**
+	
+/**
 	 * TM[9,3] CDS time report
 	 *
 	 * This function sends reports with the spacecraft time that is formatted according to the CDS
 	 * time code format(check class `TimeHelper` for the format)
 	 *
+     * @param timeInfo the time information/data from the RTC(UTC format)
 	 * @todo check if we need spacecraft time reference status
 	 * @todo ECSS standard claims: <<The time reports generated by the time reporting subservice
 	 * are spacecraft time packets. A spacecraft time packet does not carry the message type,
 	 * consisting of the service type and message subtype.>> Check if we need to implement that
 	 * or should ignore the standard?
 	 */
-	void cdsTimeReport();
 
-	/**
+	void cdsTimeReport(struct TimeAndDate &timeInfo);
+	
+    /**
 	 * TC[9,128] CDS time request
 	 *
 	 * This function is a custom subservice(mission specific) with message type 128(as defined
@@ -57,8 +61,10 @@ public:
 	 * @param messageTime The class-member `data` of /p has the data for  the time configuration
 	 * (a CUC format as described in the documentation of the class TimeHelper)
 	 */
-	void parseTime(Message &messageTime);
+    
+	void parseTime(uint8_t *timeData, uint8_t length);
 };
 
 
 #endif //ECSS_SERVICES_TIMEMANAGEMENTSERVICE_HPP
+
diff --git a/src/Helpers/TimeHelper.cpp b/src/Helpers/TimeHelper.cpp
index 0920fab4..648c783c 100644
--- a/src/Helpers/TimeHelper.cpp
+++ b/src/Helpers/TimeHelper.cpp
@@ -1,32 +1,50 @@
 #include "Helpers/TimeHelper.hpp"
 
-uint64_t TimeHelper::implementCDSTimeFormat(struct tm* timeInfo) {
+bool TimeHelper::IsLeapYear(uint16_t year) {
+
+	if (year % 4 != 0) return false;
+	if (year % 100 != 0) return true;
+	return (year % 400) == 0;
+}
+
+uint32_t TimeHelper::mkgmtime(struct TimeAndDate &timeInfo) {
+
+	uint32_t secs = 0;
+	for (uint16_t y = 1970; y < timeInfo.year; ++y)
+		secs += (IsLeapYear(y) ? 366 : 365) * SecondsPerDay;
+	for (uint16_t m = 1; m < timeInfo.month; ++m) {
+		secs += DaysOfMonth[m - 1] * SecondsPerDay;
+		if (m == 2 && IsLeapYear(timeInfo.year))
+			secs += SecondsPerDay;
+	}
+	secs += (timeInfo.day - 1) * SecondsPerDay;
+	secs += timeInfo.hour * SecondsPerHour;
+	secs += timeInfo.minute * SecondsPerMinute;
+	secs += timeInfo.second;
+	return secs;
+}
+
+uint64_t TimeHelper::implementCDSTimeFormat(struct TimeAndDate &timeInfo) {
 	/**
 	 * Define the T-field. The total number of octets for the implementation of T-field is 6(2 for
 	 * the `DAY` and 4 for the `ms of day`
 	 */
 
-	/**
-	 * Elapsed seconds between a given date from `timeInfo`(UTC time) and epoch 1 January 1970
-	 * 00:00:00(hours:minutes:seconds)
-	 *
-	 * @todo WARNING: evaluate the mktime() (Is it computer efficient and embedded
-	 * systems-compliant?)
-	 */
-	time_t seconds = mktime(timeInfo);
+
+	uint32_t seconds = mkgmtimeAccess.mkgmtime(timeInfo);
 
 	/**
 	 * The `DAY` segment, 16 bits as defined from standard. Actually the days passed from an
 	 * Agency-defined epoch,that it will be 1 January 1970(1/1/1970) 00:00:00(hours:minutes:seconds)
 	 * This epoch is configured from the current implementation, using mktime() function
 	 */
-	uint16_t elapsedDays = static_cast<uint16_t>(seconds/86400);
+	uint16_t elapsedDays = static_cast<uint16_t>(seconds / 86400);
 
 	/**
 	 * The `ms of day` segment, 32 bits as defined in standard. The `ms of the day` and DAY`
 	 * should give the time passed from the defined epoch (1/1/1970)
 	 */
-	uint32_t msOfDay = static_cast<uint32_t >((seconds%86400)*1000);
+	uint32_t msOfDay = static_cast<uint32_t >((seconds % 86400) * 1000);
 
 	/**
 	 * Define CDS time format
diff --git a/src/Services/TimeManagementService.cpp b/src/Services/TimeManagementService.cpp
index c23b2356..9087cb2f 100644
--- a/src/Services/TimeManagementService.cpp
+++ b/src/Services/TimeManagementService.cpp
@@ -1,25 +1,18 @@
 #include "Services/TimeManagementService.hpp"
 
-void TimeManagementService::cdsTimeReport() {
+void TimeManagementService::cdsTimeReport(struct TimeAndDate &timeInfo) {
 	// TM[9,3] CDS time report
 
 	Message timeReport = createTM(3);
 
-	/**
-	 * For the time being we will use C++ functions to get a time value, but this will change
-	 * when the RTC will be implemented
-	 */
-	time_t currTime = time(nullptr); // seconds have passed since 00:00:00 GMT, Jan 1, 1970
-	struct tm* timeInfo = gmtime(&currTime); // UTC time
-
-	uint64_t timeFormat = TimeHelper::implementCDSTimeFormat(timeInfo); // store the return value
+	uint64_t timeFormat = TimeHelper::implementCDSTimeFormat(timeInfo); 
 
 	timeReport.appendByte(static_cast<uint8_t >(timeFormat >> 32)); // append the first byte
-	timeReport.appendWord(static_cast<uint32_t >(timeFormat)); // append the rest bytes(4 bytes)
+	timeReport.appendWord(static_cast<uint32_t >(timeFormat)); // append the rest 4 bytes
 
 	storeMessage(timeReport);
 }
 
-void TimeManagementService::parseTime(Message &messageTime) {
+void TimeManagementService::parseTime(uint8_t *timeData, uint8_t lenght) {
 
 }
diff --git a/src/main.cpp b/src/main.cpp
index ef935aa7..7f49b9d5 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -174,7 +174,15 @@ int main() {
 
 	// ST[09] test
 	TimeManagementService timeReport;
-	timeReport.cdsTimeReport();
+	struct TimeAndDate timeInfo = {0};
+	// 10/04/1998 10:15:00
+	timeInfo.year = 1998;
+	timeInfo.month = 4;
+	timeInfo.day = 10;
+	timeInfo.hour = 10;
+	timeInfo.minute = 15;
+	timeInfo.second = 0;
+	timeReport.cdsTimeReport(timeInfo);
 
 	// ST[05] (5,5 to 5,8) test [works]
 	EventReportService::Event eventIDs[] = {EventReportService::HighSeverityUnknownEvent,
diff --git a/test/Helpers/TimeHelper.cpp b/test/Helpers/TimeHelper.cpp
index 0b23a2a0..f14a2047 100644
--- a/test/Helpers/TimeHelper.cpp
+++ b/test/Helpers/TimeHelper.cpp
@@ -2,9 +2,36 @@
 #include "Helpers/TimeHelper.hpp"
 
 TEST_CASE("Time format implementation", "[CUC]") {
-	// very simple tests for the TimeHelper
+	struct TimeAndDate timeInfo = {0};
+	// 10/04/1998 10:15:00
+	timeInfo.year = 1998;
+	timeInfo.month = 4;
+	timeInfo.day = 10;
+	timeInfo.hour = 10;
+	timeInfo.minute = 15;
+	timeInfo.second = 0;
+
+	TimeHelper time;
+	uint32_t currTime = time.get_mkgmtime(timeInfo);
+
+	uint16_t elapsedDays = currTime/86400;
+	uint32_t msOfDay = currTime % 86400 * 1000;
+	uint64_t timeFormat = (static_cast<uint64_t>(elapsedDays) << 32 | msOfDay);
+	CHECK(TimeHelper::implementCDSTimeFormat(timeInfo) == timeFormat);
+
+	// 1/1/2019 00:00:00
+	timeInfo.year = 2019;
+	timeInfo.month = 1;
+	timeInfo.day = 1;
+	timeInfo.hour = 0;
+	timeInfo.minute = 0;
+	timeInfo.second = 0;
+
+	currTime = time.get_mkgmtime(timeInfo);
+
+	elapsedDays = currTime/86400;
+	msOfDay = currTime % 86400 * 1000;
+	timeFormat = (static_cast<uint64_t>(elapsedDays) << 32 | msOfDay);
+	CHECK(TimeHelper::implementCDSTimeFormat(timeInfo) == timeFormat);
 
-	CHECK(TimeHelper::implementCUCTimeFormat(60) == 0b11110000110010);
-	CHECK(TimeHelper::implementCUCTimeFormat(1000) == 0x3E832);
-	CHECK(TimeHelper::implementCUCTimeFormat(1200) == 0x4B032);
 }
diff --git a/test/Services/TimeManagementService.cpp b/test/Services/TimeManagementService.cpp
index 714c99e1..ad3bf17d 100644
--- a/test/Services/TimeManagementService.cpp
+++ b/test/Services/TimeManagementService.cpp
@@ -2,15 +2,54 @@
 #include <Services/TimeManagementService.hpp>
 #include "ServiceTests.hpp"
 
-TEST_CASE("TM[9,2]", "[service][st09]") {
-	TimeManagementService timeFormat;
+TEST_CASE("TM[9,3]", "[service][st09]") {
+	TimeManagementService timeService;
 
-	uint32_t seconds;
-	seconds = time(nullptr);
+	struct TimeAndDate timeInfo = {0};
+	// 10/04/1998 10:15:00
+	timeInfo.year = 1998;
+	timeInfo.month = 4;
+	timeInfo.day = 10;
+	timeInfo.hour = 10;
+	timeInfo.minute = 15;
+	timeInfo.second = 0;
 
-	timeFormat.cucTimeReport();
+	TimeHelper time;
+	uint32_t currTime = time.get_mkgmtime(timeInfo);
+
+	uint16_t elapsedDays = currTime/86400;
+	uint32_t msOfDay = currTime % 86400 * 1000;
+	uint64_t timeFormat = (static_cast<uint64_t>(elapsedDays) << 32 | msOfDay);
+
+	timeService.cdsTimeReport(timeInfo);
 	Message response = ServiceTests::get(0);
-	CHECK(response.readByte() == 50);
-	CHECK(response.readWord() == seconds);
+	CHECK(response.serviceType == 9);
+	CHECK(response.messageType == 3);
+	CHECK(response.packetType == Message::TM);
+	CHECK(response.readByte() == static_cast<uint8_t>((timeFormat >> 32)));
+	CHECK(response.readWord() == static_cast<uint32_t>(timeFormat));
+
+
+	// 1/1/2019 00:00:00
+	timeInfo.year = 2019;
+	timeInfo.month = 1;
+	timeInfo.day = 1;
+	timeInfo.hour = 0;
+	timeInfo.minute = 0;
+	timeInfo.second = 0;
+
+	currTime = time.get_mkgmtime(timeInfo);
+
+	elapsedDays = currTime/86400;
+	msOfDay = currTime % 86400 * 1000;
+	timeFormat = (static_cast<uint64_t>(elapsedDays) << 32 | msOfDay);
+
+	timeService.cdsTimeReport(timeInfo);
+	response = ServiceTests::get(1);
+	CHECK(response.serviceType == 9);
+	CHECK(response.messageType == 3);
+	CHECK(response.packetType == Message::TM);
+	CHECK(response.readByte() == static_cast<uint8_t>((timeFormat >> 32)));
+	CHECK(response.readWord() == static_cast<uint32_t>(timeFormat));
 
 }
-- 
GitLab