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:
@@ -98,13 +98,8 @@ static void Devices_Init(uint32_t first_object_instance)
|
||||
|
||||
/* Now initialize the remote Device objects. */
|
||||
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(descText, MAX_DEV_DESC_LEN, "%s %d", DEV_DESCR_REMOTE, i);
|
||||
#endif
|
||||
characterstring_init_ansi(&name_string, nameText);
|
||||
|
||||
Add_Routed_Device((first_object_instance + i), &name_string, descText);
|
||||
|
||||
@@ -63,11 +63,6 @@ and globals in favor of more secure versions. */
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#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
|
||||
#define inline __inline
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h> /* for memmove */
|
||||
#include <time.h> /* for timezone, localtime */
|
||||
/* OS specific include*/
|
||||
#include "bacport.h"
|
||||
#include "bacnet/basic/sys/mstimer.h"
|
||||
@@ -41,6 +40,7 @@
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/config.h" /* the custom stuff */
|
||||
#include "bacnet/datetime.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/rp.h" /* ReadProperty handling */
|
||||
#include "bacnet/version.h"
|
||||
@@ -53,12 +53,6 @@
|
||||
/* include the device object */
|
||||
#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
|
||||
properties that are writable or that may change.
|
||||
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; */
|
||||
/* VT_Classes_Supported */
|
||||
/* Active_VT_Sessions */
|
||||
static BACNET_TIME Local_Time; /* rely on OS, if there is one */
|
||||
static BACNET_DATE Local_Date; /* rely on OS, if there is one */
|
||||
/* NOTE: BACnet UTC Offset is inverse of common practice.
|
||||
If your UTC offset is -5hours of GMT,
|
||||
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 */
|
||||
static BACNET_TIME Local_Time;
|
||||
static BACNET_DATE Local_Date;
|
||||
static int16_t UTC_Offset;
|
||||
static bool Daylight_Savings_Status;
|
||||
#if defined(BACNET_TIME_MASTER)
|
||||
static bool Align_Intervals;
|
||||
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)
|
||||
{
|
||||
struct tm *tblock = NULL;
|
||||
#if defined(_MSC_VER)
|
||||
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;
|
||||
}
|
||||
datetime_local(
|
||||
&Local_Date, &Local_Time, &UTC_Offset, &Daylight_Savings_Status);
|
||||
}
|
||||
|
||||
void Device_getCurrentDateTime(BACNET_DATE_TIME *DateTime)
|
||||
@@ -1099,6 +1040,7 @@ void Device_Init(object_functions_t *object_table)
|
||||
struct object_functions *pObject = NULL;
|
||||
|
||||
characterstring_init_ansi(&My_Object_Name, "SimpleClient");
|
||||
datetime_init();
|
||||
/* we don't use the object table passed in */
|
||||
(void)object_table;
|
||||
pObject = &Object_Table[0];
|
||||
|
||||
@@ -29,12 +29,12 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h> /* for memmove */
|
||||
#include <time.h> /* for timezone, localtime */
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/config.h" /* the custom stuff */
|
||||
#include "bacnet/datetime.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/wp.h" /* WriteProperty handling */
|
||||
#include "bacnet/rp.h" /* ReadProperty handling */
|
||||
@@ -79,12 +79,6 @@
|
||||
#include "bacnet/basic/ucix/ucix.h"
|
||||
#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 */
|
||||
int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata);
|
||||
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);
|
||||
#endif /* defined(BAC_UCI) */
|
||||
|
||||
datetime_init();
|
||||
if (object_table) {
|
||||
Object_Table = object_table;
|
||||
} else {
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h> /* for memcpy */
|
||||
#include <time.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/datetime.h"
|
||||
@@ -260,29 +259,9 @@ bool Load_Control_Object_Name(
|
||||
|
||||
static void Update_Current_Time(BACNET_DATE_TIME *bdatetime)
|
||||
{
|
||||
time_t timer;
|
||||
struct tm *tblock;
|
||||
|
||||
/*
|
||||
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);
|
||||
if (bdatetime) {
|
||||
datetime_local(&bdatetime->date, &bdatetime->time, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* convert the shed level request into an Analog Output Present_Value */
|
||||
|
||||
@@ -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
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
#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 */
|
||||
bool datetime_wildcard_year(BACNET_DATE *bdate)
|
||||
{
|
||||
@@ -1011,6 +1075,20 @@ int bacapp_decode_context_datetime(
|
||||
#include <string.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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
uint32_t days = 0;
|
||||
@@ -1479,6 +1593,8 @@ void testDateTime(Test *pTest)
|
||||
assert(rc);
|
||||
rc = ct_addTestFunction(pTest, testDateEpoch);
|
||||
assert(rc);
|
||||
rc = ct_addTestFunction(pTest, testDateEpochConversion);
|
||||
assert(rc);
|
||||
rc = ct_addTestFunction(pTest, testBACnetDateTimeSeconds);
|
||||
assert(rc);
|
||||
rc = ct_addTestFunction(pTest, testBACnetDateTimeAdd);
|
||||
|
||||
@@ -149,6 +149,11 @@ extern "C" {
|
||||
uint16_t year,
|
||||
uint8_t month,
|
||||
uint8_t day);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void datetime_seconds_since_midnight_into_time(
|
||||
uint32_t seconds,
|
||||
BACNET_TIME *btime);
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t datetime_seconds_since_midnight(
|
||||
BACNET_TIME * btime);
|
||||
@@ -208,6 +213,14 @@ extern "C" {
|
||||
BACNET_DATE_TIME * bdatetime,
|
||||
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 */
|
||||
BACNET_STACK_EXPORT
|
||||
bool datetime_wildcard_year(
|
||||
|
||||
Reference in New Issue
Block a user