diff --git a/.gitignore b/.gitignore index a8abf78f..91a0547f 100644 --- a/.gitignore +++ b/.gitignore @@ -70,3 +70,42 @@ Release/ apps/piface/libmcp23s17/ apps/piface/libpifacedigital/ +/test/build/ +/cmake-build-* +/.idea + +/apps/abort/bacabort +/apps/ack-alarm/bacackalarm +/apps/readfile/bacarf +/apps/writefile/bacawf +/apps/dcc/bacdcc +/apps/epics/bacepics +/apps/error/bacerror +/apps/event/bacevent +/apps/getevent/bacge +/apps/iam/baciam +/apps/iamrouter/baciamr +/apps/initrouter/bacinitr +/apps/netnumis/bacnni +/apps/server-client/bacpoll +/apps/readbdt/bacrbdt +/apps/reinit/bacrd +/apps/readfdt/bacrfdt +/apps/readprop/bacrp +/apps/readpropm/bacrpm +/apps/readrange/bacrr +/apps/scov/bacscov +/apps/server/bacserv +/apps/timesync/bacts +/apps/ucov/bacucov +/apps/uevent/bacuevent +/apps/uptransfer/bacupt +/apps/writebdt/bacwbdt +/apps/whohas/bacwh +/apps/whois/bacwi +/apps/whatisnetnum/bacwinn +/apps/whoisrouter/bacwir +/apps/writeprop/bacwp +/apps/writepropm/bacwpm +/apps/mstpcap/mstpcap +/apps/mstpcrc/mstpcrc diff --git a/CMakeLists.txt b/CMakeLists.txt index 2eeda6bf..b51c0535 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,6 +124,10 @@ add_library(${PROJECT_NAME} src/bacnet/bactext.h src/bacnet/bactimevalue.c src/bacnet/bactimevalue.h + src/bacnet/dailyschedule.c + src/bacnet/dailyschedule.h + src/bacnet/weeklyschedule.c + src/bacnet/weeklyschedule.h $<$:src/bacnet/basic/bbmd/h_bbmd.c> $<$:src/bacnet/basic/bbmd/h_bbmd.h> $<$:src/bacnet/basic/bbmd6/h_bbmd6.c> diff --git a/apps/writeprop/main.c b/apps/writeprop/main.c index 2e8f3cf8..8648edf2 100644 --- a/apps/writeprop/main.c +++ b/apps/writeprop/main.c @@ -337,20 +337,46 @@ int main(int argc, char *argv[]) value_string = argv[tag_value_arg]; tag_value_arg++; args_remaining--; - /* printf("tag[%d]=%u value[%d]=%s\n", - i, property_tag, i, value_string); */ - if (property_tag >= MAX_BACNET_APPLICATION_TAG) { + if (property_tag < 0) { + property_tag = bacapp_known_property_tag(Target_Object_Type, Target_Object_Property); + } else if (property_tag >= MAX_BACNET_APPLICATION_TAG) { fprintf(stderr, "Error: tag=%u - it must be less than %u\n", property_tag, MAX_BACNET_APPLICATION_TAG); return 1; } - status = bacapp_parse_application_data( - property_tag, value_string, &Target_Object_Property_Value[i]); - if (!status) { + if (property_tag >= 0) { + status = bacapp_parse_application_data( + property_tag, value_string, &Target_Object_Property_Value[i]); + if (!status) { + /* FIXME: show the expected entry format for the tag */ + fprintf(stderr, "Error: unable to parse the tag value\n"); + return 1; + } + } else { /* FIXME: show the expected entry format for the tag */ - fprintf(stderr, "Error: unable to parse the tag value\n"); + fprintf(stderr, "Error: unable to parse the known property" + " \"%s\"\r\n", value_string); return 1; } + + // Print the written value (for debug) +#if 0 + fprintf(stderr, "Writing: "); + BACNET_OBJECT_PROPERTY_VALUE dummy_opv = { + .value = &Target_Object_Property_Value[i], + .array_index = Target_Object_Property_Index, + }; + bacapp_print_value(stderr, &dummy_opv); + fprintf(stderr, "\n"); + + uint8_t apdu[1000]; + int len = bacapp_encode_application_data(apdu, &Target_Object_Property_Value[i]); + for(int q=0;q 0) { Target_Object_Property_Value[i - 1].next = diff --git a/apps/writepropm/main.c b/apps/writepropm/main.c index 3f1c9ea8..ceeaf054 100644 --- a/apps/writepropm/main.c +++ b/apps/writepropm/main.c @@ -427,16 +427,25 @@ int main(int argc, char *argv[]) if (Verbose) { printf("tag=%u value=%s\n", property_tag, value_string); } - if (property_tag >= MAX_BACNET_APPLICATION_TAG) { + if (property_tag < 0) { + property_tag = bacapp_known_property_tag(wpm_object->object_type, wpm_property->propertyIdentifier); + } else if (property_tag >= MAX_BACNET_APPLICATION_TAG) { fprintf(stderr, "Error: tag=%u - it must be less than %u\n", property_tag, MAX_BACNET_APPLICATION_TAG); return 1; } - status = bacapp_parse_application_data( - property_tag, value_string, &wpm_property->value); - if (!status) { + if (property_tag >= 0) { + status = bacapp_parse_application_data( + property_tag, value_string, &wpm_property->value); + if (!status) { + /* FIXME: show the expected entry format for the tag */ + fprintf(stderr, "Error: unable to parse the tag value\n"); + return 1; + } + } else { /* FIXME: show the expected entry format for the tag */ - fprintf(stderr, "Error: unable to parse the tag value\n"); + fprintf(stderr, "Error: unable to parse the known property" + " \"%s\"\r\n", value_string); return 1; } wpm_property->value.next = NULL; diff --git a/ports/at91sam7s/Makefile b/ports/at91sam7s/Makefile index 6ad3ab44..5a0166e6 100644 --- a/ports/at91sam7s/Makefile +++ b/ports/at91sam7s/Makefile @@ -107,6 +107,9 @@ CORESRC = $(BACNET_CORE)/abort.c \ $(BACNET_CORE)/rp.c \ $(BACNET_CORE)/rpm.c \ $(BACNET_CORE)/timestamp.c \ + $(BACNET_CORE)/weeklyschedule.c \ + $(BACNET_CORE)/dailyschedule.c \ + $(BACNET_CORE)/bactimevalue.c \ $(BACNET_CORE)/whohas.c \ $(BACNET_CORE)/whois.c \ $(BACNET_CORE)/wp.c diff --git a/ports/stm32f10x/Makefile b/ports/stm32f10x/Makefile index dcc2c7d0..ce83287c 100644 --- a/ports/stm32f10x/Makefile +++ b/ports/stm32f10x/Makefile @@ -76,6 +76,9 @@ BACNET_SRC = \ $(BACNET_CORE)/rp.c \ $(BACNET_CORE)/rpm.c \ $(BACNET_CORE)/timestamp.c \ + $(BACNET_CORE)/weeklyschedule.c \ + $(BACNET_CORE)/dailyschedule.c \ + $(BACNET_CORE)/bactimevalue.c \ $(BACNET_CORE)/whohas.c \ $(BACNET_CORE)/whois.c \ $(BACNET_CORE)/wp.c diff --git a/ports/stm32f4xx/Makefile b/ports/stm32f4xx/Makefile index 054faa42..4258372f 100644 --- a/ports/stm32f4xx/Makefile +++ b/ports/stm32f4xx/Makefile @@ -74,6 +74,9 @@ BACNET_SRC = \ $(BACNET_CORE)/rp.c \ $(BACNET_CORE)/rpm.c \ $(BACNET_CORE)/timestamp.c \ + $(BACNET_CORE)/weeklyschedule.c \ + $(BACNET_CORE)/dailyschedule.c \ + $(BACNET_CORE)/bactimevalue.c \ $(BACNET_CORE)/whohas.c \ $(BACNET_CORE)/whois.c \ $(BACNET_CORE)/wp.c diff --git a/src/bacnet/bacapp.c b/src/bacnet/bacapp.c index cd26f65c..2cd7852c 100644 --- a/src/bacnet/bacapp.c +++ b/src/bacnet/bacapp.c @@ -37,6 +37,7 @@ #include #include /* for strtol */ #include /* for isalnum */ +#include #ifdef __STDC_ISO_10646__ #include #include @@ -52,6 +53,8 @@ #include "bacnet/bacstr.h" #include "bacnet/lighting.h" #include "bacnet/hostnport.h" +#include "bacnet/weeklyschedule.h" +#include "bacnet/basic/sys/platform.h" /** @file bacapp.c Utilities for the BACnet_Application_Data_Value */ @@ -171,6 +174,11 @@ int bacapp_encode_application_data( apdu_len = color_command_encode( apdu, &value->type.Color_Command); break; + case BACNET_APPLICATION_TAG_WEEKLY_SCHEDULE: + /* BACnetWeeklySchedule */ + apdu_len = bacnet_weeklyschedule_encode( + apdu, &value->type.Weekly_Schedule); + break; case BACNET_APPLICATION_TAG_HOST_N_PORT: /* BACnetHostNPort */ apdu_len = host_n_port_encode(apdu, @@ -318,6 +326,10 @@ int bacapp_decode_data(uint8_t *apdu, len = color_command_decode( apdu, len_value_type, NULL, &value->type.Color_Command); break; + case BACNET_APPLICATION_TAG_WEEKLY_SCHEDULE: + len = bacnet_weeklyschedule_decode( + apdu, len_value_type, &value->type.Weekly_Schedule); + break; case BACNET_APPLICATION_TAG_HOST_N_PORT: len = host_n_port_decode( apdu, len_value_type, NULL, @@ -638,6 +650,11 @@ int bacapp_encode_context_data_value(uint8_t *apdu, apdu_len = xy_color_context_encode( apdu, context_tag_number, &value->type.XY_Color); break; + case BACNET_APPLICATION_TAG_WEEKLY_SCHEDULE: + /* BACnetWeeklySchedule */ + apdu_len = bacnet_weeklyschedule_context_encode( + apdu, context_tag_number, &value->type.Weekly_Schedule); + break; case BACNET_APPLICATION_TAG_COLOR_COMMAND: /* BACnetColorCommand */ apdu_len = color_command_context_encode( @@ -1011,6 +1028,142 @@ static int decode_priority_value(uint8_t *apdu, #endif #if defined(BACAPP_TYPES_EXTRA) +int bacapp_known_property_tag( + BACNET_OBJECT_TYPE object_type, + BACNET_PROPERTY_ID property) +{ + switch (property) { + case PROP_MEMBER_OF: + case PROP_ZONE_MEMBERS: + case PROP_DOOR_MEMBERS: + case PROP_SUBORDINATE_LIST: + case PROP_ACCESS_EVENT_CREDENTIAL: + case PROP_ACCESS_DOORS: + case PROP_ZONE_FROM: + case PROP_ZONE_TO: + case PROP_CREDENTIALS_IN_ZONE: + case PROP_LAST_CREDENTIAL_ADDED: + case PROP_LAST_CREDENTIAL_REMOVED: + case PROP_ENTRY_POINTS: + case PROP_EXIT_POINTS: + case PROP_MEMBERS: + case PROP_CREDENTIALS: + case PROP_ACCOMPANIMENT: + case PROP_BELONGS_TO: + case PROP_LAST_ACCESS_POINT: + /* Properties using BACnetDeviceObjectReference */ + return BACNET_APPLICATION_TAG_DEVICE_OBJECT_REFERENCE; + + case PROP_TIME_OF_ACTIVE_TIME_RESET: + case PROP_TIME_OF_STATE_COUNT_RESET: + case PROP_CHANGE_OF_STATE_TIME: + case PROP_MAXIMUM_VALUE_TIMESTAMP: + case PROP_MINIMUM_VALUE_TIMESTAMP: + case PROP_VALUE_CHANGE_TIME: + case PROP_START_TIME: + case PROP_STOP_TIME: + case PROP_MODIFICATION_DATE: + case PROP_UPDATE_TIME: + case PROP_COUNT_CHANGE_TIME: + case PROP_LAST_CREDENTIAL_ADDED_TIME: + case PROP_LAST_CREDENTIAL_REMOVED_TIME: + case PROP_ACTIVATION_TIME: + case PROP_EXPIRATION_TIME: + case PROP_LAST_USE_TIME: + /* Properties using BACnetDateTime value */ + return BACNET_APPLICATION_TAG_DATETIME; + + case PROP_OBJECT_PROPERTY_REFERENCE: + case PROP_LOG_DEVICE_OBJECT_PROPERTY: + case PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES: + /* Properties using BACnetDeviceObjectPropertyReference */ + return BACNET_APPLICATION_TAG_DEVICE_OBJECT_PROPERTY_REFERENCE; + + case PROP_MANIPULATED_VARIABLE_REFERENCE: + case PROP_CONTROLLED_VARIABLE_REFERENCE: + case PROP_INPUT_REFERENCE: + /* Properties using BACnetObjectPropertyReference */ + return BACNET_APPLICATION_TAG_OBJECT_PROPERTY_REFERENCE; + + case PROP_EVENT_TIME_STAMPS: + case PROP_LAST_RESTORE_TIME: + case PROP_TIME_OF_DEVICE_RESTART: + case PROP_ACCESS_EVENT_TIME: + /* Properties using BACnetTimeStamp */ + return BACNET_APPLICATION_TAG_TIMESTAMP; + + case PROP_DEFAULT_COLOR: + /* Properties using BACnetxyColor */ + return BACNET_APPLICATION_TAG_XY_COLOR; + + case PROP_TRACKING_VALUE: + case PROP_PRESENT_VALUE: + if (object_type == OBJECT_COLOR) { + /* Properties using BACnetxyColor */ + return BACNET_APPLICATION_TAG_XY_COLOR; + } + return -1; + + case PROP_COLOR_COMMAND: + /* Properties using BACnetColorCommand */ + return BACNET_APPLICATION_TAG_COLOR_COMMAND; + + case PROP_LIGHTING_COMMAND: + /* Properties using BACnetLightingCommand */ + return BACNET_APPLICATION_TAG_LIGHTING_COMMAND; + + case PROP_WEEKLY_SCHEDULE: + /* BACnetWeeklySchedule ([7] BACnetDailySchedule*/ + return BACNET_APPLICATION_TAG_WEEKLY_SCHEDULE; + + case PROP_PRIORITY_ARRAY: + /* [16] BACnetPriorityValue : 16x values (simple property) */ + return -1; + + case PROP_LIST_OF_GROUP_MEMBERS: + /* Properties using ReadAccessSpecification */ + return -1; + + case PROP_EXCEPTION_SCHEDULE: + /* BACnetSpecialEvent (Schedule) */ + return -1; + + case PROP_DATE_LIST: + /* FIXME: Properties using : BACnetCalendarEntry */ + return -1; + + case PROP_ACTIVE_COV_SUBSCRIPTIONS: + /* FIXME: BACnetCOVSubscription */ + return -1; + + case PROP_EFFECTIVE_PERIOD: + /* FIXME: Properties using BACnetDateRange (Schedule) */ + return -1; + + case PROP_RECIPIENT_LIST: + /* FIXME: Properties using BACnetDestination */ + return -1; + + case PROP_TIME_SYNCHRONIZATION_RECIPIENTS: + case PROP_RESTART_NOTIFICATION_RECIPIENTS: + case PROP_UTC_TIME_SYNCHRONIZATION_RECIPIENTS: + /* FIXME: Properties using BACnetRecipient */ + return -1; + + case PROP_DEVICE_ADDRESS_BINDING: + case PROP_MANUAL_SLAVE_ADDRESS_BINDING: + case PROP_SLAVE_ADDRESS_BINDING: + /* FIXME: BACnetAddressBinding */ + return -1; + + case PROP_ACTION: + return -1; + + default: + return -1; + } +} + /** * @brief Decodes a well-known, possibly complex property value * Used to reverse operations in bacapp_encode_application_data @@ -1028,6 +1181,15 @@ int bacapp_decode_known_property(uint8_t *apdu, { int len = 0; + // NOTE: + // When adding impl for a new prop, also add its tag + // to bacapp_known_property_tag() + + int tag = bacapp_known_property_tag(object_type, property); + if (tag != -1) { + value->tag = tag; + } + switch (property) { case PROP_MEMBER_OF: case PROP_ZONE_MEMBERS: @@ -1048,10 +1210,10 @@ int bacapp_decode_known_property(uint8_t *apdu, case PROP_BELONGS_TO: case PROP_LAST_ACCESS_POINT: /* Properties using BACnetDeviceObjectReference */ - value->tag = BACNET_APPLICATION_TAG_DEVICE_OBJECT_REFERENCE; len = bacapp_decode_device_obj_ref( apdu, &value->type.Device_Object_Reference); break; + case PROP_TIME_OF_ACTIVE_TIME_RESET: case PROP_TIME_OF_STATE_COUNT_RESET: case PROP_CHANGE_OF_STATE_TIME: @@ -1069,46 +1231,44 @@ int bacapp_decode_known_property(uint8_t *apdu, case PROP_EXPIRATION_TIME: case PROP_LAST_USE_TIME: /* Properties using BACnetDateTime value */ - value->tag = BACNET_APPLICATION_TAG_DATETIME; len = bacapp_decode_datetime(apdu, &value->type.Date_Time); break; + case PROP_OBJECT_PROPERTY_REFERENCE: case PROP_LOG_DEVICE_OBJECT_PROPERTY: case PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES: /* Properties using BACnetDeviceObjectPropertyReference */ - value->tag = - BACNET_APPLICATION_TAG_DEVICE_OBJECT_PROPERTY_REFERENCE; len = bacapp_decode_device_obj_property_ref(apdu, &value->type.Device_Object_Property_Reference); break; + case PROP_MANIPULATED_VARIABLE_REFERENCE: case PROP_CONTROLLED_VARIABLE_REFERENCE: case PROP_INPUT_REFERENCE: /* Properties using BACnetObjectPropertyReference */ - value->tag = BACNET_APPLICATION_TAG_OBJECT_PROPERTY_REFERENCE; len = bacapp_decode_obj_property_ref( apdu, max_apdu_len, &value->type.Object_Property_Reference); break; + case PROP_EVENT_TIME_STAMPS: case PROP_LAST_RESTORE_TIME: case PROP_TIME_OF_DEVICE_RESTART: case PROP_ACCESS_EVENT_TIME: /* Properties using BACnetTimeStamp */ - value->tag = BACNET_APPLICATION_TAG_TIMESTAMP; len = bacapp_decode_timestamp(apdu, &value->type.Time_Stamp); break; + case PROP_DEFAULT_COLOR: /* Properties using BACnetxyColor */ - value->tag = BACNET_APPLICATION_TAG_XY_COLOR; len = xy_color_decode(apdu, max_apdu_len, &value->type.XY_Color); break; + case PROP_TRACKING_VALUE: case PROP_PRESENT_VALUE: if (object_type == OBJECT_COLOR) { /* Properties using BACnetxyColor */ - value->tag = BACNET_APPLICATION_TAG_XY_COLOR; len = xy_color_decode(apdu, max_apdu_len, &value->type.XY_Color); } else { @@ -1117,26 +1277,34 @@ int bacapp_decode_known_property(uint8_t *apdu, property); } break; + case PROP_COLOR_COMMAND: /* Properties using BACnetColorCommand */ - value->tag = BACNET_APPLICATION_TAG_COLOR_COMMAND; len = color_command_decode(apdu, max_apdu_len, NULL, &value->type.Color_Command); break; + case PROP_LIGHTING_COMMAND: /* Properties using BACnetLightingCommand */ - value->tag = BACNET_APPLICATION_TAG_LIGHTING_COMMAND; len = lighting_command_decode(apdu, max_apdu_len, &value->type.Lighting_Command); break; + case PROP_PRIORITY_ARRAY: /* [16] BACnetPriorityValue : 16x values (simple property) */ len = decode_priority_value(apdu, max_apdu_len, value, property); break; + + case PROP_WEEKLY_SCHEDULE: + /* BACnetWeeklySchedule ([7] BACnetDailySchedule*/ + len = bacnet_weeklyschedule_decode( + apdu, max_apdu_len, &value->type.Weekly_Schedule); + break; + + /* properties without a specific decoder - fall through to default */ + case PROP_LIST_OF_GROUP_MEMBERS: /* Properties using ReadAccessSpecification */ - case PROP_WEEKLY_SCHEDULE: - /* BACnetDailySchedule[7] (Schedule) */ case PROP_EXCEPTION_SCHEDULE: /* BACnetSpecialEvent (Schedule) */ case PROP_DATE_LIST: @@ -1527,6 +1695,149 @@ static int bacapp_snprintf_time(char *str, size_t str_len, BACNET_TIME *btime) } #endif +#if defined(BACAPP_TYPES_EXTRA) +static int bacapp_snprintf_weeklyschedule( + char *str, + size_t str_len, + BACNET_WEEKLY_SCHEDULE *ws, + BACNET_ARRAY_INDEX arrayIndex) +{ + int slen; + int ret_val = 0; + int wi, ti; + BACNET_OBJECT_PROPERTY_VALUE dummyPropValue; + BACNET_APPLICATION_DATA_VALUE dummyDataValue; + + const char *weekdaynames[7] = { + "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" + }; + const int loopend = ((arrayIndex == BACNET_ARRAY_ALL) ? 7 : 1); + + // Find what inner type it uses + int inner_tag = -1; + for (wi = 0; wi < loopend; wi++) { + BACNET_DAILY_SCHEDULE *ds = &ws->weeklySchedule[wi]; + for (ti = 0; ti < ds->TV_Count; ti++) { + int tag = ds->Time_Values[ti].Value.tag; + if (inner_tag == -1) { + inner_tag = tag; + } else if (inner_tag != tag) { + inner_tag = -2; + } + } + } + + if (inner_tag == -1) { + slen = snprintf(str, str_len, "(Null; "); + } else if (inner_tag == -2) { + slen = snprintf(str, str_len, "(MIXED_TYPES; "); + } else { + slen = snprintf(str, str_len, "(%s; ", bactext_application_tag_name(inner_tag)); + } + ret_val += slen; + if (str) { + str += slen; + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; + } + } + + for (wi = 0; wi < loopend; wi++) { + BACNET_DAILY_SCHEDULE *ds = &ws->weeklySchedule[wi]; + if (arrayIndex == BACNET_ARRAY_ALL) { + slen = snprintf(str, str_len, "%s: [", weekdaynames[wi]); + } else { + slen = snprintf( + str, str_len, "%s: [", + (arrayIndex >= 1 && arrayIndex <= 7) ? weekdaynames[arrayIndex - 1] : "???" + ); + } + ret_val += slen; + if (str) { + str += slen; + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; + } + } + + for (ti = 0; ti < ds->TV_Count; ti++) { + slen = bacapp_snprintf_time(str, str_len, &ds->Time_Values[ti].Time); + ret_val += slen; + if (str) { + str += slen; + 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; + } + } + + bacnet_primitive_to_application_data_value( + &dummyDataValue, &ds->Time_Values[ti].Value); + dummyPropValue.value = &dummyDataValue; + dummyPropValue.object_property = PROP_PRESENT_VALUE; + dummyPropValue.object_type = OBJECT_SCHEDULE; + + slen = bacapp_snprintf_value(str, str_len, &dummyPropValue); + ret_val += slen; + if (str) { + str += slen; + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; + } + } + + if (ti < ds->TV_Count - 1) { + slen = snprintf(str, str_len, ", "); + ret_val += slen; + if (str) { + str += slen; + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; + } + } + } + } + + if (wi < loopend - 1) { + slen = snprintf(str, str_len, "]; "); + ret_val += slen; + if (str) { + str += slen; + if (str_len >= slen) { + str_len -= slen; + } else { + str_len = 0; + } + } + } + } + slen = snprintf(str, str_len, "])"); + ret_val += slen; + return ret_val; +} +#endif + + /** * @brief Extract the value into a text string * @param str - the buffer to store the extracted value, or NULL for length @@ -1939,6 +2250,11 @@ int bacapp_snprintf_value( slen = snprintf(str, str_len, ")"); ret_val += slen; break; + case BACNET_APPLICATION_TAG_WEEKLY_SCHEDULE: + /* BACnetWeeklySchedule */ + ret_val = bacapp_snprintf_weeklyschedule( + str, str_len, &value->type.Weekly_Schedule, object_value->array_index); + break; case BACNET_APPLICATION_TAG_HOST_N_PORT: if (value->type.Host_Address.host_ip_address) { octet_str = octetstring_value( @@ -1988,6 +2304,7 @@ int bacapp_snprintf_value( break; #endif default: + ret_val = snprintf(str, str_len, "UnknownType(tag=%d)", value->tag); break; } } @@ -2040,10 +2357,206 @@ bool bacapp_print_value( #endif #ifdef BACAPP_PRINT_ENABLED +static char* ltrim(char *str, const char *trimmedchars) { + if (str[0] == 0) { + return str; + } + while(strchr(trimmedchars, *str)) { + str++; + } + return str; +} + +static char* rtrim(char *str, const char *trimmedchars) { + if (str[0] == 0) { + return str; + } + char *end = str + strlen(str) - 1; + while (strchr(trimmedchars, *end)) { + *end = 0; + if (end == str) break; + end--; + } + return str; +} + +static char* trim(char *str, const char *trimmedchars) { + return ltrim(rtrim(str, trimmedchars), trimmedchars); +} + +#if defined (BACAPP_TYPES_EXTRA) +static bool parse_weeklyschedule(char *str, BACNET_APPLICATION_DATA_VALUE *value) +{ + char *chunk, *comma, *space, *t, *v; + int daynum = 0, tvnum = 0; + unsigned int inner_tag; + BACNET_APPLICATION_DATA_VALUE dummy_value = { 0 }; + BACNET_DAILY_SCHEDULE *dsch; + + /* + Format: + + (1; Mon: [02:00:00.00 FALSE, 07:35:00.00 active, 07:40:00.00 inactive]; Tue: [02:00:00.00 inactive]; ...) + + - the first number is the inner tag (e.g. 1 = boolean, 4 = real, 9 = enum) + - Day name prefix is optional and ignored. + - Entries are separated by semicolons. + - There can be a full week, or only one entry - when using array index to modify a single day + - time-value array can be empty: [] + */ + + value->tag = BACNET_APPLICATION_TAG_WEEKLY_SCHEDULE; + + // Parse the inner tag + chunk = strtok(str, ";"); + chunk = ltrim(chunk, "("); + if (false == bacapp_parse_application_data(BACNET_APPLICATION_TAG_UNSIGNED_INT, chunk, &dummy_value)) { + // Try searching it by name + if (false == bactext_application_tag_index(chunk, &inner_tag)) { + return false; + } + } else { + inner_tag = (int)dummy_value.type.Unsigned_Int; + } + + chunk = strtok(NULL, ";"); + + while(chunk != NULL) { + dsch = &value->type.Weekly_Schedule.weeklySchedule[daynum]; + + // Strip day name prefix, if present + char *colonpos = strchr(chunk, ':'); + char *sqpos = strchr(chunk, '['); + if (colonpos && colonpos < sqpos) { + chunk = colonpos + 1; + } + + // Extract the inner list of time-values + chunk = rtrim(ltrim(chunk, "([ "), " ])"); + + // The list can be empty + if (chunk[0] != 0) { + // loop through the time value pairs + tvnum = 0; + do { + // Find the comma delimiter, replace with NUL (like strtok) + comma = strchr(chunk, ','); + if (comma) { + *comma = 0; + } + // trim the time-value pair and find the delimiter space + chunk = trim(chunk, " "); + space = strchr(chunk, ' '); + if (!space) { + // malformed time-value pair + return false; + } + *space = 0; + + // Extract time and value + t = chunk; + // value starts one byte after the space, and there can be multiple spaces + chunk = ltrim(space + 1, " "); + v = chunk; + + // Parse time + if (false == bacapp_parse_application_data(BACNET_APPLICATION_TAG_TIME, t, &dummy_value)) { + return false; + } + dsch->Time_Values[tvnum].Time = dummy_value.type.Time; + + // Parse value + if (false == bacapp_parse_application_data(inner_tag, v, &dummy_value)) { + return false; + } + if (BACNET_STATUS_OK != + bacnet_application_to_primitive_data_value( + &dsch->Time_Values[tvnum].Value, &dummy_value)) { + return false; + } + + // Advance past the comma to the next chunk + if (comma) { + chunk = comma + 1; + } + tvnum++; + } while (comma != NULL); + } + + dsch->TV_Count = tvnum; + + // Find the start of the next day + chunk = strtok(NULL, ";"); + daynum++; + } + + if (daynum == 1) { + value->type.Weekly_Schedule.singleDay = true; + } + + return true; +} +#endif + +#if defined(BACAPP_SIGNED) || defined(BACAPP_BOOLEAN) +static bool strtol_checked(const char *s, long *out) +{ + char *end; + errno = 0; + *out = strtol(s, &end, 0); + if (end == s) { + // Conversion was not possible + return false; + } + if (errno == ERANGE) { + // Number too large + return false; + } + return true; +} +#endif + +#if defined(BACAPP_UNSIGNED) || defined(BACAPP_ENUMERATED) +static bool strtoul_checked(const char *s, unsigned long *out) +{ + char *end; + errno = 0; + *out = strtoul(s, &end, 0); + if (end == s) { + // Conversion was not possible + return false; + } + if (errno == ERANGE) { + // Number too large + return false; + } + return true; +} +#endif + +#if defined(BACAPP_REAL) || defined(BACAPP_DOUBLE) +static bool strtod_checked(const char *s, double *out) +{ + char *end; + errno = 0; + *out = strtod(s, &end); + if (end == s) { + // Conversion was not possible + return false; + } + if (errno == ERANGE) { + // Number too large + return false; + } + return true; +} +#endif + /* used to load the app data struct with the proper data - converted from a command line argument */ + converted from a command line argument. + "argv" is not const to allow using strtok internally. It MAY be modified. */ bool bacapp_parse_application_data(BACNET_APPLICATION_TAG tag_number, - const char *argv, + char *argv, BACNET_APPLICATION_DATA_VALUE *value) { int hour, min, sec, hundredths; @@ -2052,7 +2565,7 @@ bool bacapp_parse_application_data(BACNET_APPLICATION_TAG tag_number, uint32_t instance = 0; bool status = false; long long_value = 0; - unsigned long unsigned_long_value = 0; + BACNET_UNSIGNED_INTEGER unsigned_long_value = 0; double double_value = 0.0; int count = 0; #if defined(BACAPP_TYPES_EXTRA) @@ -2066,34 +2579,59 @@ bool bacapp_parse_application_data(BACNET_APPLICATION_TAG tag_number, switch (tag_number) { #if defined(BACAPP_BOOLEAN) case BACNET_APPLICATION_TAG_BOOLEAN: - long_value = strtol(argv, NULL, 0); - if (long_value) + if (strcasecmp(argv, "true") == 0 || strcasecmp(argv, "active") == 0) { value->type.Boolean = true; - else + } else if (strcasecmp(argv, "false") == 0 || strcasecmp(argv, "inactive") == 0) { value->type.Boolean = false; + } else { + status = strtol_checked(argv, &long_value); + if (!status) { + return false; + } + if (long_value) { + value->type.Boolean = true; + } else { + value->type.Boolean = false; + } + } break; #endif #if defined(BACAPP_UNSIGNED) case BACNET_APPLICATION_TAG_UNSIGNED_INT: - unsigned_long_value = strtoul(argv, NULL, 0); + status = strtoul_checked(argv, &unsigned_long_value); + if (!status) { + return false; + } + if (unsigned_long_value > BACNET_UNSIGNED_INTEGER_MAX) { + return false; + } value->type.Unsigned_Int = unsigned_long_value; break; #endif #if defined(BACAPP_SIGNED) case BACNET_APPLICATION_TAG_SIGNED_INT: - long_value = strtol(argv, NULL, 0); - value->type.Signed_Int = long_value; + status = strtol_checked(argv, &long_value); + if (!status || long_value > INT32_MAX || long_value < INT32_MIN) { + return false; + } + value->type.Signed_Int = (int32_t) long_value; break; #endif #if defined(BACAPP_REAL) case BACNET_APPLICATION_TAG_REAL: - double_value = strtod(argv, NULL); + status = strtod_checked(argv, &double_value); + if (!status) { + return false; + } value->type.Real = (float)double_value; break; #endif #if defined(BACAPP_DOUBLE) case BACNET_APPLICATION_TAG_DOUBLE: - double_value = strtod(argv, NULL); + status = strtod_checked(argv, &double_value); + if (!status) { + return false; + } value->type.Double = double_value; break; #endif @@ -2116,8 +2654,11 @@ bool bacapp_parse_application_data(BACNET_APPLICATION_TAG tag_number, #endif #if defined(BACAPP_ENUMERATED) case BACNET_APPLICATION_TAG_ENUMERATED: - unsigned_long_value = strtoul(argv, NULL, 0); - value->type.Enumerated = unsigned_long_value; + status = strtoul_checked(argv, &unsigned_long_value); + if (!status || unsigned_long_value > UINT32_MAX) { + return false; + } + value->type.Enumerated = (uint32_t) unsigned_long_value; break; #endif #if defined(BACAPP_DATE) @@ -2190,6 +2731,9 @@ bool bacapp_parse_application_data(BACNET_APPLICATION_TAG tag_number, case BACNET_APPLICATION_TAG_COLOR_COMMAND: /* FIXME: add parsing for BACnetColorCommand */ break; + case BACNET_APPLICATION_TAG_WEEKLY_SCHEDULE: + status = parse_weeklyschedule(argv, value); + break; case BACNET_APPLICATION_TAG_HOST_N_PORT: count = sscanf(argv, "%3u.%3u.%3u.%3u:%5u", &a[0], &a[1], &a[2], &a[3], &p); @@ -2221,7 +2765,7 @@ bool bacapp_parse_application_data(BACNET_APPLICATION_TAG tag_number, return status; } -#endif +#endif /* BACAPP_PRINT_ENABLED */ /** * Initialize an array (or single) #BACNET_APPLICATION_DATA_VALUE @@ -2405,6 +2949,11 @@ bool bacapp_same_value(BACNET_APPLICATION_DATA_VALUE *value, status = color_command_same(&value->type.Color_Command, &test_value->type.Color_Command); break; + case BACNET_APPLICATION_TAG_WEEKLY_SCHEDULE: + /* BACnetWeeklySchedule */ + status = bacnet_weeklyschedule_same(&value->type.Weekly_Schedule, + &test_value->type.Weekly_Schedule); + break; case BACNET_APPLICATION_TAG_HOST_N_PORT: status = host_n_port_same(&value->type.Host_Address, &value->type.Host_Address); diff --git a/src/bacnet/bacapp.h b/src/bacnet/bacapp.h index 6cfaafcf..31417aed 100644 --- a/src/bacnet/bacapp.h +++ b/src/bacnet/bacapp.h @@ -36,6 +36,7 @@ #include "bacnet/bacdevobjpropref.h" #include "bacnet/hostnport.h" #include "bacnet/timestamp.h" +#include "bacnet/weeklyschedule.h" struct BACnet_Application_Data_Value; typedef struct BACnet_Application_Data_Value { @@ -86,6 +87,7 @@ typedef struct BACnet_Application_Data_Value { BACNET_LIGHTING_COMMAND Lighting_Command; BACNET_COLOR_COMMAND Color_Command; BACNET_XY_COLOR XY_Color; + BACNET_WEEKLY_SCHEDULE Weekly_Schedule; BACNET_HOST_N_PORT Host_Address; BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE Device_Object_Property_Reference; @@ -216,6 +218,11 @@ extern "C" { BACNET_OBJECT_TYPE object_type, BACNET_PROPERTY_ID property); + BACNET_STACK_EXPORT + int bacapp_known_property_tag( + BACNET_OBJECT_TYPE object_type, + BACNET_PROPERTY_ID property); + BACNET_STACK_EXPORT bool bacapp_copy( BACNET_APPLICATION_DATA_VALUE * dest_value, @@ -260,7 +267,7 @@ extern "C" { BACNET_STACK_EXPORT bool bacapp_parse_application_data( BACNET_APPLICATION_TAG tag_number, - const char *argv, + char *argv, BACNET_APPLICATION_DATA_VALUE * value); BACNET_STACK_EXPORT bool bacapp_print_value( diff --git a/src/bacnet/bacdcode.h b/src/bacnet/bacdcode.h index 7b3122b0..a80addc1 100644 --- a/src/bacnet/bacdcode.h +++ b/src/bacnet/bacdcode.h @@ -617,23 +617,23 @@ extern "C" { /* from clause 20.2.1.2 Tag Number */ /* true if extended tag numbering is used */ -#define IS_EXTENDED_TAG_NUMBER(x) ((x & 0xF0) == 0xF0) +#define IS_EXTENDED_TAG_NUMBER(x) (((x) & 0xF0) == 0xF0) /* from clause 20.2.1.3.1 Primitive Data */ /* true if the extended value is used */ -#define IS_EXTENDED_VALUE(x) ((x & 0x07) == 5) +#define IS_EXTENDED_VALUE(x) (((x) & 0x07) == 5) /* from clause 20.2.1.1 Class */ /* true if the tag is context specific */ -#define IS_CONTEXT_SPECIFIC(x) ((x & BIT(3)) == BIT(3)) +#define IS_CONTEXT_SPECIFIC(x) (((x) & BIT(3)) == BIT(3)) /* from clause 20.2.1.3.2 Constructed Data */ /* true if the tag is an opening tag */ -#define IS_OPENING_TAG(x) ((x & 0x07) == 6) +#define IS_OPENING_TAG(x) (((x) & 0x07) == 6) /* from clause 20.2.1.3.2 Constructed Data */ /* true if the tag is a closing tag */ -#define IS_CLOSING_TAG(x) ((x & 0x07) == 7) +#define IS_CLOSING_TAG(x) (((x) & 0x07) == 7) #ifdef __cplusplus } diff --git a/src/bacnet/bactimevalue.c b/src/bacnet/bactimevalue.c index 93922e6b..2a705de3 100644 --- a/src/bacnet/bactimevalue.c +++ b/src/bacnet/bactimevalue.c @@ -34,57 +34,195 @@ #include #include +#include /* memcpy */ #include "bacnet/bacdcode.h" #include "bacnet/bactimevalue.h" +#include "bacnet/bacapp.h" -int bacapp_encode_time_value(uint8_t *apdu, BACNET_TIME_VALUE *value) +static bool is_data_value_schedule_compatible(uint8_t tag) { + switch (tag) { + /* Every member of the union must be listed here to allow decoding */ + case BACNET_APPLICATION_TAG_NULL: + return true; +#if defined (BACAPP_BOOLEAN) + case BACNET_APPLICATION_TAG_BOOLEAN: + return true; +#endif +#if defined (BACAPP_UNSIGNED) + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + return true; +#endif +#if defined (BACAPP_SIGNED) + case BACNET_APPLICATION_TAG_SIGNED_INT: + return true; +#endif +#if defined (BACAPP_REAL) + case BACNET_APPLICATION_TAG_REAL: + return true; +#endif +#if defined (BACAPP_DOUBLE) + case BACNET_APPLICATION_TAG_DOUBLE: + return true; +#endif +#if defined (BACAPP_ENUMERATED) + case BACNET_APPLICATION_TAG_ENUMERATED: + return true; +#endif + default: + return false; + } +} + +int bacnet_time_value_encode(uint8_t *apdu, BACNET_TIME_VALUE *value) { int len; int apdu_len = 0; + uint8_t *apdu_offset = NULL; - len = encode_application_time(&apdu[apdu_len], &value->Time); + if (!value || !is_data_value_schedule_compatible(value->Value.tag)) { + return BACNET_STATUS_ERROR; + } + + if (apdu) { + apdu_offset = &apdu[apdu_len]; + } + len = encode_application_time(apdu_offset, &value->Time); apdu_len += len; - len = bacapp_encode_application_data(&apdu[apdu_len], &value->Value); + if (apdu) { + apdu_offset = &apdu[apdu_len]; + } + + BACNET_APPLICATION_DATA_VALUE adv; + bacnet_primitive_to_application_data_value(&adv, &value->Value); + + len = bacapp_encode_application_data(apdu_offset, &adv); apdu_len += len; return apdu_len; } -int bacapp_encode_context_time_value( +int bacapp_encode_time_value(uint8_t *apdu, BACNET_TIME_VALUE *value) { + return bacnet_time_value_encode(apdu, value); +} + +int bacnet_time_value_context_encode( uint8_t *apdu, uint8_t tag_number, BACNET_TIME_VALUE *value) { int len; int apdu_len = 0; + uint8_t *apdu_offset = NULL; - len = encode_opening_tag(&apdu[apdu_len], tag_number); + if (apdu) { + apdu_offset = &apdu[apdu_len]; + } + len = encode_opening_tag(apdu_offset, tag_number); apdu_len += len; - len = bacapp_encode_time_value(&apdu[apdu_len], value); + if (apdu) { + apdu_offset = &apdu[apdu_len]; + } + len = bacnet_time_value_encode(apdu_offset, value); apdu_len += len; - len = encode_closing_tag(&apdu[apdu_len], tag_number); + if (apdu) { + apdu_offset = &apdu[apdu_len]; + } + len = encode_closing_tag(apdu_offset, tag_number); + apdu_len += len; + + return apdu_len; +} + +int bacapp_encode_context_time_value(uint8_t *apdu, uint8_t tag_number, BACNET_TIME_VALUE *value) +{ + return bacnet_time_value_context_encode(apdu, tag_number, value); +} + +/** returns 0 if OK, -1 on error */ +int bacnet_application_to_primitive_data_value( + struct BACnet_Primitive_Data_Value *dest, + const struct BACnet_Application_Data_Value *src) +{ + // make sure the value passed is valid + if (!src || !dest || !is_data_value_schedule_compatible(src->tag)) { + return BACNET_STATUS_ERROR; + } + memset(dest, 0, sizeof(struct BACnet_Primitive_Data_Value)); + dest->tag = src->tag; + memcpy(&dest->type, &src->type, sizeof(dest->type)); + return BACNET_STATUS_OK; +} + +/** returns 0 if OK, -1 on error */ +int bacnet_primitive_to_application_data_value( + struct BACnet_Application_Data_Value * dest, + const struct BACnet_Primitive_Data_Value * src) +{ + // make sure the value passed is valid + if (!dest || !src) { + return BACNET_STATUS_ERROR; + } + memset(dest, 0, sizeof(struct BACnet_Application_Data_Value)); + dest->tag = src->tag; + memcpy(&dest->type, &src->type, sizeof(src->type)); + return BACNET_STATUS_OK; /* OK */ +} + +int bacnet_time_value_decode(uint8_t *apdu, int max_apdu_len, BACNET_TIME_VALUE *value) +{ + int len; + int apdu_len = 0; + BACNET_APPLICATION_DATA_VALUE full_data_value = { 0 }; + + len = bacnet_time_application_decode(&apdu[apdu_len], max_apdu_len, &value->Time); + if (len <= 0) { + return -1; + } + apdu_len += len; + + len = bacapp_decode_application_data(&apdu[apdu_len], max_apdu_len - apdu_len, &full_data_value); + if (len <= 0) { + return -1; + } + if (BACNET_STATUS_OK != + bacnet_application_to_primitive_data_value( + &value->Value, &full_data_value)) { + return -1; + } apdu_len += len; return apdu_len; } int bacapp_decode_time_value(uint8_t *apdu, BACNET_TIME_VALUE *value) +{ + return bacnet_time_value_decode(apdu, MAX_APDU, value); +} + +int bacnet_time_value_context_decode(uint8_t *apdu, int max_apdu_len, uint8_t tag_number, BACNET_TIME_VALUE *value) { int len; int apdu_len = 0; - len = decode_application_time(&apdu[apdu_len], &value->Time); - if (len <= 0) { + if ((max_apdu_len - apdu_len) >= 1 && decode_is_opening_tag_number(&apdu[apdu_len], tag_number)) { + apdu_len += 1; + } else { return -1; } - apdu_len += len; - len = bacapp_decode_application_data(&apdu[apdu_len], 2048, &value->Value); - if (len <= 0) { + len = bacnet_time_value_decode(&apdu[apdu_len], max_apdu_len - apdu_len, value); + if (len > 0) { + apdu_len += len; + } else { + return -1; + } + + if ((max_apdu_len - apdu_len) >= 1 && decode_is_closing_tag_number(&apdu[apdu_len], tag_number)) { + apdu_len += 1; + } else { return -1; } - apdu_len += len; return apdu_len; } @@ -92,27 +230,99 @@ int bacapp_decode_time_value(uint8_t *apdu, BACNET_TIME_VALUE *value) int bacapp_decode_context_time_value( uint8_t *apdu, uint8_t tag_number, BACNET_TIME_VALUE *value) { - int len = 0; - int section_length; - - if (decode_is_opening_tag_number(&apdu[len], tag_number)) { - len++; - } else { - return -1; - } - - section_length = bacapp_decode_time_value(&apdu[len], value); - if (section_length > 0) { - len += section_length; - } else { - return -1; - } - - if (decode_is_closing_tag_number(&apdu[len], tag_number)) { - len++; - } else { - return -1; - } - - return len; + return bacnet_time_value_context_decode(apdu, MAX_APDU, tag_number, value); +} + +int bacnet_time_values_context_decode( + uint8_t * apdu, + const int max_apdu_len, + const uint8_t tag_number, + BACNET_TIME_VALUE *time_values, + const unsigned int max_time_values, + unsigned int *out_count) +{ + unsigned int j; + int len; + int apdu_len = 0; + unsigned int count_values = 0; + BACNET_TIME_VALUE dummy; + + /* day-schedule [0] SEQUENCE OF BACnetTimeValue */ + if (decode_is_opening_tag_number(&apdu[apdu_len], tag_number)) { + apdu_len++; + while ((apdu_len < max_apdu_len) && + !decode_is_closing_tag_number(&apdu[apdu_len], tag_number)) { + if (count_values < max_time_values) { + len = bacnet_time_value_decode(&apdu[apdu_len], + max_apdu_len - apdu_len, &time_values[count_values++]); + } else { + len = bacnet_time_value_decode( + &apdu[apdu_len], max_apdu_len - apdu_len, &dummy); + } + if (len < 0) { + return -1; + } + apdu_len += len; + } + /* Zeroing other values */ + for (j = count_values; j < max_time_values; j++) { + time_values[j].Value.tag = BACNET_APPLICATION_TAG_NULL; + time_values[j].Value.type.Unsigned_Int = 0; + time_values[j].Time.hour = 0; + time_values[j].Time.min = 0; + time_values[j].Time.sec = 0; + time_values[j].Time.hundredths = 0; + } + /* overflow ! */ + if (apdu_len >= max_apdu_len) { + return -1; + } + apdu_len++; /* closing tag */ + if (out_count) { + *out_count = count_values; + } + return apdu_len; + } + return -1; +} + +/* Encodes a : [x] SEQUENCE OF BACnetTimeValue into a fixed-size buffer */ +int bacnet_time_values_context_encode( + uint8_t * apdu, + uint8_t tag_number, + BACNET_TIME_VALUE *time_values, + unsigned int max_time_values) +{ + unsigned int j; + int apdu_len = 0; + int len = 0; + BACNET_TIME t0 = { 0 }; + uint8_t *apdu_offset = NULL; + + /* day-schedule [x] SEQUENCE OF BACnetTimeValue */ + if (apdu) { + apdu_offset = &apdu[apdu_len]; + } + apdu_len += encode_opening_tag(apdu_offset, tag_number); + + for (j = 0; j < max_time_values; j++) + /* Encode only non-null values (NULL,00:00:00.00) */ + if (time_values[j].Value.tag != BACNET_APPLICATION_TAG_NULL || + datetime_compare_time(&t0, &time_values[j].Time) != 0) { + if (apdu) { + apdu_offset = &apdu[apdu_len]; + } + len = + bacnet_time_value_encode(apdu_offset, + &time_values[j]); + if (len < 0) + return -1; + apdu_len += len; + } + /* close tag */ + if (apdu) { + apdu_offset = &apdu[apdu_len]; + } + apdu_len += encode_closing_tag(apdu_offset, tag_number); + return apdu_len; } diff --git a/src/bacnet/bactimevalue.h b/src/bacnet/bactimevalue.h index 3991ab30..d49f87eb 100644 --- a/src/bacnet/bactimevalue.h +++ b/src/bacnet/bactimevalue.h @@ -29,39 +29,142 @@ #include #include #include "bacnet/bacnet_stack_exports.h" +#include "bacnet/basic/sys/platform.h" #include "bacnet/bacdef.h" #include "bacnet/bacenum.h" -#include "bacnet/bacapp.h" +#include "bacnet/datetime.h" -typedef struct { +/** + * Smaller version of BACnet_Application_Data_Value used in BACnetTimeValue + * + * This must be a separate struct to avoid recursive structure. + * Keeping it small also helps keep the size of BACNET_APPLICATION_DATA_VALUE + * small. Besides, schedule can't contain complex types. + */ +typedef struct BACnet_Primitive_Data_Value { + uint8_t tag; /* application tag data type */ + union { + /* + * ATTENTION! If a new type is added here, update + * `is_data_value_schedule_compatible()` in bactimevalue.c! + */ + + /* NULL - not needed as it is encoded in the tag alone */ +#if defined (BACAPP_BOOLEAN) + bool Boolean; +#endif +#if defined (BACAPP_UNSIGNED) + BACNET_UNSIGNED_INTEGER Unsigned_Int; +#endif +#if defined (BACAPP_SIGNED) + int32_t Signed_Int; +#endif +#if defined (BACAPP_REAL) + float Real; +#endif +#if defined (BACAPP_DOUBLE) + double Double; +#endif +#if defined (BACAPP_ENUMERATED) + uint32_t Enumerated; +#endif + } type; +} BACNET_PRIMITIVE_DATA_VALUE; + +typedef struct BACnet_Time_Value { BACNET_TIME Time; - BACNET_APPLICATION_DATA_VALUE Value; + BACNET_PRIMITIVE_DATA_VALUE Value; } BACNET_TIME_VALUE; #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ + struct BACnet_Application_Data_Value; + + /** returns 0 if OK, -1 on error */ + BACNET_STACK_EXPORT + int bacnet_application_to_primitive_data_value(BACNET_PRIMITIVE_DATA_VALUE * dest, + const struct BACnet_Application_Data_Value * src); + + /** returns 0 if OK, -1 on error */ + BACNET_STACK_EXPORT + int bacnet_primitive_to_application_data_value( + struct BACnet_Application_Data_Value * dest, + const BACNET_PRIMITIVE_DATA_VALUE * src); BACNET_STACK_EXPORT - int bacapp_encode_time_value(uint8_t * apdu, + int bacnet_time_value_encode(uint8_t * apdu, BACNET_TIME_VALUE * value); + BACNET_STACK_DEPRECATED("Use bacnet_time_value_encode() instead") BACNET_STACK_EXPORT - int bacapp_encode_context_time_value(uint8_t * apdu, + int bacapp_encode_time_value(uint8_t *apdu, BACNET_TIME_VALUE *value); + + BACNET_STACK_EXPORT + int bacnet_time_value_context_encode(uint8_t * apdu, uint8_t tag_number, BACNET_TIME_VALUE * value); + BACNET_STACK_DEPRECATED("Use bacnet_time_value_context_encode() instead") + BACNET_STACK_EXPORT + int bacapp_encode_context_time_value(uint8_t *apdu, uint8_t tag_number, BACNET_TIME_VALUE *value); + + BACNET_STACK_DEPRECATED("Use bacnet_time_value_decode() instead") BACNET_STACK_EXPORT int bacapp_decode_time_value(uint8_t * apdu, BACNET_TIME_VALUE * value); + BACNET_STACK_EXPORT + int bacnet_time_value_decode(uint8_t *apdu, int max_apdu_len, + BACNET_TIME_VALUE *value); + + BACNET_STACK_DEPRECATED("Use bacnet_time_value_context_decode() instead") BACNET_STACK_EXPORT int bacapp_decode_context_time_value(uint8_t * apdu, uint8_t tag_number, BACNET_TIME_VALUE * value); + BACNET_STACK_EXPORT + int bacnet_time_value_context_decode(uint8_t *apdu, int max_apdu_len, + uint8_t tag_number, BACNET_TIME_VALUE *value); + + /** + * Decode array of time-values wrapped in a context tag + * @param apdu + * @param max_apdu_len + * @param tag_number - number expected in the context tag; 0 used for DailySchedule + * @param time_values + * @param max_time_values - number of time values to encode + * @param[out] out_count - actual number of time values found + * @return used bytes, <0 if decoding failed + */ + BACNET_STACK_EXPORT + int bacnet_time_values_context_decode( + uint8_t * apdu, + int max_apdu_len, + uint8_t tag_number, + BACNET_TIME_VALUE *time_values, + unsigned int max_time_values, + unsigned int *out_count); + + /** + * Encode array of time-values wrapped in a context tag + * @param apdu - output buffer, NULL to just measure length + * @param max_apdu_len + * @param tag_number - number to use for the context tag; 0 used for DailySchedule + * @param time_values + * @param max_time_values - number of time values to encode + * @return used bytes, <=0 if encoding failed + */ + BACNET_STACK_EXPORT + int bacnet_time_values_context_encode( + uint8_t * apdu, + uint8_t tag_number, + BACNET_TIME_VALUE * time_values, + unsigned int max_time_values); + #ifdef __cplusplus } #endif /* __cplusplus */ -#endif +#endif /* _BAC_TIME_VALUE_H_ */ diff --git a/src/bacnet/basic/object/schedule.c b/src/bacnet/basic/object/schedule.c index 214371a2..4c6e02cd 100644 --- a/src/bacnet/basic/object/schedule.c +++ b/src/bacnet/basic/object/schedule.c @@ -86,10 +86,10 @@ void Schedule_Init(void) for (j = 0; j < 7; j++) { psched->Weekly_Schedule[j].TV_Count = 0; } - psched->Present_Value = &psched->Schedule_Default; + memcpy(&psched->Present_Value, &psched->Schedule_Default, sizeof(psched->Present_Value)); psched->Schedule_Default.context_specific = false; psched->Schedule_Default.tag = BACNET_APPLICATION_TAG_REAL; - psched->Schedule_Default.type.Real = 21.0; /* 21 C, room temperature */ + psched->Schedule_Default.type.Real = 21.0f; /* 21 C, room temperature */ psched->obj_prop_ref_cnt = 0; /* no references, add as needed */ psched->Priority_For_Writing = 16; /* lowest priority */ psched->Out_Of_Service = false; @@ -197,7 +197,7 @@ int Schedule_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) apdu_len = encode_application_enumerated(&apdu[0], OBJECT_SCHEDULE); break; case PROP_PRESENT_VALUE: - apdu_len = bacapp_encode_data(&apdu[0], CurrentSC->Present_Value); + apdu_len = bacapp_encode_data(&apdu[0], &CurrentSC->Present_Value); break; case PROP_EFFECTIVE_PERIOD: /* BACnet Testing Observed Incident oi00110 @@ -224,7 +224,7 @@ int Schedule_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) apdu_len += encode_opening_tag(&apdu[apdu_len], 0); for (i = 0; i < CurrentSC->Weekly_Schedule[day].TV_Count; i++) { - apdu_len += bacapp_encode_time_value(&apdu[apdu_len], + apdu_len += bacnet_time_value_encode(&apdu[apdu_len], &CurrentSC->Weekly_Schedule[day].Time_Values[i]); } apdu_len += encode_closing_tag(&apdu[apdu_len], 0); @@ -233,7 +233,7 @@ int Schedule_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) int day = rpdata->array_index - 1; apdu_len += encode_opening_tag(&apdu[apdu_len], 0); for (i = 0; i < CurrentSC->Weekly_Schedule[day].TV_Count; i++) { - apdu_len += bacapp_encode_time_value(&apdu[apdu_len], + apdu_len += bacnet_time_value_encode(&apdu[apdu_len], &CurrentSC->Weekly_Schedule[day].Time_Values[i]); } apdu_len += encode_closing_tag(&apdu[apdu_len], 0); @@ -396,7 +396,7 @@ void Schedule_Recalculate_PV( SCHEDULE_DESCR *desc, BACNET_WEEKDAY wday, BACNET_TIME *time) { int i; - desc->Present_Value = NULL; + desc->Present_Value.tag = BACNET_APPLICATION_TAG_NULL; /* for future development, here should be the loop for Exception Schedule */ @@ -408,19 +408,19 @@ void Schedule_Recalculate_PV( broker an early release on a case-by-case basis. */ for (i = 0; i < desc->Weekly_Schedule[wday - 1].TV_Count && - desc->Present_Value == NULL; + desc->Present_Value.tag == BACNET_APPLICATION_TAG_NULL; i++) { int diff = datetime_wildcard_compare_time( time, &desc->Weekly_Schedule[wday - 1].Time_Values[i].Time); if (diff >= 0 && desc->Weekly_Schedule[wday - 1].Time_Values[i].Value.tag != BACNET_APPLICATION_TAG_NULL) { - desc->Present_Value = - &desc->Weekly_Schedule[wday - 1].Time_Values[i].Value; + bacnet_primitive_to_application_data_value(&desc->Present_Value, + &desc->Weekly_Schedule[wday - 1].Time_Values[i].Value); } } - if (desc->Present_Value == NULL) { - desc->Present_Value = &desc->Schedule_Default; + if (desc->Present_Value.tag == BACNET_APPLICATION_TAG_NULL) { + memcpy(&desc->Present_Value, &desc->Schedule_Default, sizeof(desc->Present_Value)); } } diff --git a/src/bacnet/basic/object/schedule.h b/src/bacnet/basic/object/schedule.h index 7c6a23e7..84b0387e 100644 --- a/src/bacnet/basic/object/schedule.h +++ b/src/bacnet/basic/object/schedule.h @@ -51,19 +51,28 @@ extern "C" { #endif /* __cplusplus */ - typedef struct bacnet_daily_schedule { + /* + * Note: + * This is a different struct from BACNET_DAILY_SCHEDULE used in prop value encoding! + * The number of entries is different. + */ + typedef struct bacnet_obj_daily_schedule { BACNET_TIME_VALUE Time_Values[BACNET_WEEKLY_SCHEDULE_SIZE]; uint16_t TV_Count; /* the number of time values actually used */ - } BACNET_DAILY_SCHEDULE; + } BACNET_OBJ_DAILY_SCHEDULE; typedef struct schedule { /* Effective Period: Start and End Date */ BACNET_DATE Start_Date; BACNET_DATE End_Date; /* Properties concerning Present Value */ - BACNET_DAILY_SCHEDULE Weekly_Schedule[7]; + BACNET_OBJ_DAILY_SCHEDULE Weekly_Schedule[7]; BACNET_APPLICATION_DATA_VALUE Schedule_Default; - BACNET_APPLICATION_DATA_VALUE *Present_Value; /* must be set to a valid value + /* + * Caution: This is a converted to BACNET_PRIMITIVE_APPLICATION_DATA_VALUE. + * Only some data types may be used! + */ + BACNET_APPLICATION_DATA_VALUE Present_Value; /* must be set to a valid value * default is Schedule_Default */ BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE Object_Property_References[BACNET_SCHEDULE_OBJ_PROP_REF_SIZE]; diff --git a/src/bacnet/basic/service/h_rp_a.c b/src/bacnet/basic/service/h_rp_a.c index f7b2132e..724d781a 100644 --- a/src/bacnet/basic/service/h_rp_a.c +++ b/src/bacnet/basic/service/h_rp_a.c @@ -39,6 +39,15 @@ #include "bacnet/basic/services.h" #include "bacnet/basic/tsm/tsm.h" +#if PRINT_ENABLED +#include +#define PRINTF(...) fprintf(stdout,__VA_ARGS__) +#define PRINTF_ERR(...) fprintf(stderr,__VA_ARGS__) +#else +#define PRINTF(...) +#define PRINTF_ERR(...) +#endif + /** @file h_rp_a.c Handles Read Property Acknowledgments. */ /** For debugging... @@ -67,6 +76,14 @@ void rp_ack_print_data(BACNET_READ_PROPERTY_DATA *data) len = bacapp_decode_known_property( application_data, (unsigned)application_data_len, &value, data->object_type, data->object_property); + + if (len < 0) { + PRINTF_ERR("RP Ack: unable to decode! %s:%s\n", + bactext_object_type_name(data->object_type), + bactext_property_name(data->object_property)); + break; + } + if (first_value && (len < application_data_len)) { first_value = false; #if PRINT_ENABLED diff --git a/src/bacnet/basic/sys/platform.h b/src/bacnet/basic/sys/platform.h new file mode 100644 index 00000000..bec989e9 --- /dev/null +++ b/src/bacnet/basic/sys/platform.h @@ -0,0 +1,41 @@ +/************************************************************************** +* +* Copyright (C) 2022 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BAC_PLATFORM_H +#define BAC_PLATFORM_H + +/* + * Macros to support different compilers and platforms + */ + +# ifdef _MSC_VER +# define BACNET_STACK_DEPRECATED(message) __declspec(deprecated(message)) +# else +# define BACNET_STACK_DEPRECATED(message) __attribute__((deprecated(message))) +# endif + +# if defined(WIN32) || defined(WIN64) +# define strcasecmp _stricmp +# endif + +#endif // BAC_PLATFORM_H diff --git a/src/bacnet/dailyschedule.c b/src/bacnet/dailyschedule.c new file mode 100644 index 00000000..56a9b93a --- /dev/null +++ b/src/bacnet/dailyschedule.c @@ -0,0 +1,57 @@ +/*####COPYRIGHTBEGIN#### +------------------------------------------- +Copyright (C) 2022 Steve Karg + +This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to: +The Free Software Foundation, Inc. +59 Temple Place - Suite 330 +Boston, MA 02111-1307, USA. + +As a special exception, if other files instantiate templates or +use macros or inline functions from this file, or you compile +this file and link it with other works to produce a work based +on this file, this file does not by itself cause the resulting +work to be covered by the GNU General Public License. However +the source code for this file must still be made available in +accordance with section (3) of the GNU General Public License. + +This exception does not invalidate any other reasons why a work +based on this file might be covered by the GNU General Public +License. +------------------------------------------- +####COPYRIGHTEND####*/ + +#include +#include "bacnet/dailyschedule.h" +#include "bacnet/bactimevalue.h" + +int bacnet_dailyschedule_decode( + uint8_t * apdu, + int max_apdu_len, + BACNET_DAILY_SCHEDULE * day) +{ + unsigned int tv_count = 0; + int retval = bacnet_time_values_context_decode( + apdu, max_apdu_len, 0, &day->Time_Values[0], MAX_DAY_SCHEDULE_VALUES, &tv_count); + day->TV_Count = (uint16_t) tv_count; + return retval; +} + +int bacnet_dailyschedule_encode( + uint8_t * apdu, + BACNET_DAILY_SCHEDULE * day) +{ + return bacnet_time_values_context_encode( + apdu, 0, &day->Time_Values[0], day->TV_Count); +} diff --git a/src/bacnet/dailyschedule.h b/src/bacnet/dailyschedule.h new file mode 100644 index 00000000..8e7aa26f --- /dev/null +++ b/src/bacnet/dailyschedule.h @@ -0,0 +1,68 @@ +/************************************************************************** + * + * Copyright (C) 2012 Steve Karg + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + *********************************************************************/ +#ifndef DAILYSCHEDULE_H +#define DAILYSCHEDULE_H + +#include +#include +#include "bacnet/bacnet_stack_exports.h" +#include "bacnet/bactimevalue.h" + +/* arbitrary value, shall be unlimited for B-OWS but we don't care, 640k shall be enough */ +/* however we try not to boost the bacnet application value structure size, */ +/* so 7 x (this value) x sizeof(BACNET_TIME_VALUE) fits. */ +#define MAX_DAY_SCHEDULE_VALUES 40 + +/* + BACnetDailySchedule ::= SEQUENCE { + day-schedule [0] SEQUENCE OF BACnetTimeValue + } +*/ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + typedef struct BACnet_Daily_Schedule { + BACNET_TIME_VALUE Time_Values[MAX_DAY_SCHEDULE_VALUES]; + uint16_t TV_Count; /* the number of time values actually used */ + } BACNET_DAILY_SCHEDULE; + + /** Decode DailySchedule (sequence of times and values) */ + BACNET_STACK_EXPORT + int bacnet_dailyschedule_decode( + uint8_t * apdu, + int max_apdu_len, + BACNET_DAILY_SCHEDULE * day); + + /** Encode DailySchedule (sequence of times and values) */ + BACNET_STACK_EXPORT + int bacnet_dailyschedule_encode( + uint8_t * apdu, + BACNET_DAILY_SCHEDULE * day); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DAILYSCHEDULE_H */ diff --git a/src/bacnet/weeklyschedule.c b/src/bacnet/weeklyschedule.c new file mode 100644 index 00000000..fad24cea --- /dev/null +++ b/src/bacnet/weeklyschedule.c @@ -0,0 +1,187 @@ +/*####COPYRIGHTBEGIN#### +------------------------------------------- +Copyright (C) 2022 Steve Karg + +This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to: +The Free Software Foundation, Inc. +59 Temple Place - Suite 330 +Boston, MA 02111-1307, USA. + +As a special exception, if other files instantiate templates or +use macros or inline functions from this file, or you compile +this file and link it with other works to produce a work based +on this file, this file does not by itself cause the resulting +work to be covered by the GNU General Public License. However +the source code for this file must still be made available in +accordance with section (3) of the GNU General Public License. + +This exception does not invalidate any other reasons why a work +based on this file might be covered by the GNU General Public +License. +------------------------------------------- +####COPYRIGHTEND####*/ + +#include +#include "bacnet/weeklyschedule.h" +#include "bacnet/bacdcode.h" +#include "bacapp.h" + +int bacnet_weeklyschedule_decode( + uint8_t * apdu, + int max_apdu_len, + BACNET_WEEKLY_SCHEDULE *value) +{ + int len = 0; + int apdu_len = 0; + int wi; + + value->singleDay = false; + for (wi = 0; wi < 7; wi++) { + len = bacnet_dailyschedule_decode( + &apdu[apdu_len], max_apdu_len - apdu_len, &value->weeklySchedule[wi]); + if (len < 0) { + if (wi == 1) { + value->singleDay = true; + break; + } + return -1; + } + apdu_len += len; + } + return apdu_len; +} + +int bacnet_weeklyschedule_encode( + uint8_t * apdu, + BACNET_WEEKLY_SCHEDULE *value) +{ + int apdu_len = 0; + int len = 0; + int wi; + uint8_t *apdu_offset = NULL; + + for (wi = 0; wi < (value->singleDay ? 1 : 7); wi++) { + /* not enough room to write data */ + if (apdu) { + apdu_offset = &apdu[apdu_len]; + } + len = + bacnet_dailyschedule_encode(apdu_offset, &value->weeklySchedule[wi]); + if (len < 0) + return -1; + apdu_len += len; + } + return apdu_len; +} + + + +/** + * @brief Encode a context tagged WeeklySchedule complex data type + * @param apdu - the APDU buffer + * @param tag_number - the APDU buffer size + * @param value - WeeklySchedule structure + * @return length of the APDU buffer, or 0 if not able to encode + */ +int bacnet_weeklyschedule_context_encode( + uint8_t *apdu, uint8_t tag_number, BACNET_WEEKLY_SCHEDULE *value) +{ + int len = 0; + int apdu_len = 0; + uint8_t *apdu_offset = NULL; + + if (value) { + apdu_offset = apdu; + len = encode_opening_tag(apdu_offset, tag_number); + apdu_len += len; + if (apdu) { + apdu_offset = &apdu[apdu_len]; + } + len = bacnet_weeklyschedule_encode(apdu_offset, value); + apdu_len += len; + if (apdu) { + apdu_offset = &apdu[apdu_len]; + } + len = encode_closing_tag(apdu_offset, tag_number); + apdu_len += len; + } + + return apdu_len; +} + +int bacnet_weeklyschedule_context_decode( + uint8_t *apdu, int max_apdu_len, uint8_t tag_number, + BACNET_WEEKLY_SCHEDULE *value) +{ + int apdu_len = 0; + int len; + + if ((max_apdu_len - apdu_len) >= 1 && decode_is_opening_tag_number(&apdu[apdu_len], tag_number)) { + apdu_len += 1; + } else { + return -1; + } + + if (-1 == (len = bacnet_weeklyschedule_decode(&apdu[apdu_len], max_apdu_len - apdu_len, value))) { + return -1; + } else { + apdu_len += len; + } + + if ((max_apdu_len - apdu_len) >= 1 && decode_is_closing_tag_number(&apdu[apdu_len], tag_number)) { + apdu_len += 1; + } else { + return -1; + } + return apdu_len; +} + +/** + * @brief Compare the BACnetWeeklySchedule complex data + * @param value1 - BACNET_COLOR_COMMAND structure + * @param value2 - BACNET_COLOR_COMMAND structure + * @return true if the same + */ +bool bacnet_weeklyschedule_same( + BACNET_WEEKLY_SCHEDULE *value1, BACNET_WEEKLY_SCHEDULE *value2) +{ + int wi, ti; + for (wi = 0; wi < 7; wi++) { + BACNET_DAILY_SCHEDULE *ds1 = &value1->weeklySchedule[wi]; + BACNET_DAILY_SCHEDULE *ds2 = &value2->weeklySchedule[wi]; + if (ds1->TV_Count != ds2->TV_Count) { + return false; + } + for (ti = 0; ti < ds1->TV_Count; ti++) { + BACNET_TIME_VALUE *tv1 = &ds1->Time_Values[ti]; + BACNET_TIME_VALUE *tv2 = &ds2->Time_Values[ti]; + if (0 != datetime_compare_time(&tv1->Time, &tv2->Time)) { + return false; + } + + // TODO the conversion can be avoided by adding a "primitive" variant of bacapp_same_value(), + // at the cost of some code duplication + BACNET_APPLICATION_DATA_VALUE adv1, adv2; + bacnet_primitive_to_application_data_value(&adv1, &tv1->Value); + bacnet_primitive_to_application_data_value(&adv2, &tv2->Value); + + if (!bacapp_same_value(&adv1, &adv2)) + { + return false; + } + } + } + + return true; +} diff --git a/src/bacnet/weeklyschedule.h b/src/bacnet/weeklyschedule.h new file mode 100644 index 00000000..57ff377a --- /dev/null +++ b/src/bacnet/weeklyschedule.h @@ -0,0 +1,71 @@ +/************************************************************************** + * + * Copyright (C) 2012 Steve Karg + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + *********************************************************************/ +#ifndef WEEKLYSCHEDULE_H +#define WEEKLYSCHEDULE_H + +#include +#include +#include "bacnet/bacnet_stack_exports.h" +#include "bacnet/dailyschedule.h" +#include "bacnet/bactimevalue.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + typedef struct BACnet_Weekly_Schedule { + BACNET_DAILY_SCHEDULE weeklySchedule[7]; + bool singleDay; + } BACNET_WEEKLY_SCHEDULE; + + /** Decode WeeklySchedule */ + BACNET_STACK_EXPORT + int bacnet_weeklyschedule_decode( + uint8_t * apdu, + int max_apdu_len, + BACNET_WEEKLY_SCHEDULE * value); + + /** Encode WeeklySchedule */ + BACNET_STACK_EXPORT + int bacnet_weeklyschedule_encode( + uint8_t * apdu, + BACNET_WEEKLY_SCHEDULE * value); + + BACNET_STACK_EXPORT + int bacnet_weeklyschedule_context_encode( + uint8_t *apdu, uint8_t tag_number, BACNET_WEEKLY_SCHEDULE *value); + + BACNET_STACK_EXPORT + int bacnet_weeklyschedule_context_decode( + uint8_t *apdu, int max_apdu_len, uint8_t tag_number, + BACNET_WEEKLY_SCHEDULE *value); + + BACNET_STACK_EXPORT + bool bacnet_weeklyschedule_same( + BACNET_WEEKLY_SCHEDULE *value1, BACNET_WEEKLY_SCHEDULE *value2); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* WEEKLYSCHEDULE_H */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6f5e7122..18f6464f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -70,6 +70,7 @@ list(APPEND testdirs bacnet/whohas bacnet/whois bacnet/wp + bacnet/weeklyschedule ) # bacnet/basic/* diff --git a/test/bacnet/bacapp/CMakeLists.txt b/test/bacnet/bacapp/CMakeLists.txt index 4a20794b..0023cae9 100644 --- a/test/bacnet/bacapp/CMakeLists.txt +++ b/test/bacnet/bacapp/CMakeLists.txt @@ -50,6 +50,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/indtext.c ${SRC_DIR}/bacnet/datalink/bvlc.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/bacdevobjpropref/CMakeLists.txt b/test/bacnet/bacdevobjpropref/CMakeLists.txt index 24e1831c..cf885b3c 100644 --- a/test/bacnet/bacdevobjpropref/CMakeLists.txt +++ b/test/bacnet/bacdevobjpropref/CMakeLists.txt @@ -48,6 +48,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/hostnport.c ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/bactimevalue/CMakeLists.txt b/test/bacnet/bactimevalue/CMakeLists.txt index a2fa2e24..c8099baa 100644 --- a/test/bacnet/bactimevalue/CMakeLists.txt +++ b/test/bacnet/bactimevalue/CMakeLists.txt @@ -49,6 +49,8 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/hostnport.c ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/bactimevalue/src/main.c b/test/bacnet/bactimevalue/src/main.c index 80666278..38168c2a 100644 --- a/test/bacnet/bactimevalue/src/main.c +++ b/test/bacnet/bactimevalue/src/main.c @@ -16,6 +16,7 @@ #include #include "bacnet/bactimevalue.h" #include "bacnet/datetime.h" +#include "bacnet/bacapp.h" /** @@ -35,26 +36,30 @@ static void test_BACnetTimeValue(BACNET_TIME_VALUE *value) bool status = false; uint8_t tag_number = 0; - len = bacapp_encode_time_value(apdu, value); - apdu_len = bacapp_decode_time_value(apdu, &test_value); + len = bacnet_time_value_encode(apdu, value); + apdu_len = bacnet_time_value_decode(apdu, len, &test_value); zassert_true(len > 0, NULL); zassert_true(apdu_len > 0, NULL); diff = datetime_compare_time(&test_value.Time, &value->Time); zassert_true(diff == 0, NULL); - status = bacapp_same_value(&test_value.Value, &value->Value); + status = bacapp_same_value( + (BACNET_APPLICATION_DATA_VALUE *) &test_value.Value, + (BACNET_APPLICATION_DATA_VALUE *) &value->Value); zassert_true(status, NULL); - len = bacapp_encode_context_time_value(apdu, tag_number, value); - apdu_len = bacapp_decode_context_time_value(apdu, tag_number, &test_value); + len = bacnet_time_value_context_encode(apdu, tag_number, value); + apdu_len = bacnet_time_value_context_decode(apdu, len, tag_number, &test_value); zassert_true(len > 0, NULL); zassert_true(apdu_len > 0, NULL); diff = datetime_compare_time(&test_value.Time, &value->Time); zassert_true(diff == 0, NULL); - status = bacapp_same_value(&test_value.Value, &value->Value); + status = bacapp_same_value( + (BACNET_APPLICATION_DATA_VALUE *) &test_value.Value, + (BACNET_APPLICATION_DATA_VALUE *) &value->Value); zassert_true(status, NULL); /* negative testing */ tag_number++; - apdu_len = bacapp_decode_context_time_value(apdu, tag_number, &test_value); + apdu_len = bacnet_time_value_context_decode(apdu, len, tag_number, &test_value); zassert_true(apdu_len < 0, NULL); } diff --git a/test/bacnet/basic/binding/address/CMakeLists.txt b/test/bacnet/basic/binding/address/CMakeLists.txt index 47467c71..08d6b7ec 100644 --- a/test/bacnet/basic/binding/address/CMakeLists.txt +++ b/test/bacnet/basic/binding/address/CMakeLists.txt @@ -49,6 +49,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/hostnport.c ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/acc/CMakeLists.txt b/test/bacnet/basic/object/acc/CMakeLists.txt index 18c4749c..fa16aca9 100644 --- a/test/bacnet/basic/object/acc/CMakeLists.txt +++ b/test/bacnet/basic/object/acc/CMakeLists.txt @@ -48,6 +48,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/access_credential/CMakeLists.txt b/test/bacnet/basic/object/access_credential/CMakeLists.txt index 06a55863..84789aa6 100644 --- a/test/bacnet/basic/object/access_credential/CMakeLists.txt +++ b/test/bacnet/basic/object/access_credential/CMakeLists.txt @@ -52,6 +52,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/access_door/CMakeLists.txt b/test/bacnet/basic/object/access_door/CMakeLists.txt index a4c3bfc1..e085dff2 100644 --- a/test/bacnet/basic/object/access_door/CMakeLists.txt +++ b/test/bacnet/basic/object/access_door/CMakeLists.txt @@ -49,6 +49,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/access_point/CMakeLists.txt b/test/bacnet/basic/object/access_point/CMakeLists.txt index 5a4198be..c63f13a5 100644 --- a/test/bacnet/basic/object/access_point/CMakeLists.txt +++ b/test/bacnet/basic/object/access_point/CMakeLists.txt @@ -48,6 +48,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/hostnport.c ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/access_rights/CMakeLists.txt b/test/bacnet/basic/object/access_rights/CMakeLists.txt index 923eba22..2198bebc 100644 --- a/test/bacnet/basic/object/access_rights/CMakeLists.txt +++ b/test/bacnet/basic/object/access_rights/CMakeLists.txt @@ -50,6 +50,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/access_user/CMakeLists.txt b/test/bacnet/basic/object/access_user/CMakeLists.txt index ce57a259..e859f8b2 100644 --- a/test/bacnet/basic/object/access_user/CMakeLists.txt +++ b/test/bacnet/basic/object/access_user/CMakeLists.txt @@ -49,6 +49,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/access_zone/CMakeLists.txt b/test/bacnet/basic/object/access_zone/CMakeLists.txt index 379ed2c2..68af71a6 100644 --- a/test/bacnet/basic/object/access_zone/CMakeLists.txt +++ b/test/bacnet/basic/object/access_zone/CMakeLists.txt @@ -53,6 +53,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/ai/CMakeLists.txt b/test/bacnet/basic/object/ai/CMakeLists.txt index 83815df6..9178144a 100644 --- a/test/bacnet/basic/object/ai/CMakeLists.txt +++ b/test/bacnet/basic/object/ai/CMakeLists.txt @@ -51,6 +51,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/memcopy.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/ai/src/main.c b/test/bacnet/basic/object/ai/src/main.c index 9397569f..34c66d81 100644 --- a/test/bacnet/basic/object/ai/src/main.c +++ b/test/bacnet/basic/object/ai/src/main.c @@ -10,6 +10,7 @@ #include #include +#include /** * @addtogroup bacnet_tests diff --git a/test/bacnet/basic/object/ao/CMakeLists.txt b/test/bacnet/basic/object/ao/CMakeLists.txt index 96604c96..4249504e 100644 --- a/test/bacnet/basic/object/ao/CMakeLists.txt +++ b/test/bacnet/basic/object/ao/CMakeLists.txt @@ -49,6 +49,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/av/CMakeLists.txt b/test/bacnet/basic/object/av/CMakeLists.txt index 1fbfd8e9..dc2cdc23 100644 --- a/test/bacnet/basic/object/av/CMakeLists.txt +++ b/test/bacnet/basic/object/av/CMakeLists.txt @@ -51,6 +51,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/memcopy.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/bi/CMakeLists.txt b/test/bacnet/basic/object/bi/CMakeLists.txt index 73cd2faa..27a72a57 100644 --- a/test/bacnet/basic/object/bi/CMakeLists.txt +++ b/test/bacnet/basic/object/bi/CMakeLists.txt @@ -51,6 +51,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/memcopy.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/bi/src/main.c b/test/bacnet/basic/object/bi/src/main.c index 8048322d..edfd3f0d 100644 --- a/test/bacnet/basic/object/bi/src/main.c +++ b/test/bacnet/basic/object/bi/src/main.c @@ -10,6 +10,7 @@ #include #include +#include /** * @addtogroup bacnet_tests diff --git a/test/bacnet/basic/object/bo/CMakeLists.txt b/test/bacnet/basic/object/bo/CMakeLists.txt index 20e24ba3..16102ea7 100644 --- a/test/bacnet/basic/object/bo/CMakeLists.txt +++ b/test/bacnet/basic/object/bo/CMakeLists.txt @@ -49,6 +49,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/bv/CMakeLists.txt b/test/bacnet/basic/object/bv/CMakeLists.txt index 579f62fe..13922a45 100644 --- a/test/bacnet/basic/object/bv/CMakeLists.txt +++ b/test/bacnet/basic/object/bv/CMakeLists.txt @@ -49,6 +49,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/color_object/CMakeLists.txt b/test/bacnet/basic/object/color_object/CMakeLists.txt index 9a48278b..fd864712 100644 --- a/test/bacnet/basic/object/color_object/CMakeLists.txt +++ b/test/bacnet/basic/object/color_object/CMakeLists.txt @@ -52,6 +52,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/memcopy.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ../mock/device_mock.c diff --git a/test/bacnet/basic/object/color_temperature/CMakeLists.txt b/test/bacnet/basic/object/color_temperature/CMakeLists.txt index 603d69d4..8ae88e12 100644 --- a/test/bacnet/basic/object/color_temperature/CMakeLists.txt +++ b/test/bacnet/basic/object/color_temperature/CMakeLists.txt @@ -52,6 +52,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/memcopy.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ../mock/device_mock.c diff --git a/test/bacnet/basic/object/command/CMakeLists.txt b/test/bacnet/basic/object/command/CMakeLists.txt index 98b771a1..3a2e8e7a 100644 --- a/test/bacnet/basic/object/command/CMakeLists.txt +++ b/test/bacnet/basic/object/command/CMakeLists.txt @@ -49,6 +49,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/credential_data_input/CMakeLists.txt b/test/bacnet/basic/object/credential_data_input/CMakeLists.txt index 9bafae5b..797f52a1 100644 --- a/test/bacnet/basic/object/credential_data_input/CMakeLists.txt +++ b/test/bacnet/basic/object/credential_data_input/CMakeLists.txt @@ -52,6 +52,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/device/CMakeLists.txt b/test/bacnet/basic/object/device/CMakeLists.txt index 46105bbc..d2ae3c90 100644 --- a/test/bacnet/basic/object/device/CMakeLists.txt +++ b/test/bacnet/basic/object/device/CMakeLists.txt @@ -90,6 +90,8 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/reject.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/dailyschedule.c ./stubs.c # Test and test library files ./src/main.c diff --git a/test/bacnet/basic/object/lc/CMakeLists.txt b/test/bacnet/basic/object/lc/CMakeLists.txt index cdf259f3..2e906ca2 100644 --- a/test/bacnet/basic/object/lc/CMakeLists.txt +++ b/test/bacnet/basic/object/lc/CMakeLists.txt @@ -51,6 +51,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c ./stubs.c # Test and test library files ./src/main.c diff --git a/test/bacnet/basic/object/lo/CMakeLists.txt b/test/bacnet/basic/object/lo/CMakeLists.txt index 334e2acc..11449ae8 100644 --- a/test/bacnet/basic/object/lo/CMakeLists.txt +++ b/test/bacnet/basic/object/lo/CMakeLists.txt @@ -51,6 +51,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/lsp/CMakeLists.txt b/test/bacnet/basic/object/lsp/CMakeLists.txt index 4fb65519..c18e2a17 100644 --- a/test/bacnet/basic/object/lsp/CMakeLists.txt +++ b/test/bacnet/basic/object/lsp/CMakeLists.txt @@ -50,6 +50,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/ms-input/CMakeLists.txt b/test/bacnet/basic/object/ms-input/CMakeLists.txt index 7a095219..52da6c89 100644 --- a/test/bacnet/basic/object/ms-input/CMakeLists.txt +++ b/test/bacnet/basic/object/ms-input/CMakeLists.txt @@ -49,6 +49,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ../mock/device_mock.c diff --git a/test/bacnet/basic/object/mso/CMakeLists.txt b/test/bacnet/basic/object/mso/CMakeLists.txt index d2a45fe0..970686ee 100644 --- a/test/bacnet/basic/object/mso/CMakeLists.txt +++ b/test/bacnet/basic/object/mso/CMakeLists.txt @@ -49,6 +49,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/msv/CMakeLists.txt b/test/bacnet/basic/object/msv/CMakeLists.txt index 8aa989ec..e44eaa89 100644 --- a/test/bacnet/basic/object/msv/CMakeLists.txt +++ b/test/bacnet/basic/object/msv/CMakeLists.txt @@ -51,6 +51,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/memcopy.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/netport/CMakeLists.txt b/test/bacnet/basic/object/netport/CMakeLists.txt index ec4261ef..f86c9c37 100644 --- a/test/bacnet/basic/object/netport/CMakeLists.txt +++ b/test/bacnet/basic/object/netport/CMakeLists.txt @@ -52,6 +52,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/proplist.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/osv/CMakeLists.txt b/test/bacnet/basic/object/osv/CMakeLists.txt index 83fbc904..a44b2909 100644 --- a/test/bacnet/basic/object/osv/CMakeLists.txt +++ b/test/bacnet/basic/object/osv/CMakeLists.txt @@ -49,6 +49,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/piv/CMakeLists.txt b/test/bacnet/basic/object/piv/CMakeLists.txt index cd5597e8..26307ac3 100644 --- a/test/bacnet/basic/object/piv/CMakeLists.txt +++ b/test/bacnet/basic/object/piv/CMakeLists.txt @@ -49,6 +49,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/basic/object/schedule/CMakeLists.txt b/test/bacnet/basic/object/schedule/CMakeLists.txt index 4516655a..1059d26f 100644 --- a/test/bacnet/basic/object/schedule/CMakeLists.txt +++ b/test/bacnet/basic/object/schedule/CMakeLists.txt @@ -50,6 +50,8 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/cov/CMakeLists.txt b/test/bacnet/cov/CMakeLists.txt index 4da8af1d..f6052ca1 100644 --- a/test/bacnet/cov/CMakeLists.txt +++ b/test/bacnet/cov/CMakeLists.txt @@ -49,6 +49,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/memcopy.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/getalarm/CMakeLists.txt b/test/bacnet/getalarm/CMakeLists.txt index f6da266b..0d1a6a90 100644 --- a/test/bacnet/getalarm/CMakeLists.txt +++ b/test/bacnet/getalarm/CMakeLists.txt @@ -49,6 +49,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/indtext.c ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/getevent/CMakeLists.txt b/test/bacnet/getevent/CMakeLists.txt index 5d99dd74..4495edbf 100644 --- a/test/bacnet/getevent/CMakeLists.txt +++ b/test/bacnet/getevent/CMakeLists.txt @@ -49,6 +49,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/indtext.c ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/lso/CMakeLists.txt b/test/bacnet/lso/CMakeLists.txt index 00143bab..00f18150 100644 --- a/test/bacnet/lso/CMakeLists.txt +++ b/test/bacnet/lso/CMakeLists.txt @@ -50,6 +50,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/memcopy.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/ptransfer/CMakeLists.txt b/test/bacnet/ptransfer/CMakeLists.txt index cfd822c0..489afaef 100644 --- a/test/bacnet/ptransfer/CMakeLists.txt +++ b/test/bacnet/ptransfer/CMakeLists.txt @@ -49,6 +49,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/hostnport.c ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/rpm/CMakeLists.txt b/test/bacnet/rpm/CMakeLists.txt index ae658857..17f819f7 100644 --- a/test/bacnet/rpm/CMakeLists.txt +++ b/test/bacnet/rpm/CMakeLists.txt @@ -51,6 +51,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/memcopy.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/timesync/CMakeLists.txt b/test/bacnet/timesync/CMakeLists.txt index 9bed9c6c..98f60517 100644 --- a/test/bacnet/timesync/CMakeLists.txt +++ b/test/bacnet/timesync/CMakeLists.txt @@ -50,6 +50,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/hostnport.c ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/weeklyschedule/CMakeLists.txt b/test/bacnet/weeklyschedule/CMakeLists.txt new file mode 100644 index 00000000..4f5f3779 --- /dev/null +++ b/test/bacnet/weeklyschedule/CMakeLists.txt @@ -0,0 +1,58 @@ +# 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 + BACAPP_ALL + ) + +include_directories( + ${SRC_DIR} + ${TST_DIR}/ztest/include + ) + +add_executable(${PROJECT_NAME} + # File(s) under test + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c + # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacapp.c + ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacdevobjpropref.c + ${SRC_DIR}/bacnet/bacreal.c + ${SRC_DIR}/bacnet/bacstr.c + ${SRC_DIR}/bacnet/bactext.c + ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/datetime.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/hostnport.c + ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/timestamp.c + # Test and test library files + ./src/main.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) diff --git a/test/bacnet/weeklyschedule/src/main.c b/test/bacnet/weeklyschedule/src/main.c new file mode 100644 index 00000000..fb53e830 --- /dev/null +++ b/test/bacnet/weeklyschedule/src/main.c @@ -0,0 +1,103 @@ +/** + * @file + * @brief Unit test for BACnetWeeklySchedule + * @author Ondřej Hruška + * @date Aug 2022 + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include +#include +#include +#include +#include "bacnet/bactimevalue.h" +#include "bacnet/weeklyschedule.h" +#include "bacnet/dailyschedule.h" +#include "bacnet/datetime.h" +#include "bacnet/bacapp.h" + + +/** + * @addtogroup bacnet_tests + * @{ + */ + +/** + * @brief Test encode/decode API + */ +static void test_BACnetWeeklySchedule() +{ + int len, apdu_len; + uint8_t apdu[MAX_APDU] = { 0 }; + BACNET_WEEKLY_SCHEDULE empty_value = { 0 }; + BACNET_WEEKLY_SCHEDULE value = { 0 }; + BACNET_WEEKLY_SCHEDULE decoded = { 0 }; + int diff = 0; + bool status = false; + uint8_t tag_number = 0; + + value.weeklySchedule[0].TV_Count = 2; + + value.weeklySchedule[0].Time_Values[0].Time = (BACNET_TIME) { + .hour = 5, + .min = 30 + }; + value.weeklySchedule[0].Time_Values[0].Value = (BACNET_PRIMITIVE_DATA_VALUE) { + .tag = BACNET_APPLICATION_TAG_UNSIGNED_INT, + .type.Unsigned_Int = 123, + }; + + value.weeklySchedule[0].Time_Values[1].Time = (BACNET_TIME) { + .hour = 15, + .min = 0 + }; + value.weeklySchedule[0].Time_Values[1].Value = (BACNET_PRIMITIVE_DATA_VALUE) { + .tag = BACNET_APPLICATION_TAG_UNSIGNED_INT, + .type.Unsigned_Int = 456, + }; + + value.weeklySchedule[6].TV_Count = 1; + value.weeklySchedule[6].Time_Values[0].Value = (BACNET_PRIMITIVE_DATA_VALUE) { + .tag = BACNET_APPLICATION_TAG_UNSIGNED_INT, + .type.Unsigned_Int = 777, + }; + + + len = bacnet_weeklyschedule_encode(apdu, &value); + apdu_len = bacnet_weeklyschedule_decode(apdu, len, &decoded); + zassert_true(len > 0, NULL); + zassert_true(apdu_len > 0, NULL); + status = bacnet_weeklyschedule_same(&value, &decoded); + zassert_true(status, NULL); + // Check that the compare function really compares: + status = bacnet_weeklyschedule_same(&empty_value, &decoded); + zassert_false(status, NULL); + + len = bacnet_weeklyschedule_context_encode(apdu, tag_number, &value); + apdu_len = bacnet_weeklyschedule_context_decode(apdu, len, tag_number, &decoded); + zassert_true(len > 0, NULL); + zassert_true(apdu_len > 0, NULL); + status = bacnet_weeklyschedule_same(&value, &decoded); + zassert_true(status, NULL); + + /* negative testing - the tag differs */ + tag_number++; + apdu_len = bacnet_weeklyschedule_context_decode(apdu, len, tag_number, &decoded); + zassert_true(apdu_len < 0, NULL); +} + +/** + * @} + */ +void test_main(void) +{ + ztest_test_suite(BACnetWeeklySchedule_tests, + ztest_unit_test(test_BACnetWeeklySchedule) + ); + + ztest_run_test_suite(BACnetWeeklySchedule_tests); +} diff --git a/test/bacnet/wp/CMakeLists.txt b/test/bacnet/wp/CMakeLists.txt index 8ac9ebe3..2c2e0848 100644 --- a/test/bacnet/wp/CMakeLists.txt +++ b/test/bacnet/wp/CMakeLists.txt @@ -49,6 +49,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c