Feature/date time mstimer clock (#861)

* Added daylight savings time calculation module with unit testing.

* Added datetime daylight savings time and clock API

* Added basic datetime_local() clock using mstimer as basis and time-sync option.  Integrated clock with ports/stm32f4xx example.
This commit is contained in:
Steve Karg
2024-11-24 11:20:25 -06:00
committed by GitHub
parent cdda524afc
commit fd3be47d86
19 changed files with 992 additions and 93 deletions
+2
View File
@@ -527,6 +527,8 @@ add_library(${PROJECT_NAME}
src/bacnet/basic/sys/days.h src/bacnet/basic/sys/days.h
src/bacnet/basic/sys/debug.c src/bacnet/basic/sys/debug.c
src/bacnet/basic/sys/debug.h src/bacnet/basic/sys/debug.h
src/bacnet/basic/sys/dst.c
src/bacnet/basic/sys/dst.h
src/bacnet/basic/sys/fifo.c src/bacnet/basic/sys/fifo.c
src/bacnet/basic/sys/fifo.h src/bacnet/basic/sys/fifo.h
src/bacnet/basic/sys/filename.c src/bacnet/basic/sys/filename.c
+5
View File
@@ -170,6 +170,7 @@ set(BACNET_PROJECT_SOURCE
${LIBRARY_BACNET_BASIC}/service/h_rd.c ${LIBRARY_BACNET_BASIC}/service/h_rd.c
${LIBRARY_BACNET_BASIC}/service/h_rp.c ${LIBRARY_BACNET_BASIC}/service/h_rp.c
${LIBRARY_BACNET_BASIC}/service/h_rpm.c ${LIBRARY_BACNET_BASIC}/service/h_rpm.c
${LIBRARY_BACNET_BASIC}/service/h_ts.c
${LIBRARY_BACNET_BASIC}/service/h_whohas.c ${LIBRARY_BACNET_BASIC}/service/h_whohas.c
${LIBRARY_BACNET_BASIC}/service/h_whois.c ${LIBRARY_BACNET_BASIC}/service/h_whois.c
${LIBRARY_BACNET_BASIC}/service/h_wp.c ${LIBRARY_BACNET_BASIC}/service/h_wp.c
@@ -178,6 +179,9 @@ set(BACNET_PROJECT_SOURCE
${LIBRARY_BACNET_BASIC}/service/s_ihave.c ${LIBRARY_BACNET_BASIC}/service/s_ihave.c
${LIBRARY_BACNET_BASIC}/tsm/tsm.c ${LIBRARY_BACNET_BASIC}/tsm/tsm.c
${LIBRARY_BACNET_BASIC}/sys/debug.c ${LIBRARY_BACNET_BASIC}/sys/debug.c
${LIBRARY_BACNET_BASIC}/sys/datetime_mstimer.c
${LIBRARY_BACNET_BASIC}/sys/days.c
${LIBRARY_BACNET_BASIC}/sys/dst.c
${LIBRARY_BACNET_BASIC}/sys/ringbuf.c ${LIBRARY_BACNET_BASIC}/sys/ringbuf.c
${LIBRARY_BACNET_BASIC}/sys/fifo.c ${LIBRARY_BACNET_BASIC}/sys/fifo.c
${LIBRARY_BACNET_BASIC}/sys/keylist.c ${LIBRARY_BACNET_BASIC}/sys/keylist.c
@@ -214,6 +218,7 @@ set(BACNET_PROJECT_SOURCE
${LIBRARY_BACNET_CORE}/rp.c ${LIBRARY_BACNET_CORE}/rp.c
${LIBRARY_BACNET_CORE}/rpm.c ${LIBRARY_BACNET_CORE}/rpm.c
${LIBRARY_BACNET_CORE}/timestamp.c ${LIBRARY_BACNET_CORE}/timestamp.c
${LIBRARY_BACNET_CORE}/timesync.c
${LIBRARY_BACNET_CORE}/weeklyschedule.c ${LIBRARY_BACNET_CORE}/weeklyschedule.c
${LIBRARY_BACNET_CORE}/dailyschedule.c ${LIBRARY_BACNET_CORE}/dailyschedule.c
${LIBRARY_BACNET_CORE}/calendar_entry.c ${LIBRARY_BACNET_CORE}/calendar_entry.c
+10 -5
View File
@@ -3,15 +3,15 @@
#+-------------------------------------------------------------------------------------------------+ #+-------------------------------------------------------------------------------------------------+
TARGET=bacnet TARGET=bacnet
BACNET_DIR = ../.. BACNET_DIR = $(realpath ../..)
BACNET_SRC := $(BACNET_DIR)/src BACNET_SRC := $(BACNET_DIR)/src
BACNET_CORE := $(BACNET_SRC)/bacnet BACNET_CORE := $(BACNET_SRC)/bacnet
BACNET_BASIC := $(BACNET_CORE)/basic BACNET_BASIC := $(BACNET_CORE)/basic
BACNET_INCLUDE := $(BACNET_SRC) BACNET_INCLUDE := $(BACNET_SRC)
PLATFORM_DIR = . PLATFORM_DIR = $(realpath .)
LIBRARY_STM32 = ./external/STM32F4xx_StdPeriph_Driver/src LIBRARY_STM32 = $(realpath ./external/STM32F4xx_StdPeriph_Driver/src)
LIBRARY_STM32_INCLUDES = ./external/STM32F4xx_StdPeriph_Driver/inc LIBRARY_STM32_INCLUDES = $(realpath ./external/STM32F4xx_StdPeriph_Driver/inc)
LIBRARY_CMSIS = ./external/CMSIS LIBRARY_CMSIS = $(realpath ./external/CMSIS)
CSTACK_TOOL := $(BACNET_DIR)/tools/avstack/avstack.pl CSTACK_TOOL := $(BACNET_DIR)/tools/avstack/avstack.pl
MEMAP_TOOL := $(BACNET_DIR)/tools/memap/memap.py MEMAP_TOOL := $(BACNET_DIR)/tools/memap/memap.py
@@ -48,6 +48,7 @@ BASIC_SRC = \
$(BACNET_BASIC)/service/h_rd.c \ $(BACNET_BASIC)/service/h_rd.c \
$(BACNET_BASIC)/service/h_rp.c \ $(BACNET_BASIC)/service/h_rp.c \
$(BACNET_BASIC)/service/h_rpm.c \ $(BACNET_BASIC)/service/h_rpm.c \
$(BACNET_BASIC)/service/h_ts.c \
$(BACNET_BASIC)/service/h_whohas.c \ $(BACNET_BASIC)/service/h_whohas.c \
$(BACNET_BASIC)/service/h_whois.c \ $(BACNET_BASIC)/service/h_whois.c \
$(BACNET_BASIC)/service/h_wp.c \ $(BACNET_BASIC)/service/h_wp.c \
@@ -55,6 +56,9 @@ BASIC_SRC = \
$(BACNET_BASIC)/service/s_iam.c \ $(BACNET_BASIC)/service/s_iam.c \
$(BACNET_BASIC)/service/s_ihave.c \ $(BACNET_BASIC)/service/s_ihave.c \
$(BACNET_BASIC)/sys/debug.c \ $(BACNET_BASIC)/sys/debug.c \
$(BACNET_BASIC)/sys/datetime_mstimer.c \
$(BACNET_BASIC)/sys/days.c \
$(BACNET_BASIC)/sys/dst.c \
$(BACNET_BASIC)/sys/ringbuf.c \ $(BACNET_BASIC)/sys/ringbuf.c \
$(BACNET_BASIC)/sys/fifo.c \ $(BACNET_BASIC)/sys/fifo.c \
$(BACNET_BASIC)/sys/keylist.c \ $(BACNET_BASIC)/sys/keylist.c \
@@ -92,6 +96,7 @@ BACNET_SRC = \
$(BACNET_CORE)/rpm.c \ $(BACNET_CORE)/rpm.c \
$(BACNET_CORE)/special_event.c \ $(BACNET_CORE)/special_event.c \
$(BACNET_CORE)/timestamp.c \ $(BACNET_CORE)/timestamp.c \
$(BACNET_CORE)/timesync.c \
$(BACNET_CORE)/weeklyschedule.c \ $(BACNET_CORE)/weeklyschedule.c \
$(BACNET_CORE)/whohas.c \ $(BACNET_CORE)/whohas.c \
$(BACNET_CORE)/whois.c \ $(BACNET_CORE)/whois.c \
+7 -1
View File
@@ -14,8 +14,8 @@
#include "bacnet/datalink/datalink.h" #include "bacnet/datalink/datalink.h"
#include "bacnet/npdu.h" #include "bacnet/npdu.h"
#include "bacnet/basic/services.h" #include "bacnet/basic/services.h"
#include "bacnet/basic/services.h"
#include "bacnet/basic/tsm/tsm.h" #include "bacnet/basic/tsm/tsm.h"
#include "bacnet/datetime.h"
#include "bacnet/dcc.h" #include "bacnet/dcc.h"
#include "bacnet/iam.h" #include "bacnet/iam.h"
/* BACnet objects */ /* BACnet objects */
@@ -116,6 +116,12 @@ void bacnet_init(void)
SERVICE_CONFIRMED_REINITIALIZE_DEVICE, handler_reinitialize_device); SERVICE_CONFIRMED_REINITIALIZE_DEVICE, handler_reinitialize_device);
apdu_set_confirmed_handler( apdu_set_confirmed_handler(
SERVICE_CONFIRMED_WRITE_PROPERTY, handler_write_property); SERVICE_CONFIRMED_WRITE_PROPERTY, handler_write_property);
/* local time and date */
apdu_set_unconfirmed_handler(
SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION,
handler_timesync);
handler_timesync_set_callback_set(datetime_timesync);
datetime_init();
/* handle communication so we can shutup when asked */ /* handle communication so we can shutup when asked */
apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
handler_device_communication_control); handler_device_communication_control);
+76 -8
View File
@@ -18,6 +18,7 @@
#include "bacnet/bacstr.h" #include "bacnet/bacstr.h"
#include "bacnet/bacenum.h" #include "bacnet/bacenum.h"
#include "bacnet/apdu.h" #include "bacnet/apdu.h"
#include "bacnet/datetime.h"
#include "bacnet/dcc.h" #include "bacnet/dcc.h"
#include "bacnet/datalink/datalink.h" #include "bacnet/datalink/datalink.h"
#include "bacnet/version.h" #include "bacnet/version.h"
@@ -49,11 +50,12 @@ static struct my_object_functions {
read_property_function Object_Read_Property; read_property_function Object_Read_Property;
write_property_function Object_Write_Property; write_property_function Object_Write_Property;
rpm_property_lists_function Object_RPM_List; rpm_property_lists_function Object_RPM_List;
} Object_Table[] = { { OBJECT_DEVICE, NULL, /* don't init - recursive! */ } Object_Table[] = { { OBJECT_DEVICE, NULL,
Device_Count, Device_Index_To_Instance, /* don't init - recursive! */
Device_Valid_Object_Instance_Number, Device_Count, Device_Index_To_Instance,
Device_Object_Name, Device_Read_Property_Local, Device_Valid_Object_Instance_Number,
Device_Write_Property_Local, Device_Property_Lists }, Device_Object_Name, Device_Read_Property_Local,
Device_Write_Property_Local, Device_Property_Lists },
{ OBJECT_ANALOG_INPUT, Analog_Input_Init, Analog_Input_Count, { OBJECT_ANALOG_INPUT, Analog_Input_Init, Analog_Input_Count,
Analog_Input_Index_To_Instance, Analog_Input_Valid_Instance, Analog_Input_Index_To_Instance, Analog_Input_Valid_Instance,
Analog_Input_Object_Name, Analog_Input_Read_Property, Analog_Input_Object_Name, Analog_Input_Read_Property,
@@ -116,7 +118,9 @@ static const char *BACnet_Version = BACNET_VERSION_TEXT;
static uint8_t Device_UUID[16]; static uint8_t Device_UUID[16];
/* These three arrays are used by the ReadPropertyMultiple handler */ /* These three arrays are used by the ReadPropertyMultiple handler */
static const int Device_Properties_Required[] = { PROP_OBJECT_IDENTIFIER, static const int Device_Properties_Required[] = {
/* required properties for this object */
PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_SYSTEM_STATUS, PROP_VENDOR_NAME, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_SYSTEM_STATUS, PROP_VENDOR_NAME,
PROP_VENDOR_IDENTIFIER, PROP_MODEL_NAME, PROP_FIRMWARE_REVISION, PROP_VENDOR_IDENTIFIER, PROP_MODEL_NAME, PROP_FIRMWARE_REVISION,
PROP_APPLICATION_SOFTWARE_VERSION, PROP_PROTOCOL_VERSION, PROP_APPLICATION_SOFTWARE_VERSION, PROP_PROTOCOL_VERSION,
@@ -126,8 +130,17 @@ static const int Device_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_APDU_TIMEOUT, PROP_NUMBER_OF_APDU_RETRIES, PROP_DEVICE_ADDRESS_BINDING, PROP_APDU_TIMEOUT, PROP_NUMBER_OF_APDU_RETRIES, PROP_DEVICE_ADDRESS_BINDING,
PROP_DATABASE_REVISION, -1 }; PROP_DATABASE_REVISION, -1 };
static const int Device_Properties_Optional[] = { PROP_DESCRIPTION, static const int Device_Properties_Optional[] = {
PROP_LOCATION, PROP_MAX_MASTER, PROP_MAX_INFO_FRAMES, PROP_DEVICE_UUID, /* optional properties for this object */
PROP_DESCRIPTION,
PROP_LOCATION,
PROP_MAX_MASTER,
PROP_MAX_INFO_FRAMES,
PROP_DEVICE_UUID,
PROP_LOCAL_DATE,
PROP_LOCAL_TIME,
PROP_UTC_OFFSET,
PROP_DAYLIGHT_SAVINGS_STATUS,
-1 }; -1 };
static const int Device_Properties_Proprietary[] = { -1 }; static const int Device_Properties_Proprietary[] = { -1 };
@@ -686,6 +699,10 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
BACNET_BIT_STRING bit_string = { 0 }; BACNET_BIT_STRING bit_string = { 0 };
BACNET_CHARACTER_STRING char_string = { 0 }; BACNET_CHARACTER_STRING char_string = { 0 };
BACNET_OCTET_STRING octet_string = { 0 }; BACNET_OCTET_STRING octet_string = { 0 };
BACNET_DATE bdate;
BACNET_TIME btime;
int16_t utc_offset_minutes;
bool dst_active;
uint32_t i = 0; uint32_t i = 0;
uint32_t count = 0; uint32_t count = 0;
uint8_t *apdu = NULL; uint8_t *apdu = NULL;
@@ -817,6 +834,23 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
octetstring_init(&octet_string, Device_UUID, sizeof(Device_UUID)); octetstring_init(&octet_string, Device_UUID, sizeof(Device_UUID));
apdu_len = encode_application_octet_string(&apdu[0], &octet_string); apdu_len = encode_application_octet_string(&apdu[0], &octet_string);
break; break;
case PROP_LOCAL_TIME:
datetime_local(&bdate, &btime, &utc_offset_minutes, &dst_active);
apdu_len = encode_application_time(&apdu[0], &btime);
break;
case PROP_LOCAL_DATE:
datetime_local(&bdate, &btime, &utc_offset_minutes, &dst_active);
apdu_len = encode_application_date(&apdu[0], &bdate);
break;
case PROP_UTC_OFFSET:
datetime_local(&bdate, &btime, &utc_offset_minutes, &dst_active);
apdu_len = encode_application_signed(&apdu[0], utc_offset_minutes);
break;
case PROP_DAYLIGHT_SAVINGS_STATUS:
datetime_local(&bdate, &btime, &utc_offset_minutes, &dst_active);
apdu_len =
encode_application_boolean(&apdu[0], dst_active);
break;
default: default:
rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
@@ -1060,6 +1094,39 @@ bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data)
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
} }
break; break;
case PROP_LOCAL_TIME:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_TIME);
if (status) {
status = datetime_time_is_valid(&value.type.Time);
if (status) {
datetime_timesync(NULL, &value.type.Time, false);
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
case PROP_LOCAL_DATE:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_DATE);
if (status) {
status = datetime_date_is_valid(&value.type.Date);
if (status) {
datetime_timesync(&value.type.Date, NULL, false);
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
case PROP_UTC_OFFSET:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_SIGNED_INT);
if (status) {
datetime_utc_offset_minutes_set(value.type.Signed_Int);
}
break;
case PROP_OBJECT_TYPE: case PROP_OBJECT_TYPE:
case PROP_VENDOR_NAME: case PROP_VENDOR_NAME:
case PROP_VENDOR_IDENTIFIER: case PROP_VENDOR_IDENTIFIER:
@@ -1076,6 +1143,7 @@ bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data)
case PROP_DEVICE_ADDRESS_BINDING: case PROP_DEVICE_ADDRESS_BINDING:
case PROP_ACTIVE_COV_SUBSCRIPTIONS: case PROP_ACTIVE_COV_SUBSCRIPTIONS:
case PROP_DATABASE_REVISION: case PROP_DATABASE_REVISION:
case PROP_DAYLIGHT_SAVINGS_STATUS:
wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
break; break;
+262
View File
@@ -0,0 +1,262 @@
/**
* @file
* @brief API for Milliseconds Timer based Time-of-Day Clock
* @author Steve Karg <skarg@users.sourceforge.net>
* @date 2024
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
*/
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
/* BACnet Stack defines - first */
#include "bacnet/bacdef.h"
/* BACnet Stack API */
#include "bacnet/basic/sys/dst.h"
#include "bacnet/basic/sys/mstimer.h"
#include "bacnet/datetime.h"
/* local time */
static BACNET_DATE_TIME BACnet_Date_Time;
static int16_t UTC_Offset_Minutes;
/* starting and stopping dates/times to determine DST */
static struct daylight_savings_data DST_Range;
static bool DST_Enabled;
/* local time based on mstimer */
static struct mstimer Date_Timer;
/**
* @brief Synchronize the local time from the millisecond timer
*/
static void datetime_sync(void)
{
bacnet_time_t seconds, elapsed_seconds;
unsigned long milliseconds;
milliseconds = mstimer_elapsed(&Date_Timer);
elapsed_seconds = milliseconds / 1000UL;
if (elapsed_seconds) {
mstimer_restart(&Date_Timer);
seconds = datetime_seconds_since_epoch(&BACnet_Date_Time);
seconds += elapsed_seconds;
datetime_since_epoch_seconds(&BACnet_Date_Time, seconds);
/* generate a hundredths value */
milliseconds -= (elapsed_seconds * 1000UL);
BACnet_Date_Time.time.hundredths = milliseconds / 10;
}
}
/**
* @brief Get the local determination of daylight savings time being active
* @return true if DST is active, false otherwise
*/
static bool datetime_dst_active(
struct daylight_savings_data *dst,
BACNET_DATE_TIME *bdatetime,
bool enabled)
{
bool active = false;
if (enabled) {
active = dst_active(
dst, bdatetime->date.year, bdatetime->date.month,
bdatetime->date.day, bdatetime->time.hour, bdatetime->time.min,
bdatetime->time.sec);
}
return active;
}
/**
* @brief Get the local date and time
* @param bdate [out] The date to get
* @param btime [out] The time to get
* @param utc_offset_minutes [out] The UTC offset in minutes
* @param dst_active [out] The DST flag
* @return true if successful, false on error
*/
bool datetime_local(
BACNET_DATE *bdate,
BACNET_TIME *btime,
int16_t *utc_offset_minutes,
bool *dst_active)
{
datetime_sync();
if (bdate) {
datetime_copy_date(bdate, &BACnet_Date_Time.date);
}
if (btime) {
datetime_copy_time(btime, &BACnet_Date_Time.time);
}
if (utc_offset_minutes) {
*utc_offset_minutes = UTC_Offset_Minutes;
}
if (dst_active) {
*dst_active =
datetime_dst_active(&DST_Range, &BACnet_Date_Time, DST_Enabled);
}
return true;
}
/**
* @brief Get the UTC offset in minutes
* @return The UTC offset in minutes
*/
int16_t datetime_utc_offset_minutes(void)
{
return UTC_Offset_Minutes;
}
/**
* @brief Set the UTC offset in minutes
* @param minutes [in] The UTC offset in minutes
* @return true if successful, false on error
*/
bool datetime_utc_offset_minutes_set(int16_t minutes)
{
UTC_Offset_Minutes = minutes;
return true;
}
/**
* @brief Get the Daylight Savings Enabled flag
* @return Daylight Savings Enabled flag
*/
bool datetime_dst_enabled(void)
{
return DST_Enabled;
}
/**
* @brief Set the Daylight Savings Enabled flag
* @param flag [in] The Daylight Savings Enabled flag
*/
void datetime_dst_enabled_set(bool flag)
{
DST_Enabled = flag;
}
/**
* @brief Get the local DST start and end date range
* @param dst_range [out] The DST range to get
* @return true if successful, false on error
*/
bool datetime_dst_ordinal_range(
uint8_t *start_month,
uint8_t *start_week,
uint8_t *start_day,
uint8_t *end_month,
uint8_t *end_week,
uint8_t *end_day)
{
if (!DST_Range.Ordinal) {
return false;
}
*start_month = DST_Range.Begin_Month;
*start_week = DST_Range.Begin_Week;
*start_day = DST_Range.Begin_Day;
*end_month = DST_Range.End_Month;
*end_week = DST_Range.End_Week;
*end_day = DST_Range.End_Day;
return true;
}
bool datetime_dst_ordinal_range_set(
uint8_t start_month,
uint8_t start_week,
BACNET_WEEKDAY start_day,
uint8_t end_month,
uint8_t end_week,
BACNET_WEEKDAY end_day)
{
DST_Range.Ordinal = true;
DST_Range.Begin_Month = start_month;
DST_Range.Begin_Week = start_week;
DST_Range.Begin_Day = start_day;
DST_Range.End_Month = end_month;
DST_Range.End_Week = end_week;
DST_Range.End_Day = end_day;
return true;
}
/**
* @brief Get the local DST start and end date range for specific month day
* @param dst_range [in] The DST range
* @return true if the start and end month day values are returned
*/
bool datetime_dst_date_range(
uint8_t *start_month,
uint8_t *start_day,
uint8_t *end_month,
uint8_t *end_day)
{
if (DST_Range.Ordinal) {
return false;
}
*start_month = DST_Range.Begin_Month;
*start_day = DST_Range.Begin_Day;
*end_month = DST_Range.End_Month;
*end_day = DST_Range.End_Day;
return true;
}
/**
* @brief Set the local DST start and end date range for specific month day
* @param dst_range [in] The DST range to set
* @return true if successful, false on error
*/
bool datetime_dst_date_range_set(
uint8_t start_month, uint8_t start_day, uint8_t end_month, uint8_t end_day)
{
DST_Range.Ordinal = false;
DST_Range.Begin_Month = start_month;
DST_Range.Begin_Day = start_day;
DST_Range.End_Month = end_month;
DST_Range.End_Day = end_day;
return true;
}
/**
* @brief Set the local date and time from a BACnet TimeSynchronization request
* @param bdate [in] The date to set
* @param btime [in] The time to set
* @param utc [in] true if originating from an UTCTimeSynchronization request
*/
void datetime_timesync(BACNET_DATE *bdate, BACNET_TIME *btime, bool utc)
{
BACNET_DATE_TIME local_time = { 0 };
const int32_t dst_adjust_minutes = 60L;
if (utc) {
if (bdate && btime) {
datetime_copy_date(&local_time.date, bdate);
datetime_copy_time(&local_time.time, btime);
datetime_add_minutes(&local_time, UTC_Offset_Minutes);
if (datetime_dst_active(&DST_Range, &local_time, DST_Enabled)) {
datetime_add_minutes(&local_time, dst_adjust_minutes);
}
datetime_copy(&BACnet_Date_Time, &local_time);
mstimer_restart(&Date_Timer);
}
} else {
datetime_copy_date(&BACnet_Date_Time.date, bdate);
datetime_copy_time(&BACnet_Date_Time.time, btime);
mstimer_restart(&Date_Timer);
}
}
/**
* @brief Initialize the local date and time timer
*/
void datetime_init(void)
{
dst_init_defaults(&DST_Range);
mstimer_set(&Date_Timer, 0);
}
+21 -3
View File
@@ -221,6 +221,8 @@ days_since_epoch(uint16_t epoch_year, uint16_t year, uint8_t month, uint8_t day)
days += days_per_month(year, mm); days += days_per_month(year, mm);
} }
days += day; days += day;
/* 'days since' is one less */
days -= 1;
} }
return (days); return (days);
@@ -243,15 +245,15 @@ void days_since_epoch_to_date(
uint8_t *pDay) uint8_t *pDay)
{ {
uint8_t month = 1; uint8_t month = 1;
uint8_t day = 0; uint8_t day = 1;
uint16_t year; uint16_t year;
year = epoch_year; year = epoch_year;
while (days > days_per_year(year)) { while (days >= days_per_year(year)) {
days -= days_per_year(year); days -= days_per_year(year);
year++; year++;
} }
while (days > days_per_month(year, month)) { while (days >= days_per_month(year, month)) {
days -= days_per_month(year, month); days -= days_per_month(year, month);
month++; month++;
} }
@@ -291,3 +293,19 @@ bool days_date_is_valid(uint16_t year, uint8_t month, uint8_t day)
return (valid); return (valid);
} }
/**
* Returns the day of the week value
*
* @param epoch_day - day of week for epoch day
* @param days - number of days since epoch
* @return day of week 1..7 offset by epoch day
*/
uint8_t days_of_week(uint8_t epoch_day, uint32_t days)
{
uint8_t dow = epoch_day;
dow += (days % 7);
return dow;
}
+3
View File
@@ -48,6 +48,9 @@ void days_since_epoch_to_date(
uint8_t *pMonth, uint8_t *pMonth,
uint8_t *pDay); uint8_t *pDay);
BACNET_STACK_EXPORT
uint8_t days_of_week(uint8_t epoch_day, uint32_t days);
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
bool days_date_is_valid(uint16_t year, uint8_t month, uint8_t day); bool days_date_is_valid(uint16_t year, uint8_t month, uint8_t day);
+229
View File
@@ -0,0 +1,229 @@
/**
* @file
* @brief computes whether day is during daylight savings time
* @note Public domain algorithms from ACM
* @author Steve Karg <skarg@users.sourceforge.net>
* @date 1997
* @copyright SPDX-License-Identifier: CC-PDDC
*/
#include <stdint.h>
#include <stdbool.h>
#include "days.h"
#include "dst.h"
/**
* This function returns the number of seconds after midnight
*
* @param hours - hours after midnight (0..23)
* @param minutes - minutes after hour (0..59)
* @param seconds - holds seconds after minute (0..59)
*
* @return true if date-time falls in DST. false if not.
*/
static uint32_t
time_to_seconds(uint32_t hours, uint32_t minutes, uint32_t seconds)
{
return (((hours) * 60 * 60) + ((minutes) * 60) + (seconds));
}
/**
* This function returns the day of the month for starting the Nth week
*
* @param year - Year of our Lord A.D. (1900..9999)
* @param month - months of the year (1=Jan,...,12=Dec)
* @param ordinal - Ordinal Day of the Month
* 1=1st, 2=2nd, 3=3rd, 4=4th, or 5=LAST
* @return day of the month (1..31)
*/
static uint8_t
ordinal_week_month_day(uint16_t year, uint8_t month, uint8_t ordinal)
{
uint8_t day = 0;
if (ordinal == 5) {
/* last week of the month */
day = days_per_month(year, month) - 6;
} else {
if (ordinal) {
ordinal--;
day = 1 + (ordinal * 7);
}
}
return day;
}
/**
* This function returns true if the date-time is during DST
*
* @param year - Year of our Lord A.D. (2000,2001,..2099)
* @param month - months of the year (1=Jan,...,12=Dec)
* @param day - day of the month (1..31)
* @param hour - hours after midnight (0..23)
* @param minute - minutes after hour (0..59)
* @param second - holds seconds after minute (0..59)
*
* @return true if date-time falls in DST. false if not.
*/
bool dst_active(
struct daylight_savings_data *data,
uint16_t year,
uint8_t month,
uint8_t day,
uint8_t hour,
uint8_t minute,
uint8_t second)
{
bool active = false;
uint8_t i = 0;
uint32_t time_now = 0;
uint32_t time_dst = 0;
uint8_t day_of_week = 0;
uint8_t days = 0;
uint32_t days_begin = 0;
uint32_t days_now = 0;
uint32_t days_end = 0;
if (data->Ordinal) {
if ((month >= data->Begin_Month) && (month <= data->End_Month)) {
if (month == data->Begin_Month) {
days = days_per_month(year, month);
i = ordinal_week_month_day(year, month, data->Begin_Week);
for (; i <= days; i++) {
day_of_week = days_of_week(
data->Epoch_Day,
days_since_epoch(data->Epoch_Year, year, month, i));
if (day_of_week == data->Begin_Day) {
if (day == i) {
time_now = time_to_seconds(hour, minute, second);
/* begins at 2 AM Standard Time */
time_dst = time_to_seconds(2, 0, 0);
if (time_now >= time_dst) {
active = true;
}
} else if (day > i) {
active = true;
}
break;
}
}
} else if (month == data->End_Month) {
days = days_per_month(year, month);
i = ordinal_week_month_day(year, month, data->End_Week);
for (; i <= days; i++) {
day_of_week = days_of_week(
data->Epoch_Day,
days_since_epoch(data->Epoch_Year, year, month, i));
if (day_of_week == data->End_Day) {
if (day == i) {
time_now = time_to_seconds(hour, minute, second);
/* ends at 2 AM Daylight time,
which is 1 AM Standard Time */
time_dst = time_to_seconds(1, 0, 0);
if (time_now < time_dst) {
active = true;
}
} else if (day < i) {
active = true;
}
break;
}
}
} else {
/* months between the beginning and end months */
active = true;
}
}
} else {
days_now = days_since_epoch(data->Epoch_Year, year, month, day);
days_begin = days_since_epoch(
data->Epoch_Year, year, data->Begin_Month, data->Begin_Day);
days_end = days_since_epoch(
data->Epoch_Year, year, data->End_Month, data->End_Day);
if ((days_now >= days_begin) && (days_now <= days_end)) {
if (days_now == days_begin) {
time_now = time_to_seconds(hour, minute, second);
/* begins at 2 AM Standard Time */
time_dst = time_to_seconds(2, 0, 0);
if (time_now >= time_dst) {
active = true;
}
} else if (days_now == days_end) {
time_now = time_to_seconds(hour, minute, second);
/* ends at 2 AM Daylight time,
which is 1 AM Standard Time */
time_dst = time_to_seconds(1, 0, 0);
if (time_now < time_dst) {
active = true;
}
} else {
active = true;
}
}
}
return active;
}
/**
* @brief This function sets the daylight savings time parameters
* @param data - daylight savings time data
* @param ordinal - true when ordinal day of month used, false if specific dates
* @param begin_month - month DST begins 1=Jan,...,12=Dec
* @param begin_day - day of the month DST begins
* 1..31 for specific day, or day of week 1..7 for ordinal day
* @param begin_which_day - which ordinal day of the month is used
* 1=1st, 2=2nd, 3=3rd, 4=4th, or 5=LAST
* @param end_month - month DST ends 1=Jan,...,12=Dec
* @param end_day - day of the month DST ends
* 1..31 for specific day, or day of week 1..7 for ordinal day
* @param end_which_day - which ordinal day of the month is used
* 1=1st, 2=2nd, 3=3rd, 4=4th, or 5=LAST
* @param epoch_day - day of the week for the BACnet Epoch (1=Monday..7=Sunday)
* @param epoch_year - year of the BACnet Epoch (1900..9999)
*/
void dst_init(
struct daylight_savings_data *data,
bool ordinal,
uint8_t begin_month,
uint8_t begin_day,
uint8_t begin_which_day,
uint8_t end_month,
uint8_t end_day,
uint8_t end_which_day,
uint8_t epoch_day,
uint16_t epoch_year)
{
if (data) {
data->Ordinal = ordinal;
data->Begin_Month = begin_month;
data->Begin_Day = begin_day;
data->Begin_Week = begin_which_day;
data->End_Month = end_month;
data->End_Day = end_day;
data->End_Week = end_which_day;
data->Epoch_Day = epoch_day;
data->Epoch_Year = epoch_year;
}
}
/**
* Initializes the daylight savings time parameters to their defaults.
*/
void dst_init_defaults(struct daylight_savings_data *data)
{
if (data) {
/* Starts: Second=2 Sunday=7 in March=3 */
/* Ends: First=1 Sunday=7 in November=11 */
data->Ordinal = true;
data->Begin_Month = 3;
data->Begin_Day = 7;
data->Begin_Week = 2;
data->End_Month = 11;
data->End_Day = 7;
data->End_Week = 1;
/* BACnet Epoch */
data->Epoch_Day = 1 /* Monday=1 */;
data->Epoch_Year = 1900;
}
}
+58
View File
@@ -0,0 +1,58 @@
/**
* @file
* @brief This file contains the function prototypes for for the module.
* @author Steve Karg <skarg@users.sourceforge.net>
* @date 1997
* @note Public domain algorithms from ACM
* @copyright SPDX-License-Identifier: CC-PDDC
*/
#ifndef BACNET_BASIC_SYS_DST_H
#define BACNET_BASIC_SYS_DST_H
#include <stdint.h>
#include <stdbool.h>
/* BACnet Stack defines - first */
#include "bacnet/bacdef.h"
struct daylight_savings_data {
bool Ordinal : 1;
uint8_t Begin_Month;
uint8_t Begin_Day;
uint8_t Begin_Week;
uint8_t End_Month;
uint8_t End_Day;
uint8_t End_Week;
uint16_t Epoch_Year;
uint8_t Epoch_Day;
};
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
bool dst_active(
struct daylight_savings_data *dst,
uint16_t year,
uint8_t month,
uint8_t day,
uint8_t hour,
uint8_t minute,
uint8_t second);
void dst_init(
struct daylight_savings_data *data,
bool automatic,
uint8_t begin_month,
uint8_t begin_day,
uint8_t begin_which_day,
uint8_t end_month,
uint8_t end_day,
uint8_t end_which_day,
uint8_t epoch_day,
uint16_t epoch_year);
/* initialization */
void dst_init_defaults(struct daylight_savings_data *data);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
-1
View File
@@ -884,7 +884,6 @@ void dlmstp_set_frame_rx_start_callback(dlmstp_hook_frame_rx_start_cb cb_func)
void dlmstp_reset_statistics(void) void dlmstp_reset_statistics(void)
{ {
struct dlmstp_user_data_t *user; struct dlmstp_user_data_t *user;
struct dlmstp_statistics *stats;
if (!MSTP_Port) { if (!MSTP_Port) {
return; return;
+5 -16
View File
@@ -141,21 +141,12 @@ uint32_t
datetime_ymd_to_days_since_epoch(uint16_t year, uint8_t month, uint8_t day) datetime_ymd_to_days_since_epoch(uint16_t year, uint8_t month, uint8_t day)
{ {
uint32_t days = 0; /* return value */ uint32_t days = 0; /* return value */
uint16_t years = 0; /* loop counter for years */
if (datetime_ymd_is_valid(year, month, day)) { if (datetime_ymd_is_valid(year, month, day)) {
for (years = BACNET_DATE_YEAR_EPOCH; years < year; years++) { days = days_since_epoch(BACNET_DATE_YEAR_EPOCH, year, month, day);
days += 365;
if (days_is_leap_year(years)) {
days++;
}
}
days += datetime_ymd_day_of_year(year, month, day);
/* 'days since' is one less */
days -= 1;
} }
return (days); return days;
} }
/** /**
@@ -248,11 +239,9 @@ void datetime_days_since_epoch_into_date(uint32_t days, BACNET_DATE *bdate)
*/ */
uint8_t datetime_day_of_week(uint16_t year, uint8_t month, uint8_t day) uint8_t datetime_day_of_week(uint16_t year, uint8_t month, uint8_t day)
{ {
uint8_t dow = (uint8_t)BACNET_DAY_OF_WEEK_EPOCH; return days_of_week(
BACNET_DAY_OF_WEEK_EPOCH,
dow += (datetime_ymd_to_days_since_epoch(year, month, day) % 7); datetime_ymd_to_days_since_epoch(year, month, day));
return dow;
} }
/** /**
+30 -3
View File
@@ -318,18 +318,45 @@ bool datetime_local(
BACNET_TIME *btime, BACNET_TIME *btime,
int16_t *utc_offset_minutes, int16_t *utc_offset_minutes,
bool *dst_active); bool *dst_active);
/* UTC Offset API */
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
int16_t datetime_utc_offset_minutes(void); int16_t datetime_utc_offset_minutes(void);
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
bool datetime_utc_offset_minutes_set(int16_t minutes); bool datetime_utc_offset_minutes_set(int16_t minutes);
/* Daylight Savings Time API */
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
bool datetime_dst_active(void); bool datetime_dst_enabled(void);
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
bool datetime_dst_range(BACNET_DATE_RANGE *dst_range); void datetime_dst_enabled_set(bool flag);
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
bool datetime_dst_range_set(BACNET_DATE_RANGE *dst_range); bool datetime_dst_ordinal_range(
uint8_t *start_month,
uint8_t *start_week,
uint8_t *start_day,
uint8_t *end_month,
uint8_t *end_week,
uint8_t *end_day);
BACNET_STACK_EXPORT
bool datetime_dst_ordinal_range_set(
uint8_t start_month,
uint8_t start_week,
BACNET_WEEKDAY start_day,
uint8_t end_month,
uint8_t end_week,
BACNET_WEEKDAY end_day);
BACNET_STACK_EXPORT
bool datetime_dst_date_range(
uint8_t *start_month,
uint8_t *start_day,
uint8_t *end_month,
uint8_t *end_day);
BACNET_STACK_EXPORT
bool datetime_dst_date_range_set(
uint8_t start_month, uint8_t start_day, uint8_t end_month, uint8_t end_day);
/* BACnet TimeSynchronization service handler API */
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
void datetime_timesync(BACNET_DATE *bdate, BACNET_TIME *btime, bool utc); void datetime_timesync(BACNET_DATE *bdate, BACNET_TIME *btime, bool utc);
/* Initialization for integration with a clock */
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
void datetime_init(void); void datetime_init(void);
+1
View File
@@ -171,6 +171,7 @@ list(APPEND testdirs
# basic/sys # basic/sys
bacnet/basic/sys/color_rgb bacnet/basic/sys/color_rgb
bacnet/basic/sys/days bacnet/basic/sys/days
bacnet/basic/sys/dst
bacnet/basic/sys/lighting_command bacnet/basic/sys/lighting_command
bacnet/basic/sys/fifo bacnet/basic/sys/fifo
bacnet/basic/sys/filename bacnet/basic/sys/filename
+3 -7
View File
@@ -1,12 +1,8 @@
/* @file /* @file
* @brief test BACnet integer encode/decode APIs
* @date June 2022
* @brief tests sRGB to and from from CIE xy and brightness API * @brief tests sRGB to and from from CIE xy and brightness API
* * @date June 2022
* @section LICENSE * @author Steve Karg <Steve Karg <skarg@users.sourceforge.net>
* Copyright (c) 2022 Steve Karg <skarg@users.sourceforge.net> * @copyright SPDX-License-Identifier: MIT
*
* SPDX-License-Identifier: MIT
*/ */
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
+100 -12
View File
@@ -1,16 +1,18 @@
/*
* Copyright (c) 2021 Steve Karg <skarg@users.sourceforge.net>
*
* SPDX-License-Identifier: MIT
*/
/* @file /* @file
* @brief test BACnet integer encode/decode APIs * @brief tests day of year calculations API
* @date August 2021
* @author Steve Karg <Steve Karg <skarg@users.sourceforge.net>
* @copyright SPDX-License-Identifier: MIT
*/ */
#include <zephyr/ztest.h> #include <zephyr/ztest.h>
#include <bacnet/basic/sys/days.h> #include <bacnet/basic/sys/days.h>
/* define our epic beginnings */
#define BACNET_EPOCH_YEAR 1900
/* 1/1/1900 is a Monday */
/* Monday=1..Sunday=7 */
#define BACNET_EPOCH_DOW 1
/** /**
* @addtogroup bacnet_tests * @addtogroup bacnet_tests
* @{ * @{
@@ -31,9 +33,43 @@ static void test_epoch_conversion_date(
days = days_since_epoch(epoch_year, year, month, day); days = days_since_epoch(epoch_year, year, month, day);
days_since_epoch_to_date( days_since_epoch_to_date(
epoch_year, days, &test_year, &test_month, &test_day); epoch_year, days, &test_year, &test_month, &test_day);
zassert_equal(year, test_year, NULL); zassert_equal(
zassert_equal(month, test_month, NULL); year, test_year, "date=%u/%u/%u year=%u test_year=%u", year, month, day,
zassert_equal(day, test_day, NULL); year, test_year);
zassert_equal(
month, test_month, "date=%u/%u/%u month=%u test_month=%u", year, month,
day, month, test_month);
zassert_equal(
day, test_day, "date=%u/%u/%u day=%u test_day=%u", year, month, day,
day, test_day);
}
/**
* Unit Test for the day of week based on epoch year and epoch day of week
* @param epoch_year - years after Christ birth (0..9999 AD)
* @param epoch_dow - day of week (1=Monday...7=Sunday)
* @param year - years after Christ birth (0..9999 AD)
* @param month - months (1=Jan...12=Dec)
* @param day - day of month (1-31)
* @param dow - day of week (1=Monday...7=Sunday)
*/
static void test_epoch_conversion_day(
uint16_t epoch_year,
uint8_t epoch_dow,
uint16_t year,
uint8_t month,
uint8_t day,
uint8_t dow)
{
uint32_t days;
uint16_t test_dow;
/* conversions of day and date */
days = days_since_epoch(epoch_year, year, month, day);
test_dow = days_of_week(epoch_dow, days);
zassert_equal(
dow, test_dow, "date=%u/%u/%u dow=%u test_dow=%u", year, month, day,
dow, test_dow);
} }
/** /**
@@ -45,13 +81,28 @@ ZTEST(days_tests, test_days_epoch_conversion)
static void test_days_epoch_conversion(void) static void test_days_epoch_conversion(void)
#endif #endif
{ {
const uint16_t epoch_year = 2000; const uint16_t epoch_year = BACNET_EPOCH_YEAR;
const uint8_t epoch_day_of_week = BACNET_EPOCH_DOW;
test_epoch_conversion_date(epoch_year, 2000, 1, 1); test_epoch_conversion_date(epoch_year, 2000, 1, 1);
test_epoch_conversion_date(epoch_year, 2048, 2, 28); test_epoch_conversion_date(epoch_year, 2048, 2, 28);
test_epoch_conversion_date(epoch_year, 2048, 2, 29); test_epoch_conversion_date(epoch_year, 2048, 2, 29);
test_epoch_conversion_date(epoch_year, 2038, 6, 15); test_epoch_conversion_date(epoch_year, 2038, 6, 15);
test_epoch_conversion_date(epoch_year, 9999, 12, 31); test_epoch_conversion_date(epoch_year, 9999, 12, 31);
test_epoch_conversion_day(
epoch_year, epoch_day_of_week, epoch_year, 1, 1, epoch_day_of_week);
/* some known day of week (1=Monday...7=Sunday) */
test_epoch_conversion_day(epoch_year, epoch_day_of_week, 2003, 1, 6, 1);
test_epoch_conversion_day(epoch_year, epoch_day_of_week, 2003, 1, 7, 2);
test_epoch_conversion_day(epoch_year, epoch_day_of_week, 2003, 1, 8, 3);
test_epoch_conversion_day(epoch_year, epoch_day_of_week, 2003, 1, 9, 4);
test_epoch_conversion_day(epoch_year, epoch_day_of_week, 2003, 1, 10, 5);
test_epoch_conversion_day(epoch_year, epoch_day_of_week, 2003, 1, 11, 6);
test_epoch_conversion_day(epoch_year, epoch_day_of_week, 2003, 1, 12, 7);
test_epoch_conversion_day(epoch_year, epoch_day_of_week, 2003, 1, 13, 1);
/* 50th wedding anniversary */
test_epoch_conversion_day(epoch_year, epoch_day_of_week, 2043, 6, 26, 5);
} }
/** /**
@@ -134,6 +185,42 @@ static void test_days_date_is_valid(void)
zassert_equal(days_per_month(0, 0), 0, NULL); zassert_equal(days_per_month(0, 0), 0, NULL);
} }
/**
* Unit Test for the days, checking the date to see if it is a valid date
*/
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(days_tests, test_days_since_epoch)
#else
static void test_days_since_epoch(void)
#endif
{
uint32_t days = 0;
uint16_t year = 0, test_year = 0;
uint8_t month = 0, test_month = 0;
uint8_t day = 0, test_day = 0;
days = days_since_epoch(BACNET_EPOCH_YEAR, BACNET_EPOCH_YEAR, 1, 1);
zassert_equal(days, 0, "days=%lu", (unsigned long)days);
days_since_epoch_to_date(BACNET_EPOCH_YEAR, days, &year, &month, &day);
zassert_equal(year, BACNET_EPOCH_YEAR, NULL);
zassert_equal(month, 1, NULL);
zassert_equal(day, 1, NULL);
for (year = BACNET_EPOCH_YEAR; year < (BACNET_EPOCH_YEAR + 0xFF); year++) {
for (month = 1; month <= 12; month++) {
for (day = 1; day <= days_per_month(year, month); day++) {
days = days_since_epoch(BACNET_EPOCH_YEAR, year, month, day);
days_since_epoch_to_date(
BACNET_EPOCH_YEAR, days, &test_year, &test_month,
&test_day);
zassert_equal(year, test_year, NULL);
zassert_equal(month, test_month, NULL);
zassert_equal(day, test_day, NULL);
}
}
}
}
/** /**
* Unit Test for days apart, checking the dates to see how many days apart * Unit Test for days apart, checking the dates to see how many days apart
*/ */
@@ -162,6 +249,7 @@ void test_main(void)
{ {
ztest_test_suite( ztest_test_suite(
days_tests, ztest_unit_test(test_days_epoch_conversion), days_tests, ztest_unit_test(test_days_epoch_conversion),
ztest_unit_test(test_days_since_epoch),
ztest_unit_test(test_days_of_year_to_md), ztest_unit_test(test_days_of_year_to_md),
ztest_unit_test(test_days_date_is_valid), ztest_unit_test(test_days_date_is_valid),
ztest_unit_test(test_days_apart)); ztest_unit_test(test_days_apart));
+42
View File
@@ -0,0 +1,42 @@
# SPDX-License-Identifier: MIT
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME)
project(test_${basename}
VERSION 1.0.0
LANGUAGES C)
string(REGEX REPLACE
"/test/bacnet/[a-zA-Z_/-]*$"
"/src"
SRC_DIR
${CMAKE_CURRENT_SOURCE_DIR})
string(REGEX REPLACE
"/test/bacnet/[a-zA-Z_/-]*$"
"/test"
TST_DIR
${CMAKE_CURRENT_SOURCE_DIR})
set(ZTST_DIR "${TST_DIR}/ztest/src")
add_compile_definitions(
BIG_ENDIAN=0
CONFIG_ZTEST=1
)
include_directories(
${SRC_DIR}
${TST_DIR}/ztest/include
)
add_executable(${PROJECT_NAME}
# File(s) under test
${SRC_DIR}/bacnet/basic/sys/dst.c
# Support files and stubs (pathname alphabetical)
${SRC_DIR}/bacnet/basic/sys/days.c
# Test and test library files
./src/main.c
${ZTST_DIR}/ztest_mock.c
${ZTST_DIR}/ztest.c
)
+137
View File
@@ -0,0 +1,137 @@
/* @file
* @brief tests daylight savings time validity API
* @date August 2021
* @author Steve Karg <Steve Karg <skarg@users.sourceforge.net>
* @copyright SPDX-License-Identifier: MIT
*/
#include <zephyr/ztest.h>
#include <bacnet/basic/sys/dst.h>
/**
* @addtogroup bacnet_tests
* @{
*/
/**
* Unit Test for daylight savings time
*/
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(dst_tests, dst_test_valid)
#else
static void dst_test_valid(void)
#endif
{
struct daylight_savings_data data = { 0 };
uint8_t epoch_day;
uint16_t epoch_year;
/* test at 3am */
uint8_t hour = 3;
uint8_t minute = 0;
uint8_t second = 0;
bool active;
unsigned i;
struct dst_test_data {
uint16_t year;
uint8_t month;
uint8_t day;
bool active;
} test_ordinal_data[] = {
/* start date boundary checking for several years */
{ 2007, 3, 10, false },
{ 2007, 3, 11, true },
{ 2008, 3, 8, false },
{ 2008, 3, 9, true },
{ 2009, 3, 7, false },
{ 2009, 3, 8, true },
{ 2010, 3, 13, false },
{ 2010, 3, 14, true },
{ 2011, 3, 12, false },
{ 2011, 3, 13, true },
{ 2012, 3, 10, false },
{ 2012, 3, 11, true },
{ 2013, 3, 9, false },
{ 2013, 3, 10, true },
{ 2014, 3, 8, false },
{ 2014, 3, 9, true },
{ 2015, 3, 7, false },
{ 2015, 3, 8, true },
/* end date boundary checking for several years */
{ 2007, 11, 3, true },
{ 2007, 11, 4, false },
{ 2008, 11, 1, true },
{ 2008, 11, 2, false },
{ 2009, 10, 31, true },
{ 2009, 11, 1, false },
{ 2010, 11, 6, true },
{ 2010, 11, 7, false },
{ 2011, 11, 5, true },
{ 2011, 11, 6, false },
{ 2012, 11, 3, true },
{ 2012, 11, 4, false },
{ 2013, 11, 2, true },
{ 2013, 11, 3, false },
{ 2014, 11, 1, true },
{ 2014, 11, 2, false },
{ 2015, 10, 31, true },
{ 2015, 11, 1, false },
/* year long check boundaries over a year */
{ 2013, 1, 1, false },
{ 2013, 3, 3, false },
{ 2013, 3, 7, false },
{ 2013, 3, 8, false },
{ 2013, 3, 9, false },
{ 2013, 3, 10, true },
{ 2013, 3, 11, true },
{ 2013, 3, 12, true },
{ 2013, 7, 10, true },
{ 2013, 11, 2, true },
{ 2013, 11, 3, false },
{ 2013, 11, 4, false },
{ 2013, 11, 7, false },
{ 2013, 11, 8, false },
{ 2013, 11, 30, false },
{ 2013, 12, 31, false },
};
struct dst_test_data *td;
dst_init_defaults(&data);
for (i = 0; i < ARRAY_SIZE(test_ordinal_data); i++) {
td = &test_ordinal_data[i];
active = dst_active(
&data, td->year, td->month, td->day, hour, minute, second);
zassert_true(active == td->active, NULL);
}
/* test the fixed dates */
epoch_day = data.Epoch_Day;
epoch_year = data.Epoch_Year;
dst_init(&data, false, 4, 1, 0, 9, 30, 0, epoch_day, epoch_year);
/* check the boundaries */
active = dst_active(&data, 2013, 3, 31, hour, minute, second);
zassert_true(active == false, NULL);
active = dst_active(&data, 2013, 4, 1, hour, minute, second);
zassert_true(active == true, NULL);
active = dst_active(&data, 2013, 4, 2, hour, minute, second);
zassert_true(active == true, NULL);
active = dst_active(&data, 2013, 9, 29, hour, minute, second);
zassert_true(active == true, NULL);
active = dst_active(&data, 2013, 9, 30, hour, minute, second);
zassert_true(active == false, NULL);
active = dst_active(&data, 2013, 10, 1, hour, minute, second);
zassert_true(active == false, NULL);
}
/**
* @}
*/
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST_SUITE(dst_tests, NULL, NULL, NULL, NULL, NULL);
#else
void test_main(void)
{
ztest_test_suite(dst_tests, ztest_unit_test(dst_test_valid));
ztest_run_test_suite(dst_tests);
}
#endif
+1 -37
View File
@@ -22,8 +22,6 @@
/* define our epic beginnings */ /* define our epic beginnings */
#define BACNET_EPOCH_YEAR 1900 #define BACNET_EPOCH_YEAR 1900
/* 1/1/1900 is a Monday */
#define BACNET_EPOCH_DOW BACNET_WEEKDAY_MONDAY
/** /**
* @addtogroup bacnet_tests * @addtogroup bacnet_tests
@@ -423,35 +421,6 @@ static void testDateEpochConversion(void)
BACNET_EPOCH_YEAR + 0xFF - 1, 12, 31, 23, 59, 59, 0); BACNET_EPOCH_YEAR + 0xFF - 1, 12, 31, 23, 59, 59, 0);
} }
static void testDateEpoch(void)
{
uint32_t days = 0;
uint16_t year = 0, test_year = 0;
uint8_t month = 0, test_month = 0;
uint8_t day = 0, test_day = 0;
days = days_since_epoch(BACNET_EPOCH_YEAR, BACNET_EPOCH_YEAR, 1, 1);
zassert_equal(days, 1, "days=%lu", (unsigned long)days);
days_since_epoch_to_date(BACNET_EPOCH_YEAR, days, &year, &month, &day);
zassert_equal(year, BACNET_EPOCH_YEAR, NULL);
zassert_equal(month, 1, NULL);
zassert_equal(day, 1, NULL);
for (year = BACNET_EPOCH_YEAR; year < (BACNET_EPOCH_YEAR + 0xFF); year++) {
for (month = 1; month <= 12; month++) {
for (day = 1; day <= days_per_month(year, month); day++) {
days = days_since_epoch(BACNET_EPOCH_YEAR, year, month, day);
days_since_epoch_to_date(
BACNET_EPOCH_YEAR, days, &test_year, &test_month,
&test_day);
zassert_equal(year, test_year, NULL);
zassert_equal(month, test_month, NULL);
zassert_equal(day, test_day, NULL);
}
}
}
}
#if defined(CONFIG_ZTEST_NEW_API) #if defined(CONFIG_ZTEST_NEW_API)
ZTEST(bacnet_datetime, testBACnetDayOfWeek) ZTEST(bacnet_datetime, testBACnetDayOfWeek)
#else #else
@@ -641,11 +610,6 @@ ZTEST_SUITE(bacnet_datetime, NULL, NULL, NULL, NULL, NULL);
#else #else
void test_main(void) void test_main(void)
{ {
#if 0
ztest_unit_test(testDateEpoch),
ztest_unit_test(testBACnetDateTimeSeconds),
ztest_unit_test(testDayOfYear),
#endif
ztest_test_suite( ztest_test_suite(
bacnet_datetime, ztest_unit_test(testBACnetDate), bacnet_datetime, ztest_unit_test(testBACnetDate),
ztest_unit_test(testBACnetTime), ztest_unit_test(testBACnetDateTime), ztest_unit_test(testBACnetTime), ztest_unit_test(testBACnetDateTime),
@@ -654,7 +618,7 @@ void test_main(void)
ztest_unit_test(testBACnetDateTimeAdd), ztest_unit_test(testBACnetDateTimeAdd),
ztest_unit_test(testBACnetDateTimeWildcard), ztest_unit_test(testBACnetDateTimeWildcard),
ztest_unit_test(testDatetimeCodec), ztest_unit_test(testDatetimeCodec),
ztest_unit_test(testWildcardDateTime), ztest_unit_test(testDateEpoch), ztest_unit_test(testWildcardDateTime),
ztest_unit_test(testBACnetDateTimeSeconds), ztest_unit_test(testBACnetDateTimeSeconds),
ztest_unit_test(testDayOfYear), ztest_unit_test(testDayOfYear),
ztest_unit_test(testDatetimeConvertUTC)); ztest_unit_test(testDatetimeConvertUTC));