diff --git a/src/bacnet/bacapp.c b/src/bacnet/bacapp.c index d12bc84b..081f8110 100644 --- a/src/bacnet/bacapp.c +++ b/src/bacnet/bacapp.c @@ -1865,15 +1865,20 @@ int bacapp_data_len( } #if defined(BACAPP_DATE) -/* 135.1-4.4 Notational Rules for Parameter Values -(j) -dates are represented enclosed in parenthesis: -(Monday, 24-January-1998). -Any "wild card" or unspecified field is shown by an asterisk (X'2A'): -(Monday, *-January-1998). -The omission of day of week implies that the day is unspecified: -(24-January-1998); -*/ +/** + * @brief Print a date value to a string for EPICS + * @param str - destination string, or NULL for length only + * @param str_len - length of the destination string, or 0 for length only + * @param bdate - date value to print + * @return number of characters written + * @note 135.1-4.4 Notational Rules for Parameter Values + * (j) dates are represented enclosed in parenthesis: + * (Monday, 24-January-1998). + * Any "wild card" or unspecified field is shown by an asterisk (X'2A'): + * (Monday, *-January-1998). + * The omission of day of week implies that the day is unspecified: + * (24-January-1998); + */ static int bacapp_snprintf_date(char *str, size_t str_len, BACNET_DATE *bdate) { int ret_val = 0; @@ -1888,11 +1893,11 @@ static int bacapp_snprintf_date(char *str, size_t str_len, BACNET_DATE *bdate) slen = snprintf(str, str_len, "%s, %s", weekday_text, month_text); if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } ret_val += slen; if (bdate->day == 255) { @@ -1902,11 +1907,11 @@ static int bacapp_snprintf_date(char *str, size_t str_len, BACNET_DATE *bdate) } if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } ret_val += slen; if (bdate->year == 2155) { @@ -1921,11 +1926,17 @@ static int bacapp_snprintf_date(char *str, size_t str_len, BACNET_DATE *bdate) #endif #if defined(BACAPP_TIME) -/* 135.1-4.4 Notational Rules for Parameter Values -(k) -times are represented as hours, minutes, seconds, hundredths in the format -hh:mm:ss.xx: 2:05:44.00, 16:54:59.99. Any "wild card" field is shown by an -asterisk (X'2A'): 16:54:*.*; */ +/** + * @brief Print a time value to a string for EPICS + * @param str - destination string, or NULL for length only + * @param str_len - length of the destination string, or 0 for length only + * @param btime - date value to print + * @return number of characters written + * @note 135.1-4.4 Notational Rules for Parameter Values + * (k) times are represented as hours, minutes, seconds, hundredths + * in the format hh:mm:ss.xx: 2:05:44.00, 16:54:59.99. + * Any "wild card" field is shown by an asterisk (X'2A'): 16:54:*.*; + */ static int bacapp_snprintf_time(char *str, size_t str_len, BACNET_TIME *btime) { int ret_val = 0; @@ -1940,11 +1951,11 @@ static int bacapp_snprintf_time(char *str, size_t str_len, BACNET_TIME *btime) } if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } ret_val += slen; if (btime->min == 255) { @@ -1954,11 +1965,11 @@ static int bacapp_snprintf_time(char *str, size_t str_len, BACNET_TIME *btime) } if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } ret_val += slen; if (btime->sec == 255) { @@ -1968,11 +1979,11 @@ static int bacapp_snprintf_time(char *str, size_t str_len, BACNET_TIME *btime) } if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } ret_val += slen; if (btime->hundredths == 255) { @@ -1987,6 +1998,14 @@ static int bacapp_snprintf_time(char *str, size_t str_len, BACNET_TIME *btime) #endif #if defined(BACAPP_WEEKLY_SCHEDULE) +/** + * @brief Print a weekly schedule value to a string for EPICS + * @param str - destination string, or NULL for length only + * @param str_len - length of the destination string, or 0 for length only + * @param ws - weekly schedule value to print + * @param arrayIndex - index of the weekly schedule to print + * @return number of characters written + */ static int bacapp_snprintf_weeklyschedule(char *str, size_t str_len, BACNET_WEEKLY_SCHEDULE *ws, @@ -2027,11 +2046,11 @@ static int bacapp_snprintf_weeklyschedule(char *str, ret_val += slen; if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } for (wi = 0; wi < loopend; wi++) { @@ -2047,11 +2066,11 @@ static int bacapp_snprintf_weeklyschedule(char *str, ret_val += slen; if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } for (ti = 0; ti < ds->TV_Count; ti++) { @@ -2071,13 +2090,12 @@ static int bacapp_snprintf_weeklyschedule(char *str, ret_val += slen; if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } } - + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; + } bacnet_primitive_to_application_data_value( &dummyDataValue, &ds->Time_Values[ti].Value); dummyPropValue.value = &dummyDataValue; @@ -2088,11 +2106,11 @@ static int bacapp_snprintf_weeklyschedule(char *str, ret_val += slen; if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } if (ti < ds->TV_Count - 1) { @@ -2100,11 +2118,11 @@ static int bacapp_snprintf_weeklyschedule(char *str, ret_val += slen; if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } } } @@ -2114,11 +2132,11 @@ static int bacapp_snprintf_weeklyschedule(char *str, ret_val += slen; if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } } } @@ -2131,7 +2149,7 @@ static int bacapp_snprintf_weeklyschedule(char *str, /** * @brief Extract the value into a text string * @param str - the buffer to store the extracted value, or NULL for length - * @param str_len - the size of the buffer + * @param str_len - the size of the buffer, or 0 for length only * @param object_value - ptr to BACnet object value from which to extract str * @return number of bytes (excluding terminating NULL byte) that were stored * to the output string. @@ -2204,11 +2222,11 @@ int bacapp_snprintf_value( octet_str++; if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } ret_val += slen; } @@ -2222,11 +2240,11 @@ int bacapp_snprintf_value( slen = snprintf(str, str_len, "\""); if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } ret_val += slen; #if (__STDC_VERSION__ >= 199901L) && defined(__STDC_ISO_10646__) @@ -2250,11 +2268,11 @@ int bacapp_snprintf_value( slen = snprintf(str, str_len, "%lc", (wint_t)wc); if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } ret_val += slen; if (len > wclen) { @@ -2275,11 +2293,11 @@ int bacapp_snprintf_value( } if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } ret_val += slen; char_str++; @@ -2295,11 +2313,11 @@ int bacapp_snprintf_value( slen = snprintf(str, str_len, "{"); if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } ret_val += slen; for (i = 0; i < len; i++) { @@ -2308,22 +2326,22 @@ int bacapp_snprintf_value( slen = snprintf(str, str_len, "%s", bit ? "true" : "false"); if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } ret_val += slen; if (i < (len - 1)) { slen = snprintf(str, str_len, ","); if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } ret_val += slen; } @@ -2448,11 +2466,11 @@ int bacapp_snprintf_value( slen = snprintf(str, str_len, "("); if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } ret_val += slen; if (value->type.Object_Id.type <= BACNET_OBJECT_TYPE_LAST) { @@ -2468,11 +2486,11 @@ int bacapp_snprintf_value( } if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } ret_val += slen; slen = snprintf(str, str_len, "%lu)", @@ -2487,16 +2505,22 @@ int bacapp_snprintf_value( ret_val += slen; if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } } - + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; + } slen = snprintf(str, str_len, ".."); ret_val += slen; - + if (str) { + str += slen; + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; + } slen = bacapp_snprintf_date( str, str_len, &value->type.Date_Range.enddate); ret_val += slen; @@ -2504,17 +2528,8 @@ int bacapp_snprintf_value( #endif #if defined(BACAPP_TIMESTAMP) case BACNET_APPLICATION_TAG_TIMESTAMP: - /*ISO 8601 format */ - slen = snprintf(str, str_len, - "%04u-%02u-%02uT%02u:%02u:%02u.%03u", - (unsigned)value->type.Time_Stamp.value.dateTime.date.year, - (unsigned)value->type.Time_Stamp.value.dateTime.date.month, - (unsigned)value->type.Time_Stamp.value.dateTime.date.day, - (unsigned)value->type.Time_Stamp.value.dateTime.time.hour, - (unsigned)value->type.Time_Stamp.value.dateTime.time.min, - (unsigned)value->type.Time_Stamp.value.dateTime.time.sec, - (unsigned) - value->type.Time_Stamp.value.dateTime.time.hundredths); + slen = bacapp_timestamp_to_ascii(str, str_len, + &value->type.Time_Stamp); ret_val += slen; break; #endif @@ -2525,11 +2540,21 @@ int bacapp_snprintf_value( ret_val += slen; if (str) { str += slen; - if (str_len >= slen) { - str_len -= slen; - } else { - str_len = 0; - } + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; + } + slen = snprintf(str, str_len, "-"); + ret_val += slen; + if (str) { + str += slen; + } + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; } slen = bacapp_snprintf_time( str, str_len, &value->type.Date_Time.time); diff --git a/src/bacnet/datetime.c b/src/bacnet/datetime.c index e4746aa6..a1ef8777 100644 --- a/src/bacnet/datetime.c +++ b/src/bacnet/datetime.c @@ -1344,6 +1344,30 @@ bool datetime_date_init_ascii(BACNET_DATE *bdate, const char *ascii) return status; } +/** + * @brief Print the date as an ascii string 2021/12/31 + * @param bdate - pointer to a BACnetDate + * @param str - pointer to the string, or NULL for length only + * @param str_size - size of the string, or 0 for length only + * @return number of characters printed + */ +int datetime_date_to_ascii(BACNET_DATE *bdate, char *str, size_t str_size) +{ + int str_len = 0; + + if (!bdate) { + return 0; + } + /* 2021/12/31 */ + str_len = snprintf(str, str_size, + "%04u/%02u/%02u", + (unsigned)bdate->year, + (unsigned)bdate->month, + (unsigned)bdate->day); + + return str_len; +} + /** * @brief Parse an ascii string for the time formatted 23:59:59.99 * @param btime - #BACNET_TIME structure @@ -1381,6 +1405,31 @@ bool datetime_time_init_ascii(BACNET_TIME *btime, const char *ascii) return status; } +/** + * @brief Print the time as an ascii string 23:59:59.99 + * @param btime - pointer to a BACnetTime + * @param str - pointer to the string, or NULL for length only + * @param str_size - size of the string, or 0 for length only + * @return number of characters printed + */ +int datetime_time_to_ascii(BACNET_TIME *btime, char *str, size_t str_size) +{ + int str_len = 0; + + if (!btime) { + return 0; + } + /* 23:59:59.99 */ + str_len = snprintf(str, str_size, + "%02u:%02u:%02u.%02u", + (unsigned)btime->hour, + (unsigned)btime->min, + (unsigned)btime->sec, + (unsigned)btime->hundredths); + + return str_len; +} + /** * @brief Parse an ascii string for the date+time 2021/12/31 23:59:59.99 * @param bdate - #BACNET_DATE_TIME structure @@ -1394,7 +1443,7 @@ bool datetime_init_ascii(BACNET_DATE_TIME *bdatetime, const char *ascii) int hour = 0, min = 0, sec = 0, hundredths = 0; int count = 0; - count = sscanf(ascii, "%4d/%3d/%3d %3d:%3d:%3d.%3d", &year, &month, &day, + count = sscanf(ascii, "%4d/%3d/%3d-%3d:%3d:%3d.%3d", &year, &month, &day, &hour, &min, &sec, &hundredths); if (count >= 3) { datetime_set_date( diff --git a/src/bacnet/datetime.h b/src/bacnet/datetime.h index 18b635ec..14595954 100644 --- a/src/bacnet/datetime.h +++ b/src/bacnet/datetime.h @@ -250,8 +250,12 @@ bool datetime_utc_to_local(BACNET_DATE_TIME *local_time, BACNET_STACK_EXPORT bool datetime_date_init_ascii(BACNET_DATE *bdate, const char *ascii); BACNET_STACK_EXPORT +int datetime_date_to_ascii(BACNET_DATE *bdate, char *str, size_t str_size); +BACNET_STACK_EXPORT bool datetime_time_init_ascii(BACNET_TIME *btime, const char *ascii); BACNET_STACK_EXPORT +int datetime_time_to_ascii(BACNET_TIME *btime, char *str, size_t str_size); +BACNET_STACK_EXPORT bool datetime_init_ascii(BACNET_DATE_TIME *bdatetime, const char *ascii); BACNET_STACK_EXPORT diff --git a/src/bacnet/timestamp.c b/src/bacnet/timestamp.c index a0be1e40..0ae0e890 100644 --- a/src/bacnet/timestamp.c +++ b/src/bacnet/timestamp.c @@ -31,9 +31,11 @@ License. ------------------------------------------- ####COPYRIGHTEND####*/ +#include #include -#include +#include #include "bacnet/bacapp.h" +#include "bacnet/datetime.h" #include "bacnet/timestamp.h" /** @file timestamp.c Encode/Decode BACnet Timestamps */ @@ -182,7 +184,6 @@ int bacapp_encode_timestamp(uint8_t *apdu, BACNET_TIMESTAMP *value) default: len = 0; - assert(len); break; } } @@ -457,3 +458,52 @@ bool bacapp_timestamp_init_ascii(BACNET_TIMESTAMP *timestamp, const char *ascii) return status; } + +/** + * @brief Print the timestamp to a string + * @param str - pointer to the string, or NULL for length only + * @param str_size - size of the string, or 0 for length only + * @param ts - pointer to the timestamp + * @return number of characters printed +*/ +int bacapp_timestamp_to_ascii(char *str, size_t str_size, + BACNET_TIMESTAMP *timestamp) +{ + int str_len = 0; + + if (!timestamp) { + return 0; + } + switch (timestamp->tag) { + case TIME_STAMP_TIME: + /* 23:59:59.99 */ + str_len = snprintf(str, str_size, + "%02u:%02u:%02u.%02u", + (unsigned)timestamp->value.time.hour, + (unsigned)timestamp->value.time.min, + (unsigned)timestamp->value.time.sec, + (unsigned)timestamp->value.time.hundredths); + break; + case TIME_STAMP_SEQUENCE: + /* 65535 */ + str_len = snprintf(str, str_size, "%u", + (unsigned)timestamp->value.sequenceNum); + break; + case TIME_STAMP_DATETIME: + /* 2021/12/31-23:59:59.99 */ + str_len = snprintf(str, str_size, + "%04u/%02u/%02u-%02u:%02u:%02u.%02u", + (unsigned)timestamp->value.dateTime.date.year, + (unsigned)timestamp->value.dateTime.date.month, + (unsigned)timestamp->value.dateTime.date.day, + (unsigned)timestamp->value.dateTime.time.hour, + (unsigned)timestamp->value.dateTime.time.min, + (unsigned)timestamp->value.dateTime.time.sec, + (unsigned)timestamp->value.dateTime.time.hundredths); + break; + default: + break; + } + + return str_len; +} diff --git a/src/bacnet/timestamp.h b/src/bacnet/timestamp.h index e828491f..bfa1e815 100644 --- a/src/bacnet/timestamp.h +++ b/src/bacnet/timestamp.h @@ -113,6 +113,11 @@ extern "C" { bool bacapp_timestamp_init_ascii( BACNET_TIMESTAMP *timestamp, const char *ascii); + BACNET_STACK_EXPORT + int bacapp_timestamp_to_ascii( + char *str, + size_t str_size, + BACNET_TIMESTAMP *timestamp); #ifdef __cplusplus } diff --git a/test/bacnet/datetime/src/main.c b/test/bacnet/datetime/src/main.c index b340da7b..39807851 100644 --- a/test/bacnet/datetime/src/main.c +++ b/test/bacnet/datetime/src/main.c @@ -520,6 +520,8 @@ static void testDatetimeCodec(void) int apdu_len; int test_len; int null_len; + int str_len; + char str[64]; int diff; bool status; @@ -541,6 +543,20 @@ static void testDatetimeCodec(void) zassert_true(apdu_len > 0, NULL); diff = datetime_compare(&datetimeOut, &datetimeIn); zassert_equal(diff, 0, NULL); + /* test time stringify */ + str_len = datetime_time_to_ascii(&datetimeIn.time, str, sizeof(str)); + zassert_true(str_len > 0, NULL); + status = datetime_time_init_ascii(&datetimeIn.time, str); + zassert_true(status, NULL); + diff = datetime_compare(&datetimeOut, &datetimeIn); + zassert_equal(diff, 0, NULL); + /* test date stringify */ + str_len = datetime_date_to_ascii(&datetimeIn.date, str, sizeof(str)); + zassert_true(str_len > 0, NULL); + status = datetime_date_init_ascii(&datetimeIn.date, str); + zassert_true(status, NULL); + diff = datetime_compare(&datetimeOut, &datetimeIn); + zassert_equal(diff, 0, NULL); /* test for invalid date tag */ apdu_len = bacapp_encode_datetime(apdu, &datetimeIn); encode_tag(apdu, BACNET_APPLICATION_TAG_REAL, false, 4); diff --git a/test/bacnet/timestamp/src/main.c b/test/bacnet/timestamp/src/main.c index f1a0ef5c..1878f73f 100644 --- a/test/bacnet/timestamp/src/main.c +++ b/test/bacnet/timestamp/src/main.c @@ -54,6 +54,8 @@ static void testTimestampTime(void) BACNET_TIMESTAMP testTimestampIn; BACNET_TIMESTAMP testTimestampOut; uint8_t buffer[MAX_APDU]; + bool status = false; + char str[64] = ""; int len; int test_len; @@ -78,6 +80,12 @@ static void testTimestampTime(void) testTimestampIn.value.time.sec, testTimestampOut.value.time.sec, NULL); zassert_equal(testTimestampIn.value.time.hundredths, testTimestampOut.value.time.hundredths, NULL); + + bacapp_timestamp_to_ascii(str, sizeof(str), &testTimestampIn); + status = bacapp_timestamp_init_ascii(&testTimestampOut, str); + zassert_true(status, NULL); + status = bacapp_timestamp_same(&testTimestampIn, &testTimestampOut); + zassert_true(status, NULL); } #if defined(CONFIG_ZTEST_NEW_API)