From ba01055f1d9edd83f6e467807e04e7204fc5a878 Mon Sep 17 00:00:00 2001 From: Chris Knight Date: Fri, 26 May 2023 09:45:13 -0700 Subject: [PATCH] Fix #2316, CFE_TIME_Print() calls strftime() --- modules/core_api/fsw/inc/cfe_time.h | 8 +- .../core_api/ut-stubs/src/cfe_time_handlers.c | 3 +- .../core_api/ut-stubs/src/cfe_time_stubs.c | 6 +- .../config/default_cfe_time_interface_cfg.h | 15 +- modules/time/fsw/src/cfe_time_api.c | 158 +++--------------- modules/time/ut-coverage/time_UT.c | 26 ++- 6 files changed, 49 insertions(+), 167 deletions(-) diff --git a/modules/core_api/fsw/inc/cfe_time.h b/modules/core_api/fsw/inc/cfe_time.h index ac2cf98ce..d4dfbc943 100644 --- a/modules/core_api/fsw/inc/cfe_time.h +++ b/modules/core_api/fsw/inc/cfe_time.h @@ -691,6 +691,8 @@ CFE_Status_t CFE_TIME_UnregisterSynchCallback(CFE_TIME_SynchCallbackPtr_t Callba ** - \c \\0 = trailing null ** ** \par Assumptions, External Events, and Notes: +** - This function calls strftime to format the output. Systems without this +** C99-standard function will have to write their own implementation. ** - The value of the time argument is simply added to the configuration ** definitions for the ground epoch and converted into a fixed length ** string in the buffer provided by the caller. @@ -706,8 +708,12 @@ CFE_Status_t CFE_TIME_UnregisterSynchCallback(CFE_TIME_SynchCallbackPtr_t Callba ** ** \param[in] TimeToPrint The time to print into the character array. ** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_TIME_BAD_ARGUMENT \copybrief CFE_TIME_BAD_ARGUMENT +** ******************************************************************************/ -void CFE_TIME_Print(char *PrintBuffer, CFE_TIME_SysTime_t TimeToPrint); +CFE_Status_t CFE_TIME_Print(char *PrintBuffer, CFE_TIME_SysTime_t TimeToPrint); /*****************************************************************************/ /** diff --git a/modules/core_api/ut-stubs/src/cfe_time_handlers.c b/modules/core_api/ut-stubs/src/cfe_time_handlers.c index ec9f8b5e9..c277bb721 100644 --- a/modules/core_api/ut-stubs/src/cfe_time_handlers.c +++ b/modules/core_api/ut-stubs/src/cfe_time_handlers.c @@ -45,13 +45,14 @@ * Default handler for CFE_TIME_Print coverage stub function * *------------------------------------------------------------*/ -void UT_DefaultHandler_CFE_TIME_Print(void *UserObj, UT_EntryKey_t FuncKey, const UT_StubContext_t *Context) +CFE_Status_t UT_DefaultHandler_CFE_TIME_Print(void *UserObj, UT_EntryKey_t FuncKey, const UT_StubContext_t *Context) { char * PrintBuffer = UT_Hook_GetArgValueByName(Context, "PrintBuffer", char *); CFE_TIME_SysTime_t TimeToPrint = UT_Hook_GetArgValueByName(Context, "TimeToPrint", CFE_TIME_SysTime_t); snprintf(PrintBuffer, CFE_TIME_PRINTED_STRING_SIZE, "UT %u.%u -", (unsigned int)TimeToPrint.Seconds, (unsigned int)TimeToPrint.Subseconds); + return CFE_SUCCESS; } /*------------------------------------------------------------ diff --git a/modules/core_api/ut-stubs/src/cfe_time_stubs.c b/modules/core_api/ut-stubs/src/cfe_time_stubs.c index 999f06008..ca99702db 100644 --- a/modules/core_api/ut-stubs/src/cfe_time_stubs.c +++ b/modules/core_api/ut-stubs/src/cfe_time_stubs.c @@ -305,12 +305,16 @@ uint32 CFE_TIME_Micro2SubSecs(uint32 MicroSeconds) * Generated stub function for CFE_TIME_Print() * ---------------------------------------------------- */ -void CFE_TIME_Print(char *PrintBuffer, CFE_TIME_SysTime_t TimeToPrint) +CFE_Status_t CFE_TIME_Print(char *PrintBuffer, CFE_TIME_SysTime_t TimeToPrint) { + UT_GenStub_SetupReturnBuffer(CFE_TIME_Print, CFE_Status_t); + UT_GenStub_AddParam(CFE_TIME_Print, char *, PrintBuffer); UT_GenStub_AddParam(CFE_TIME_Print, CFE_TIME_SysTime_t, TimeToPrint); UT_GenStub_Execute(CFE_TIME_Print, Basic, UT_DefaultHandler_CFE_TIME_Print); + + return UT_GenStub_GetReturnValue(CFE_TIME_Print, CFE_Status_t); } /* diff --git a/modules/time/config/default_cfe_time_interface_cfg.h b/modules/time/config/default_cfe_time_interface_cfg.h index 3e614f154..151d38c09 100644 --- a/modules/time/config/default_cfe_time_interface_cfg.h +++ b/modules/time/config/default_cfe_time_interface_cfg.h @@ -151,23 +151,14 @@ ** \cfetimecfg Default EPOCH Values ** ** \par Description: -** Default ground time epoch values +** Default ground time epoch values (from Jan. 1, 1970 00:00:00) ** Note: these values are used only by the CFE_TIME_Print() API function ** ** \par Limits -** Year - must be within 136 years -** Day - Jan 1 = 1, Feb 1 = 32, etc. -** Hour - 0 to 23 -** Minute - 0 to 59 -** Second - 0 to 59 ** Micros - 0 to 999999 */ -#define CFE_MISSION_TIME_EPOCH_YEAR 1980 -#define CFE_MISSION_TIME_EPOCH_DAY 1 -#define CFE_MISSION_TIME_EPOCH_HOUR 0 -#define CFE_MISSION_TIME_EPOCH_MINUTE 0 -#define CFE_MISSION_TIME_EPOCH_SECOND 0 -#define CFE_MISSION_TIME_EPOCH_MICROS 0 +#define CFE_MISSION_TIME_EPOCH_SECONDS 315532800 /* Jan. 1, 1980 00:00:00 */ +#define CFE_MISSION_TIME_EPOCH_MICROS 0 /** ** \cfetimecfg Time File System Factor diff --git a/modules/time/fsw/src/cfe_time_api.c b/modules/time/fsw/src/cfe_time_api.c index 1cd7e99d7..f8e8b35b6 100644 --- a/modules/time/fsw/src/cfe_time_api.c +++ b/modules/time/fsw/src/cfe_time_api.c @@ -34,6 +34,8 @@ #include +#include + /*---------------------------------------------------------------- * * Implemented per public API @@ -563,150 +565,30 @@ uint32 CFE_TIME_Micro2SubSecs(uint32 MicroSeconds) * See description in header file for argument/return detail * *-----------------------------------------------------------------*/ -void CFE_TIME_Print(char *PrintBuffer, CFE_TIME_SysTime_t TimeToPrint) +CFE_Status_t CFE_TIME_Print(char *PrintBuffer, CFE_TIME_SysTime_t TimeToPrint) { - uint32 NumberOfYears; - uint32 NumberOfDays; - uint32 NumberOfHours; - uint32 NumberOfMinutes; - uint32 NumberOfSeconds; - uint32 NumberOfMicros; - uint32 DaysInThisYear; - - bool StillCountingYears = true; + size_t FmtLen = 0; + uint32 Micros = (CFE_TIME_Sub2MicroSecs(TimeToPrint.Subseconds) + CFE_MISSION_TIME_EPOCH_MICROS) / 10; + struct tm tm; if (PrintBuffer == NULL) { - CFE_ES_WriteToSysLog("%s: Failed invalid arguments\n", __func__); - return; - } - - /* - ** Convert the cFE time (offset from epoch) into calendar time... - */ - NumberOfMicros = CFE_TIME_Sub2MicroSecs(TimeToPrint.Subseconds) + CFE_MISSION_TIME_EPOCH_MICROS; - - NumberOfMinutes = (NumberOfMicros / 60000000) + (TimeToPrint.Seconds / 60) + CFE_MISSION_TIME_EPOCH_MINUTE; - NumberOfMicros = NumberOfMicros % 60000000; - - NumberOfSeconds = (NumberOfMicros / 1000000) + (TimeToPrint.Seconds % 60) + CFE_MISSION_TIME_EPOCH_SECOND; - NumberOfMicros = NumberOfMicros % 1000000; - /* - ** Adding the epoch "seconds" after computing the minutes avoids - ** overflow problems when the input time value (seconds) is - ** at, or near, 0xFFFFFFFF... - */ - while (NumberOfSeconds >= 60) - { - NumberOfMinutes++; - NumberOfSeconds -= 60; - } - - /* - ** Compute the years/days/hours/minutes... - */ - NumberOfHours = (NumberOfMinutes / 60) + CFE_MISSION_TIME_EPOCH_HOUR; - NumberOfMinutes = (NumberOfMinutes % 60); - - /* - ** Unlike hours and minutes, epoch days are counted as Jan 1 = day 1... - */ - NumberOfDays = (NumberOfHours / 24) + (CFE_MISSION_TIME_EPOCH_DAY - 1); - NumberOfHours = (NumberOfHours % 24); - - NumberOfYears = CFE_MISSION_TIME_EPOCH_YEAR; - - /* - ** Convert total number of days into years and remainder days... - */ - while (StillCountingYears) - { - /* - ** Set number of days in this year (leap year?)... - */ - DaysInThisYear = 365; - - if ((NumberOfYears % 4) == 0) - { - if ((NumberOfYears % 100) != 0) - { - DaysInThisYear = 366; - } - else if ((NumberOfYears % 400) == 0) - { - DaysInThisYear = 366; - } - else - { - /* Do Nothing. Non-leap year. */ - } - } - - /* - ** When we have less than a years worth of days, we're done... - */ - if (NumberOfDays < DaysInThisYear) - { - StillCountingYears = false; - } - else - { - /* - ** Add a year and remove the number of days in that year... - */ - NumberOfYears++; - NumberOfDays -= DaysInThisYear; - } + return CFE_TIME_BAD_ARGUMENT; } - /* - ** Unlike hours and minutes, days are displayed as Jan 1 = day 1... - */ - NumberOfDays++; - - /* - ** After computing microseconds, convert to 5 digits from 6 digits... - */ - NumberOfMicros = NumberOfMicros / 10; - - /* - ** Build formatted output string (yyyy-ddd-hh:mm:ss.xxxxx)... - */ - *PrintBuffer++ = '0' + (char)(NumberOfYears / 1000); - NumberOfYears = NumberOfYears % 1000; - *PrintBuffer++ = '0' + (char)(NumberOfYears / 100); - NumberOfYears = NumberOfYears % 100; - *PrintBuffer++ = '0' + (char)(NumberOfYears / 10); - *PrintBuffer++ = '0' + (char)(NumberOfYears % 10); - *PrintBuffer++ = '-'; - - *PrintBuffer++ = '0' + (char)(NumberOfDays / 100); - NumberOfDays = NumberOfDays % 100; - *PrintBuffer++ = '0' + (char)(NumberOfDays / 10); - *PrintBuffer++ = '0' + (char)(NumberOfDays % 10); - *PrintBuffer++ = '-'; - - *PrintBuffer++ = '0' + (char)(NumberOfHours / 10); - *PrintBuffer++ = '0' + (char)(NumberOfHours % 10); - *PrintBuffer++ = ':'; - - *PrintBuffer++ = '0' + (char)(NumberOfMinutes / 10); - *PrintBuffer++ = '0' + (char)(NumberOfMinutes % 10); - *PrintBuffer++ = ':'; - - *PrintBuffer++ = '0' + (char)(NumberOfSeconds / 10); - *PrintBuffer++ = '0' + (char)(NumberOfSeconds % 10); - *PrintBuffer++ = '.'; - - *PrintBuffer++ = '0' + (char)(NumberOfMicros / 10000); - NumberOfMicros = NumberOfMicros % 10000; - *PrintBuffer++ = '0' + (char)(NumberOfMicros / 1000); - NumberOfMicros = NumberOfMicros % 1000; - *PrintBuffer++ = '0' + (char)(NumberOfMicros / 100); - NumberOfMicros = NumberOfMicros % 100; - *PrintBuffer++ = '0' + (char)(NumberOfMicros / 10); - *PrintBuffer++ = '0' + (char)(NumberOfMicros % 10); - *PrintBuffer++ = '\0'; + time_t sec = TimeToPrint.Seconds + CFE_MISSION_TIME_EPOCH_SECONDS; // epoch is Jan 1, 1980 + gmtime_r(&sec, &tm); + FmtLen = strftime(PrintBuffer, CFE_TIME_PRINTED_STRING_SIZE - 6, "%Y-%j-%H:%M:%S", &tm); + PrintBuffer += FmtLen; + *(PrintBuffer++) = '.'; + + *(PrintBuffer++) = '0' + (char)((Micros % 100000) / 10000); + *(PrintBuffer++) = '0' + (char)((Micros % 10000) / 1000); + *(PrintBuffer++) = '0' + (char)((Micros % 1000) / 100); + *(PrintBuffer++) = '0' + (char)((Micros % 100) / 10); + *(PrintBuffer++) = '0' + (char)(Micros % 10); + *PrintBuffer = '\0'; + return CFE_SUCCESS; } /*---------------------------------------------------------------- diff --git a/modules/time/ut-coverage/time_UT.c b/modules/time/ut-coverage/time_UT.c index 3a4961ac1..20161b427 100644 --- a/modules/time/ut-coverage/time_UT.c +++ b/modules/time/ut-coverage/time_UT.c @@ -821,7 +821,7 @@ void Test_ConvertTime(void) ** ** NOTE: Test results depend on the epoch values in cfe_mission_cfg.h (the ** tests below assume an epoch of 1980-001-00:00:00.00000). Full -** coverage is possible only when CFE_MISSION_TIME_EPOCH_SECOND > 0 +** coverage is possible only when CFE_MISSION_TIME_EPOCH_SECONDS > 0 */ void Test_Print(void) { @@ -834,8 +834,7 @@ void Test_Print(void) UtPrintf("Begin Test Print"); - if (CFE_MISSION_TIME_EPOCH_YEAR != 1980 || CFE_MISSION_TIME_EPOCH_DAY != 1 || CFE_MISSION_TIME_EPOCH_HOUR != 0 || - CFE_MISSION_TIME_EPOCH_MINUTE != 0 || CFE_MISSION_TIME_EPOCH_SECOND != 0 || CFE_MISSION_TIME_EPOCH_MICROS != 0) + if (CFE_MISSION_TIME_EPOCH_SECONDS != 0 || CFE_MISSION_TIME_EPOCH_MICROS != 0) { UtPrintf("Custom epoch time requires manual inspection for CFE_TIME_Print"); usingDefaultEpoch = false; @@ -843,11 +842,10 @@ void Test_Print(void) /* Test print with null print buffer argument */ UT_InitData(); - UtAssert_VOIDCALL(CFE_TIME_Print(NULL, time)); - CFE_UtAssert_SYSLOG(TIME_SYSLOG_MSGS[6]); + UtAssert_INT32_EQ(CFE_TIME_Print(NULL, time), CFE_TIME_BAD_ARGUMENT); /* Test with zero time value */ - CFE_TIME_Print(timeBuf, time); + CFE_UtAssert_SUCCESS(CFE_TIME_Print(timeBuf, time)); if (usingDefaultEpoch) { strcpy(expectedBuf, "1980-001-00:00:00.00000"); @@ -860,12 +858,12 @@ void Test_Print(void) } /* Test with a time value that causes seconds >= 60 when - * CFE_MISSION_TIME_EPOCH_SECOND > 0 + * CFE_MISSION_TIME_EPOCH_SECONDS > 0 */ time.Subseconds = 0; time.Seconds = 59; - CFE_TIME_Print(timeBuf, time); + CFE_UtAssert_SUCCESS(CFE_TIME_Print(timeBuf, time)); if (usingDefaultEpoch) { strcpy(expectedBuf, "1980-001-00:00:59.00000"); @@ -881,7 +879,7 @@ void Test_Print(void) time.Subseconds = 215000; time.Seconds = 1041472984; - CFE_TIME_Print(timeBuf, time); + CFE_UtAssert_SUCCESS(CFE_TIME_Print(timeBuf, time)); if (usingDefaultEpoch) { strcpy(expectedBuf, "2013-001-02:03:04.00005"); @@ -893,14 +891,14 @@ void Test_Print(void) (unsigned int)time.Seconds, (unsigned int)time.Subseconds, timeBuf); } - /* Test with maximum seconds and subseconds values */ - time.Subseconds = 0xffffffff; - time.Seconds = 0xffffffff; + /* Test with sufficiently-large seconds and subseconds values */ + time.Subseconds = 0x7fffffff; + time.Seconds = 0x7fffffff; - CFE_TIME_Print(timeBuf, time); + CFE_UtAssert_SUCCESS(CFE_TIME_Print(timeBuf, time)); if (usingDefaultEpoch) { - strcpy(expectedBuf, "2116-038-06:28:15.99999"); + strcpy(expectedBuf, "2048-019-03:14:07.49999"); UtAssert_STRINGBUF_EQ(timeBuf, sizeof(timeBuf), expectedBuf, sizeof(expectedBuf)); } else