Feature/refactor datetime os dependency (#63)

* remove dependency on OS for time functions.

* add datetime epoch to and from seconds (yikes! 64-bit!). Add symmetric midnight seconds for unit test. Add unit tests.

* clean up BACnet date time warnings

* fix BACnet datetime warnings

Co-authored-by: Steve Karg <skarg@users.sourceforge.net>
This commit is contained in:
Steve Karg
2020-04-04 11:23:26 -05:00
committed by GitHub
parent b159402647
commit 0ce6368b43
7 changed files with 142 additions and 108 deletions
-5
View File
@@ -98,13 +98,8 @@ static void Devices_Init(uint32_t first_object_instance)
/* Now initialize the remote Device objects. */ /* Now initialize the remote Device objects. */
for (i = 1; i < MAX_NUM_DEVICES; i++) { for (i = 1; i < MAX_NUM_DEVICES; i++) {
#ifdef _MSC_VER
_snprintf(nameText, MAX_DEV_NAME_LEN, "%s %d", DEV_NAME_BASE, i + 1);
_snprintf(descText, MAX_DEV_DESC_LEN, "%s %d", DEV_DESCR_REMOTE, i);
#else
snprintf(nameText, MAX_DEV_NAME_LEN, "%s %d", DEV_NAME_BASE, i + 1); snprintf(nameText, MAX_DEV_NAME_LEN, "%s %d", DEV_NAME_BASE, i + 1);
snprintf(descText, MAX_DEV_DESC_LEN, "%s %d", DEV_DESCR_REMOTE, i); snprintf(descText, MAX_DEV_DESC_LEN, "%s %d", DEV_DESCR_REMOTE, i);
#endif
characterstring_init_ansi(&name_string, nameText); characterstring_init_ansi(&name_string, nameText);
Add_Routed_Device((first_object_instance + i), &name_string, descText); Add_Routed_Device((first_object_instance + i), &name_string, descText);
-5
View File
@@ -63,11 +63,6 @@ and globals in favor of more secure versions. */
#include <sys/time.h> #include <sys/time.h>
#endif #endif
#include <sys/timeb.h> #include <sys/timeb.h>
#if defined(__BORLANDC__) || defined(_WIN32)
/* seems to not be defined in time.h as specified by The Open Group */
/* difference from UTC and local standard time */
extern long int timezone;
#endif
#ifdef _MSC_VER #ifdef _MSC_VER
#define inline __inline #define inline __inline
+8 -66
View File
@@ -31,7 +31,6 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> /* for memmove */ #include <string.h> /* for memmove */
#include <time.h> /* for timezone, localtime */
/* OS specific include*/ /* OS specific include*/
#include "bacport.h" #include "bacport.h"
#include "bacnet/basic/sys/mstimer.h" #include "bacnet/basic/sys/mstimer.h"
@@ -41,6 +40,7 @@
#include "bacnet/bacenum.h" #include "bacnet/bacenum.h"
#include "bacnet/bacapp.h" #include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */ #include "bacnet/config.h" /* the custom stuff */
#include "bacnet/datetime.h"
#include "bacnet/apdu.h" #include "bacnet/apdu.h"
#include "bacnet/rp.h" /* ReadProperty handling */ #include "bacnet/rp.h" /* ReadProperty handling */
#include "bacnet/version.h" #include "bacnet/version.h"
@@ -53,12 +53,6 @@
/* include the device object */ /* include the device object */
#include "bacnet/basic/object/device.h" /* me */ #include "bacnet/basic/object/device.h" /* me */
#if defined(__BORLANDC__) || defined(_WIN32)
/* seems to not be defined in time.h as specified by The Open Group */
/* difference from UTC and local standard time */
long int timezone;
#endif
/* note: you really only need to define variables for /* note: you really only need to define variables for
properties that are writable or that may change. properties that are writable or that may change.
The properties that are constant can be hard coded The properties that are constant can be hard coded
@@ -83,14 +77,10 @@ static char *Description = "command line client";
/* static uint8_t Max_Segments_Accepted = 0; */ /* static uint8_t Max_Segments_Accepted = 0; */
/* VT_Classes_Supported */ /* VT_Classes_Supported */
/* Active_VT_Sessions */ /* Active_VT_Sessions */
static BACNET_TIME Local_Time; /* rely on OS, if there is one */ static BACNET_TIME Local_Time;
static BACNET_DATE Local_Date; /* rely on OS, if there is one */ static BACNET_DATE Local_Date;
/* NOTE: BACnet UTC Offset is inverse of common practice. static int16_t UTC_Offset;
If your UTC offset is -5hours of GMT, static bool Daylight_Savings_Status;
then BACnet UTC offset is +5hours.
BACnet UTC offset is expressed in minutes. */
static int32_t UTC_Offset = 5 * 60;
static bool Daylight_Savings_Status = false; /* rely on OS */
#if defined(BACNET_TIME_MASTER) #if defined(BACNET_TIME_MASTER)
static bool Align_Intervals; static bool Align_Intervals;
static uint32_t Interval_Minutes; static uint32_t Interval_Minutes;
@@ -696,57 +686,8 @@ bool Device_Object_Name_Copy(BACNET_OBJECT_TYPE object_type,
static void Update_Current_Time(void) static void Update_Current_Time(void)
{ {
struct tm *tblock = NULL; datetime_local(
#if defined(_MSC_VER) &Local_Date, &Local_Time, &UTC_Offset, &Daylight_Savings_Status);
time_t tTemp;
#else
struct timeval tv;
#endif
/*
struct tm
int tm_sec Seconds [0,60].
int tm_min Minutes [0,59].
int tm_hour Hour [0,23].
int tm_mday Day of month [1,31].
int tm_mon Month of year [0,11].
int tm_year Years since 1900.
int tm_wday Day of week [0,6] (Sunday =0).
int tm_yday Day of year [0,365].
int tm_isdst Daylight Savings flag.
*/
#if defined(_MSC_VER)
time(&tTemp);
tblock = (struct tm *)localtime(&tTemp);
#else
if (gettimeofday(&tv, NULL) == 0) {
tblock = (struct tm *)localtime((const time_t *)&tv.tv_sec);
}
#endif
if (tblock) {
datetime_set_date(&Local_Date, (uint16_t)tblock->tm_year + 1900,
(uint8_t)tblock->tm_mon + 1, (uint8_t)tblock->tm_mday);
#if !defined(_MSC_VER)
datetime_set_time(&Local_Time, (uint8_t)tblock->tm_hour,
(uint8_t)tblock->tm_min, (uint8_t)tblock->tm_sec,
(uint8_t)(tv.tv_usec / 10000));
#else
datetime_set_time(&Local_Time, (uint8_t)tblock->tm_hour,
(uint8_t)tblock->tm_min, (uint8_t)tblock->tm_sec, 0);
#endif
if (tblock->tm_isdst) {
Daylight_Savings_Status = true;
} else {
Daylight_Savings_Status = false;
}
/* note: timezone is declared in <time.h> stdlib. */
UTC_Offset = timezone / 60;
} else {
datetime_date_wildcard_set(&Local_Date);
datetime_time_wildcard_set(&Local_Time);
Daylight_Savings_Status = false;
}
} }
void Device_getCurrentDateTime(BACNET_DATE_TIME *DateTime) void Device_getCurrentDateTime(BACNET_DATE_TIME *DateTime)
@@ -1099,6 +1040,7 @@ void Device_Init(object_functions_t *object_table)
struct object_functions *pObject = NULL; struct object_functions *pObject = NULL;
characterstring_init_ansi(&My_Object_Name, "SimpleClient"); characterstring_init_ansi(&My_Object_Name, "SimpleClient");
datetime_init();
/* we don't use the object table passed in */ /* we don't use the object table passed in */
(void)object_table; (void)object_table;
pObject = &Object_Table[0]; pObject = &Object_Table[0];
+2 -8
View File
@@ -29,12 +29,12 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> /* for memmove */ #include <string.h> /* for memmove */
#include <time.h> /* for timezone, localtime */
#include "bacnet/bacdef.h" #include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h" #include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h" #include "bacnet/bacenum.h"
#include "bacnet/bacapp.h" #include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */ #include "bacnet/config.h" /* the custom stuff */
#include "bacnet/datetime.h"
#include "bacnet/apdu.h" #include "bacnet/apdu.h"
#include "bacnet/wp.h" /* WriteProperty handling */ #include "bacnet/wp.h" /* WriteProperty handling */
#include "bacnet/rp.h" /* ReadProperty handling */ #include "bacnet/rp.h" /* ReadProperty handling */
@@ -79,12 +79,6 @@
#include "bacnet/basic/ucix/ucix.h" #include "bacnet/basic/ucix/ucix.h"
#endif /* defined(BAC_UCI) */ #endif /* defined(BAC_UCI) */
#if defined(__BORLANDC__) || defined(_WIN32)
/* Not included in time.h as specified by The Open Group */
/* Difference from UTC and local standard time */
long int timezone;
#endif
/* local forward (semi-private) and external prototypes */ /* local forward (semi-private) and external prototypes */
int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata); int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata);
bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data); bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data);
@@ -1838,7 +1832,7 @@ void Device_Init(object_functions_t *object_table)
} }
ucix_cleanup(ctx); ucix_cleanup(ctx);
#endif /* defined(BAC_UCI) */ #endif /* defined(BAC_UCI) */
datetime_init();
if (object_table) { if (object_table) {
Object_Table = object_table; Object_Table = object_table;
} else { } else {
+3 -24
View File
@@ -30,7 +30,6 @@
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> /* for memcpy */ #include <string.h> /* for memcpy */
#include <time.h>
#include "bacnet/bacdef.h" #include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h" #include "bacnet/bacdcode.h"
#include "bacnet/datetime.h" #include "bacnet/datetime.h"
@@ -260,29 +259,9 @@ bool Load_Control_Object_Name(
static void Update_Current_Time(BACNET_DATE_TIME *bdatetime) static void Update_Current_Time(BACNET_DATE_TIME *bdatetime)
{ {
time_t timer; if (bdatetime) {
struct tm *tblock; datetime_local(&bdatetime->date, &bdatetime->time, NULL, NULL);
}
/*
struct tm {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
*/
timer = time(NULL);
tblock = localtime(&timer);
datetime_set_values(bdatetime, (uint16_t)tblock->tm_year,
(uint8_t)tblock->tm_mon, (uint8_t)tblock->tm_mday,
(uint8_t)tblock->tm_hour, (uint8_t)tblock->tm_min,
(uint8_t)tblock->tm_sec, 0);
} }
/* convert the shed level request into an Analog Output Present_Value */ /* convert the shed level request into an Analog Output Present_Value */
+116
View File
@@ -534,6 +534,22 @@ static void seconds_since_midnight_into_hms(
} }
} }
/**
* @brief Converts the number of seconds since midnight into BACnet Time
* @param seconds since midnight
* @param btime [in] BACNET_TIME containing the time to convert
*/
void datetime_seconds_since_midnight_into_time(
uint32_t seconds,
BACNET_TIME *btime)
{
if (btime) {
seconds_since_midnight_into_hms(seconds,
&btime->hour, &btime->min, &btime->sec);
btime->hundredths = 0;
}
}
/** Calculates the number of seconds since midnight /** Calculates the number of seconds since midnight
* *
* @param btime [in] BACNET_TIME containing the time to convert * @param btime [in] BACNET_TIME containing the time to convert
@@ -617,6 +633,54 @@ void datetime_add_minutes(BACNET_DATE_TIME *bdatetime, int32_t minutes)
datetime_days_since_epoch_into_date(bdatetime_days, &bdatetime->date); datetime_days_since_epoch_into_date(bdatetime_days, &bdatetime->date);
} }
#ifdef UINT64_MAX
/**
* @brief Calculates the number of seconds since epoch
* @param bdatetime [in] the starting date and time
* @return seconds since midnight
*/
uint64_t datetime_seconds_since_epoch(
BACNET_DATE_TIME * bdatetime)
{
uint64_t seconds = 0;
uint32_t days = 0;
if (bdatetime) {
days = datetime_days_since_epoch(&bdatetime->date);
seconds = seconds_since_midnight(24, 0, 0);
seconds *= days;
seconds += datetime_seconds_since_midnight(&bdatetime->time);
}
return seconds;
}
#endif
#ifdef UINT64_MAX
/**
* @brief Calculates the number of seconds since epoch
* @param bdatetime [in] the starting date and time
* @return seconds since midnight
*/
void datetime_since_epoch_seconds(
BACNET_DATE_TIME * bdatetime,
uint64_t seconds)
{
uint32_t seconds_after_midnight = 0;
uint32_t days = 0;
uint32_t day_seconds = 0;
if (bdatetime) {
day_seconds = seconds_since_midnight(24, 0, 0);
days = seconds / day_seconds;
seconds_after_midnight = seconds - (days * day_seconds);
datetime_seconds_since_midnight_into_time(
seconds_after_midnight, &bdatetime->time);
datetime_days_since_epoch_into_date(days, &bdatetime->date);
}
}
#endif
/* Returns true if year is a wildcard */ /* Returns true if year is a wildcard */
bool datetime_wildcard_year(BACNET_DATE *bdate) bool datetime_wildcard_year(BACNET_DATE *bdate)
{ {
@@ -1011,6 +1075,20 @@ int bacapp_decode_context_datetime(
#include <string.h> #include <string.h>
#include "ctest.h" #include "ctest.h"
static void datetime_print(const char *title,
BACNET_DATE_TIME *bdatetime)
{
printf("%s: %04u/%02u/%02u %02u:%02u:%02u.%03u\n",
title,
(unsigned int)bdatetime->date.year,
(unsigned int)bdatetime->date.month,
(unsigned int)bdatetime->date.wday,
(unsigned int)bdatetime->time.hour,
(unsigned int)bdatetime->time.min,
(unsigned int)bdatetime->time.sec,
(unsigned int)bdatetime->time.hundredths);
}
static void testBACnetDateTimeWildcard(Test *pTest) static void testBACnetDateTimeWildcard(Test *pTest)
{ {
BACNET_DATE_TIME bdatetime; BACNET_DATE_TIME bdatetime;
@@ -1323,6 +1401,42 @@ static void testDayOfYear(Test *pTest)
} }
} }
static void testDateEpochConversionCompare(Test *pTest,
uint16_t year, uint8_t month, uint8_t day,
uint8_t hour, uint8_t minute, uint8_t second, uint8_t hundredth)
{
uint64_t epoch_seconds = 0;
BACNET_DATE_TIME bdatetime = {0};
BACNET_DATE_TIME test_bdatetime = {0};
int compare = 0;
datetime_set_date(&bdatetime.date, year, month, day);
datetime_set_time(&bdatetime.time, hour, minute, second,
hundredth);
epoch_seconds = datetime_seconds_since_epoch(&bdatetime);
datetime_since_epoch_seconds(&test_bdatetime,
epoch_seconds);
compare = datetime_compare(&bdatetime,&test_bdatetime);
ct_test(pTest,compare == 0);
if (compare != 0) {
datetime_print("bdatetime", &bdatetime);
datetime_print("test_bdatetime", &test_bdatetime);
}
}
static void testDateEpochConversion(Test *pTest)
{
/* min */
testDateEpochConversionCompare(pTest,
BACNET_EPOCH_YEAR, 1, 1, 0, 0, 0, 0);
/* middle */
testDateEpochConversionCompare(pTest,
2020, 6, 26, 12, 30, 30, 0);
/* max */
testDateEpochConversionCompare(pTest,
BACNET_EPOCH_YEAR + 0xFF - 1, 12, 31, 23, 59, 59, 0);
}
static void testDateEpoch(Test *pTest) static void testDateEpoch(Test *pTest)
{ {
uint32_t days = 0; uint32_t days = 0;
@@ -1479,6 +1593,8 @@ void testDateTime(Test *pTest)
assert(rc); assert(rc);
rc = ct_addTestFunction(pTest, testDateEpoch); rc = ct_addTestFunction(pTest, testDateEpoch);
assert(rc); assert(rc);
rc = ct_addTestFunction(pTest, testDateEpochConversion);
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetDateTimeSeconds); rc = ct_addTestFunction(pTest, testBACnetDateTimeSeconds);
assert(rc); assert(rc);
rc = ct_addTestFunction(pTest, testBACnetDateTimeAdd); rc = ct_addTestFunction(pTest, testBACnetDateTimeAdd);
+13
View File
@@ -149,6 +149,11 @@ extern "C" {
uint16_t year, uint16_t year,
uint8_t month, uint8_t month,
uint8_t day); uint8_t day);
BACNET_STACK_EXPORT
void datetime_seconds_since_midnight_into_time(
uint32_t seconds,
BACNET_TIME *btime);
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
uint32_t datetime_seconds_since_midnight( uint32_t datetime_seconds_since_midnight(
BACNET_TIME * btime); BACNET_TIME * btime);
@@ -208,6 +213,14 @@ extern "C" {
BACNET_DATE_TIME * bdatetime, BACNET_DATE_TIME * bdatetime,
int32_t minutes); int32_t minutes);
BACNET_STACK_EXPORT
uint64_t datetime_seconds_since_epoch(
BACNET_DATE_TIME * bdatetime);
BACNET_STACK_EXPORT
void datetime_since_epoch_seconds(
BACNET_DATE_TIME * bdatetime,
uint64_t seconds);
/* date and time wildcards */ /* date and time wildcards */
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
bool datetime_wildcard_year( bool datetime_wildcard_year(