From dcf79b038f53d686ff7feb251fafbec6936e4429 Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Wed, 23 Oct 2024 13:54:48 -0500 Subject: [PATCH] Enabled BACnetARRAY checking when using proplist.c and added to apps/epics (#823) --- CMakeLists.txt | 6 - apps/Makefile | 1 - apps/epics/main.c | 334 +++++++++--------- src/bacnet/basic/object/access_rights.c | 16 +- src/bacnet/basic/object/schedule.c | 11 +- src/bacnet/basic/object/trendlog.c | 22 +- src/bacnet/property.c | 96 ----- src/bacnet/property.h | 6 - src/bacnet/proplist.c | 210 +++++++++++ src/bacnet/proplist.h | 11 + .../basic/object/access_rights/CMakeLists.txt | 1 - .../object/bitstring_value/CMakeLists.txt | 1 - .../bacnet/basic/object/device/CMakeLists.txt | 1 - .../object/structured_view/CMakeLists.txt | 1 - test/bacnet/property/CMakeLists.txt | 1 - 15 files changed, 421 insertions(+), 297 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 97fe7832..af8a04b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,11 +24,6 @@ option( "enable property lists" ON) -option( - BACNET_PROPERTY_ARRAY_LISTS - "enable property array lists" - ON) - option( BACNET_BUILD_PIFACE_APP "compile the piface app" @@ -626,7 +621,6 @@ target_compile_definitions( $<$:BACDL_ETHERNET> $<$:BACDL_NONE> $<$:BACNET_PROPERTY_LISTS=1> - $<$:BACNET_PROPERTY_ARRAY_LISTS=1> $<$:BAC_ROUTING> $<$>:BACNET_STACK_STATIC_DEFINE> PRIVATE diff --git a/apps/Makefile b/apps/Makefile index 857cd3d1..77f98554 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -178,7 +178,6 @@ BACNET_DEFINES += -DBACFILE BACNET_DEFINES += -DINTRINSIC_REPORTING BACNET_DEFINES += -DBACNET_TIME_MASTER BACNET_DEFINES += -DBACNET_PROPERTY_LISTS=1 -BACNET_DEFINES += -DBACNET_PROPERTY_ARRAY_LISTS=1 BACNET_DEFINES += -DBACNET_PROTOCOL_REVISION=24 # put all the flags together diff --git a/apps/epics/main.c b/apps/epics/main.c index 844d50d9..f0a6e2ce 100644 --- a/apps/epics/main.c +++ b/apps/epics/main.c @@ -147,6 +147,7 @@ static BACNET_APPLICATION_DATA_VALUE *object_property_value(int32_t property_id) */ static uint32_t Walked_List_Length = 0; static uint32_t Walked_List_Index = 0; +static uint32_t Walked_List_Columns = 0; /* TODO: Probably should have done this as additional EPICS_STATES */ static bool Using_Walked_List = false; /* When requesting RP for BACNET_ARRAY_ALL of what we know can be a long @@ -439,6 +440,106 @@ static bool PrettyPrintPropertyValue( return status; } +static void PrintReadPropertyArray( + BACNET_PROPERTY_REFERENCE *rpm_property, + BACNET_APPLICATION_DATA_VALUE *value, + BACNET_OBJECT_PROPERTY_VALUE *object_value) +{ + KEY object_list_element; + uint32_t columns; + + /* These are all arrays, so they open and close with braces */ + if (Using_Walked_List) { + if ((rpm_property->propertyArrayIndex == 0) && + (value->tag == BACNET_APPLICATION_TAG_UNSIGNED_INT)) { + /* Grab the value of the Object List length - don't + * print it! */ + Walked_List_Length = value->type.Unsigned_Int; + if (rpm_property->propertyIdentifier == PROP_OBJECT_LIST) { + Object_List_Length = value->type.Unsigned_Int; + } + return; + } else { + assert( + Walked_List_Index == + (uint32_t)rpm_property->propertyArrayIndex); + } + } else { + Walked_List_Index++; + /* If we got the whole Object List array in one RP call, + * keep the Index and List_Length in sync as we cycle + * through. */ + if (rpm_property->propertyIdentifier == PROP_OBJECT_LIST) { + Object_List_Length = ++Object_List_Index; + } + } + if (Walked_List_Index == 1) { + /* If the array is empty, make it VTS3-friendly */ + if (value->tag == BACNET_APPLICATION_TAG_EMPTYLIST) { + fprintf(stdout, "?\n "); + return; + } + + /* Open this Array of Objects for the first entry (unless + * opening brace has already printed, since this is an array + * of values[] ) */ + if (value->next == NULL) { + fprintf(stdout, "{ \n "); + } else { + fprintf(stdout, "\n "); + } + } + + if (rpm_property->propertyIdentifier == PROP_OBJECT_LIST) { + if (value->tag != BACNET_APPLICATION_TAG_OBJECT_ID) { + return; + } + /* Store the object list so we can interrogate + each object. */ + object_list_element = KEY_ENCODE( + value->type.Object_Id.type, value->type.Object_Id.instance); + /* We don't have anything to put in the data pointer + * yet, so just leave it null. The key is Key here. */ + Keylist_Data_Add(Object_List, object_list_element, NULL); + } else if (rpm_property->propertyIdentifier == PROP_STATE_TEXT) { + /* Make sure it fits within 31 chars for original VTS3 + * limitation. If longer, take first 15 dash, and last 15 + * chars. */ + if (value->type.Character_String.length > 31) { + int iLast15idx = value->type.Character_String.length - 15; + value->type.Character_String.value[15] = '-'; + memmove( + &value->type.Character_String.value[16], + &value->type.Character_String.value[iLast15idx], 15); + value->type.Character_String.value[31] = 0; + value->type.Character_String.length = 31; + } + } else if (rpm_property->propertyIdentifier == PROP_SUBORDINATE_LIST) { + if (value->tag != BACNET_APPLICATION_TAG_DEVICE_OBJECT_REFERENCE) { + return; + } + } + bacapp_print_value(stdout, object_value); + if ((Walked_List_Index < Walked_List_Length) || (value->next != NULL)) { + columns = Walked_List_Columns; + if (Walked_List_Columns == 0) { + if ((rpm_property->propertyIdentifier == PROP_OBJECT_LIST) || + (rpm_property->propertyIdentifier == PROP_PRIORITY_ARRAY)) { + /* traditional behavior - 3 columns for object-list */ + columns = 3; + } + } + /* There are more. */ + fprintf(stdout, ", "); + if ((columns == 0) || (!(Walked_List_Index % columns))) { + /* newline and indent for the next value */ + fprintf(stdout, "\n "); + } + } else { + fprintf(stdout, " } \n"); + } +} + /** Print out the value(s) for one Property. * This function may be called repeatedly for one property if we are walking * through a list (Using_Walked_List is True) to show just one value of the @@ -457,7 +558,6 @@ static void PrintReadPropertyData( BACNET_OBJECT_PROPERTY_VALUE object_value; /* for bacapp printing */ BACNET_APPLICATION_DATA_VALUE *value, *old_value; bool print_brace = false; - KEY object_list_element; if (rpm_property == NULL) { fprintf(stdout, " -- Null Property data \n"); @@ -488,8 +588,7 @@ static void PrintReadPropertyData( * print anything for them. To achieve this, swap * out the Property for a non-existent Property * and catch that below. */ - rpm_property->propertyIdentifier = - PROP_PROTOCOL_CONFORMANCE_CLASS; + rpm_property->propertyIdentifier = MAX_BACNET_PROPERTY_ID; break; } if (object_type == OBJECT_DATETIME_VALUE) { @@ -503,7 +602,6 @@ static void PrintReadPropertyData( break; } } - if (!Using_Walked_List) { Walked_List_Index = Walked_List_Length = 0; /* In case we need this. */ } @@ -512,169 +610,70 @@ static void PrintReadPropertyData( object_value.object_property = rpm_property->propertyIdentifier; object_value.array_index = rpm_property->propertyArrayIndex; object_value.value = value; - switch (rpm_property->propertyIdentifier) { - /* These are all arrays, so they open and close with braces */ - case PROP_OBJECT_LIST: - case PROP_STATE_TEXT: - case PROP_STRUCTURED_OBJECT_LIST: - case PROP_SUBORDINATE_ANNOTATIONS: - case PROP_SUBORDINATE_LIST: - if (Using_Walked_List) { - if ((rpm_property->propertyArrayIndex == 0) && - (value->tag == BACNET_APPLICATION_TAG_UNSIGNED_INT)) { - /* Grab the value of the Object List length - don't - * print it! */ - Walked_List_Length = value->type.Unsigned_Int; - if (rpm_property->propertyIdentifier == - PROP_OBJECT_LIST) { - Object_List_Length = value->type.Unsigned_Int; - } - break; - } else { - assert( - Walked_List_Index == - (uint32_t)rpm_property->propertyArrayIndex); - } - } else { - Walked_List_Index++; - /* If we got the whole Object List array in one RP call, - * keep the Index and List_Length in sync as we cycle - * through. */ - if (rpm_property->propertyIdentifier == PROP_OBJECT_LIST) { - Object_List_Length = ++Object_List_Index; - } - } - if (Walked_List_Index == 1) { - /* If the array is empty (nothing for this first entry), - * Make it VTS3-friendly and don't show "Null" as a value. - */ - if (value->tag == BACNET_APPLICATION_TAG_NULL) { - fprintf(stdout, "?\n "); - break; - } - - /* Open this Array of Objects for the first entry (unless - * opening brace has already printed, since this is an array - * of values[] ) */ - if (value->next == NULL) { - fprintf(stdout, "{ \n "); - } else { - fprintf(stdout, "\n "); - } - } - - if (rpm_property->propertyIdentifier == PROP_OBJECT_LIST) { - if (value->tag != BACNET_APPLICATION_TAG_OBJECT_ID) { - break; - } - /* Store the object list so we can interrogate - each object. */ - object_list_element = KEY_ENCODE( - value->type.Object_Id.type, - value->type.Object_Id.instance); - /* We don't have anything to put in the data pointer - * yet, so just leave it null. The key is Key here. */ - Keylist_Data_Add(Object_List, object_list_element, NULL); - } else if ( - rpm_property->propertyIdentifier == PROP_STATE_TEXT) { - /* Make sure it fits within 31 chars for original VTS3 - * limitation. If longer, take first 15 dash, and last 15 - * chars. */ - if (value->type.Character_String.length > 31) { - int iLast15idx = - value->type.Character_String.length - 15; - value->type.Character_String.value[15] = '-'; - memmove( - &value->type.Character_String.value[16], - &value->type.Character_String.value[iLast15idx], - 15); - value->type.Character_String.value[31] = 0; - value->type.Character_String.length = 31; - } - } else if ( - rpm_property->propertyIdentifier == PROP_SUBORDINATE_LIST) { - if (value->tag != - BACNET_APPLICATION_TAG_DEVICE_OBJECT_REFERENCE) { - break; - } - } - bacapp_print_value(stdout, &object_value); - if ((Walked_List_Index < Walked_List_Length) || - (value->next != NULL)) { - /* There are more. */ - fprintf(stdout, ", "); - if (!(Walked_List_Index % 3)) { - fprintf(stdout, "\n "); - } - } else { - fprintf(stdout, " } \n"); - } - break; - - case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: - case PROP_PROTOCOL_SERVICES_SUPPORTED: + if (property_list_bacnet_array_member( + object_type, rpm_property->propertyIdentifier)) { + PrintReadPropertyArray(rpm_property, value, &object_value); + } else if ( + (rpm_property->propertyIdentifier == + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED) || + (rpm_property->propertyIdentifier == + PROP_PROTOCOL_SERVICES_SUPPORTED)) { + PrettyPrintPropertyValue(stdout, &object_value); + } else if (rpm_property->propertyIdentifier == MAX_BACNET_PROPERTY_ID) { + /* Our special non-existent case; do nothing further here. */ + } else { + /* First, if this is a date type, it needs a different format + * for VTS, so pretty print it. */ + if (ShowValues && + (object_value.value->tag == BACNET_APPLICATION_TAG_DATE)) { + /* This would be PROP_LOCAL_DATE, or OBJECT_DATETIME_VALUE, + * or OBJECT_DATE_VALUE */ PrettyPrintPropertyValue(stdout, &object_value); - break; - - /* Our special non-existent case; do nothing further here. */ - case PROP_PROTOCOL_CONFORMANCE_CLASS: - break; - - default: - /* First, if this is a date type, it needs a different format - * for VTS, so pretty print it. */ - if (ShowValues && - (object_value.value->tag == BACNET_APPLICATION_TAG_DATE)) { - /* This would be PROP_LOCAL_DATE, or OBJECT_DATETIME_VALUE, - * or OBJECT_DATE_VALUE */ - PrettyPrintPropertyValue(stdout, &object_value); - } else { - /* Some properties are presented just as '?' in an EPICS; - * screen these out here, unless ShowValues is true. */ - switch (rpm_property->propertyIdentifier) { - case PROP_DEVICE_ADDRESS_BINDING: - /* Make it VTS3-friendly and don't show "Null" - * as a value. */ - if (value->tag == BACNET_APPLICATION_TAG_NULL) { - fprintf(stdout, "?"); - break; - } - BACNET_STACK_FALLTHROUGH(); - case PROP_DAYLIGHT_SAVINGS_STATUS: - case PROP_LOCAL_TIME: - case PROP_LOCAL_DATE: /* Only if !ShowValues */ - case PROP_PRESENT_VALUE: - case PROP_PRIORITY_ARRAY: - case PROP_RELIABILITY: - case PROP_UTC_OFFSET: - case PROP_DATABASE_REVISION: - if (!ShowValues) { - fprintf(stdout, "?"); - break; - } - BACNET_STACK_FALLTHROUGH(); - default: - bacapp_print_value(stdout, &object_value); + } else { + /* Some properties are presented just as '?' in an EPICS; + * screen these out here, unless ShowValues is true. */ + switch (rpm_property->propertyIdentifier) { + case PROP_DEVICE_ADDRESS_BINDING: + /* Make it VTS3-friendly and don't show "Null" + * as a value. */ + if (value->tag == BACNET_APPLICATION_TAG_NULL) { + fprintf(stdout, "?"); break; - } + } + BACNET_STACK_FALLTHROUGH(); + case PROP_DAYLIGHT_SAVINGS_STATUS: + case PROP_LOCAL_TIME: + case PROP_LOCAL_DATE: /* Only if !ShowValues */ + case PROP_PRESENT_VALUE: + case PROP_PRIORITY_ARRAY: + case PROP_RELIABILITY: + case PROP_UTC_OFFSET: + case PROP_DATABASE_REVISION: + if (!ShowValues) { + fprintf(stdout, "?"); + break; + } + BACNET_STACK_FALLTHROUGH(); + default: + bacapp_print_value(stdout, &object_value); + break; } - if (value->next != NULL) { - /* there's more! */ - fprintf(stdout, ","); - } else { - if (print_brace) { - /* Closing brace for this multi-valued array */ - fprintf(stdout, " }"); - } - if (property_list_writable_member( - object_type, rpm_property->propertyIdentifier)) { - fprintf(stdout, " Writable"); - } - fprintf(stdout, "\n"); + } + if (value->next != NULL) { + /* there's more! */ + fprintf(stdout, ","); + } else { + if (print_brace) { + /* Closing brace for this multi-valued array */ + fprintf(stdout, " }"); } - break; + if (property_list_writable_member( + object_type, rpm_property->propertyIdentifier)) { + fprintf(stdout, " Writable"); + } + fprintf(stdout, "\n"); + } } - old_value = value; value = value->next; /* next or NULL */ free(old_value); @@ -924,6 +923,7 @@ static void print_help(const char *filename) "I-Am services.\n"); printf("\n"); printf("-v: show values instead of '?' \n"); + printf("-c: columns break for BACnetARRAY. Default is 0=always\n"); printf("-d: show only device object properties\n"); printf("-p: Use sport for \"my\" port. 0xBAC0 is default.\n"); printf(" Allows you to communicate with a localhost target.\n"); @@ -933,8 +933,8 @@ static void print_help(const char *filename) printf("-n: specify target's DNET if not local BACnet network \n"); printf(" or on routed Virtual Network \n"); printf("\n"); - printf("You can redirect the output to a .tpi file for VTS use,\n"); - printf("e.g., bacepics 2701876 > epics-2701876.tpi \n"); + printf("To generate output directly to a .tpi file for VTS:\n"); + printf("$ bacepics 4194302 > epics-4194302.tpi \n"); } static int CheckCommandLineArgs(int argc, char *argv[]) @@ -975,6 +975,12 @@ static int CheckCommandLineArgs(int argc, char *argv[]) case 'v': ShowValues = true; break; + case 'c': + /* Number of columns before a break for BACnetARRAY props */ + if (++i < argc) { + Walked_List_Columns = strtol(argv[i], NULL, 0); + } + break; case 'd': ShowDeviceObjectOnly = true; break; diff --git a/src/bacnet/basic/object/access_rights.c b/src/bacnet/basic/object/access_rights.c index 891913e8..cb307c76 100644 --- a/src/bacnet/basic/object/access_rights.c +++ b/src/bacnet/basic/object/access_rights.c @@ -300,6 +300,14 @@ bool Access_Rights_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) BACNET_APPLICATION_DATA_VALUE value = { 0 }; unsigned object_index = 0; + /* only array properties can have array options */ + is_array = property_list_bacnet_array_member( + wp_data->object_type, wp_data->object_property); + if (!is_array && (wp_data->array_index != BACNET_ARRAY_ALL)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } /* decode the some of the request */ len = bacapp_decode_application_data( wp_data->application_data, wp_data->application_data_len, &value); @@ -310,14 +318,6 @@ bool Access_Rights_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; return false; } - /* only array properties can have array options */ - is_array = property_list_bacnet_array_member( - wp_data->object_type, wp_data->object_property); - if (is_array && (wp_data->array_index != BACNET_ARRAY_ALL)) { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; - return false; - } object_index = Access_Rights_Instance_To_Index(wp_data->object_instance); switch (wp_data->object_property) { case PROP_GLOBAL_IDENTIFIER: diff --git a/src/bacnet/basic/object/schedule.c b/src/bacnet/basic/object/schedule.c index f2a8d7f0..bd64e50b 100644 --- a/src/bacnet/basic/object/schedule.c +++ b/src/bacnet/basic/object/schedule.c @@ -421,7 +421,16 @@ bool Schedule_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) bool status = false; /* return value */ int len; BACNET_APPLICATION_DATA_VALUE value = { 0 }; + bool is_array; + /* only array properties can have array options */ + is_array = property_list_bacnet_array_member( + wp_data->object_type, wp_data->object_property); + if (!is_array && (wp_data->array_index != BACNET_ARRAY_ALL)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } /* decode the some of the request */ len = bacapp_decode_application_data( wp_data->application_data, wp_data->application_data_len, &value); @@ -432,12 +441,10 @@ bool Schedule_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; return false; } - object_index = Schedule_Instance_To_Index(wp_data->object_instance); if (object_index >= MAX_SCHEDULES) { return false; } - switch ((int)wp_data->object_property) { case PROP_OUT_OF_SERVICE: status = write_property_type_valid( diff --git a/src/bacnet/basic/object/trendlog.c b/src/bacnet/basic/object/trendlog.c index 11e92123..24437a55 100644 --- a/src/bacnet/basic/object/trendlog.c +++ b/src/bacnet/basic/object/trendlog.c @@ -262,6 +262,7 @@ int Trend_Log_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) BACNET_CHARACTER_STRING char_string; TL_LOG_INFO *CurrentLog; uint8_t *apdu = NULL; + bool is_array; if ((rpdata == NULL) || (rpdata->application_data == NULL) || (rpdata->application_data_len == 0)) { @@ -405,8 +406,9 @@ int Trend_Log_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) break; } /* only array properties can have array options */ - if ((apdu_len >= 0) && - (rpdata->object_property != PROP_EVENT_TIME_STAMPS) && + is_array = property_list_bacnet_array_member( + rpdata->object_type, rpdata->object_property); + if ((apdu_len >= 0) && (!is_array) && (rpdata->array_index != BACNET_ARRAY_ALL)) { rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; @@ -427,7 +429,16 @@ bool Trend_Log_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE TempSource; bool bEffectiveEnable; int log_index; + bool is_array; + /* only array properties can have array options */ + is_array = property_list_bacnet_array_member( + wp_data->object_type, wp_data->object_property); + if (!is_array && (wp_data->array_index != BACNET_ARRAY_ALL)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } /* Pin down which log to look at */ log_index = Trend_Log_Instance_To_Index(wp_data->object_instance); CurrentLog = &LogInfo[log_index]; @@ -442,13 +453,6 @@ bool Trend_Log_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; return false; } - if ((wp_data->object_property != PROP_EVENT_TIME_STAMPS) && - (wp_data->array_index != BACNET_ARRAY_ALL)) { - /* only array properties can have array options */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; - return false; - } switch (wp_data->object_property) { case PROP_ENABLE: status = write_property_type_valid( diff --git a/src/bacnet/property.c b/src/bacnet/property.c index 70d55467..5478a5ad 100644 --- a/src/bacnet/property.c +++ b/src/bacnet/property.c @@ -19,10 +19,6 @@ #define BACNET_PROPERTY_LISTS 0 #endif -#ifndef BACNET_PROPERTY_ARRAY_LISTS -#define BACNET_PROPERTY_ARRAY_LISTS 0 -#endif - #if BACNET_PROPERTY_LISTS /** @file property.c List of Required and Optional object properties */ /* note: the PROP_PROPERTY_LIST is NOT included in these lists, on purpose */ @@ -3194,98 +3190,6 @@ unsigned property_list_special_count( } #endif -#if BACNET_PROPERTY_ARRAY_LISTS -/* standard properties that are arrays - but not necessary supported in every object */ - -static const int Properties_BACnetARRAY[] = { - /* unordered list of properties */ - PROP_AUTHENTICATION_FACTORS, - PROP_ASSIGNED_ACCESS_RIGHTS, - PROP_PRIORITY_ARRAY, - PROP_VALUE_SOURCE_ARRAY, - PROP_COMMAND_TIME_ARRAY, - PROP_ALARM_VALUES, - PROP_FAULT_VALUES, - PROP_EVENT_TIME_STAMPS, - PROP_EVENT_MESSAGE_TEXTS, - PROP_EVENT_MESSAGE_TEXTS_CONFIG, - PROP_SUPPORTED_FORMATS, - PROP_SUPPORTED_FORMAT_CLASSES, - PROP_SUBORDINATE_LIST, - PROP_SUBORDINATE_ANNOTATIONS, - PROP_SUBORDINATE_TAGS, - PROP_SUBORDINATE_NODE_TYPES, - PROP_SUBORDINATE_RELATIONSHIPS, - PROP_GROUP_MEMBERS, - PROP_GROUP_MEMBER_NAMES, - PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES, - PROP_EXECUTION_DELAY, - PROP_CONTROL_GROUPS, - PROP_BIT_TEXT, - PROP_PORT_FILTER, - PROP_NOTIFICATION_CLASS, - PROP_STATE_CHANGE_VALUES, - PROP_LINK_SPEEDS, - PROP_IP_DNS_SERVER, - PROP_IPV6_DNS_SERVER, - PROP_FLOOR_TEXT, - PROP_CAR_DOOR_TEXT, - PROP_ASSIGNED_LANDING_CALLS, - PROP_MAKING_CAR_CALL, - PROP_REGISTERED_CAR_CALL, - PROP_CAR_DOOR_STATUS, - PROP_CAR_DOOR_COMMAND, - PROP_LANDING_DOOR_STATUS, - PROP_STAGES, - PROP_STAGE_NAMES, - PROP_TARGET_REFERENCES, - PROP_MONITORED_OBJECTS, - PROP_TAGS, - -1 -}; - -/** - * Function that returns the list of Required properties - * of known standard objects. - * - * @param object_type - enumerated BACNET_OBJECT_TYPE - * @return returns a pointer to a '-1' terminated array of - * type 'int' that contain BACnet object properties for the given object - * type. - */ -const int *property_list_bacnet_array(void) -{ - return Properties_BACnetARRAY; -} - -/** - * @brief Determine if the object property is a BACnetARRAY property - * @param object_type - object-type to be checked - * @param object_property - object-property to be checked - * @return true if the property is a BACnetARRAY property - */ -bool property_list_bacnet_array_member( - BACNET_OBJECT_TYPE object_type, BACNET_PROPERTY_ID object_property) -{ - switch (object_type) { - case OBJECT_GLOBAL_GROUP: - switch (object_property) { - case PROP_PRESENT_VALUE: - /* special - the only present-value that is an array! */ - return true; - default: - break; - } - break; - default: - break; - } - - return property_list_member(Properties_BACnetARRAY, object_property); -} -#endif - /** * @brief Determine if this property is required to be writable * @note Property Conformance Code of W means writable diff --git a/src/bacnet/property.h b/src/bacnet/property.h index b7010dac..9db25b63 100644 --- a/src/bacnet/property.h +++ b/src/bacnet/property.h @@ -36,12 +36,6 @@ BACNET_PROPERTY_ID property_list_special_property( BACNET_STACK_EXPORT unsigned property_list_special_count( BACNET_OBJECT_TYPE object_type, BACNET_PROPERTY_ID special_property); - -BACNET_STACK_EXPORT -const int *property_list_bacnet_array(void); -BACNET_STACK_EXPORT -bool property_list_bacnet_array_member( - BACNET_OBJECT_TYPE object_type, BACNET_PROPERTY_ID object_property); BACNET_STACK_EXPORT bool property_list_writable_member( BACNET_OBJECT_TYPE object_type, BACNET_PROPERTY_ID object_property); diff --git a/src/bacnet/proplist.c b/src/bacnet/proplist.c index 31daea76..db844c3e 100644 --- a/src/bacnet/proplist.c +++ b/src/bacnet/proplist.c @@ -334,3 +334,213 @@ bool property_list_common(BACNET_PROPERTY_ID property) return status; } + +/* standard properties that are arrays + but not required to be supported in every object */ +static const int Properties_BACnetARRAY[] = { + /* unordered list of properties */ + PROP_OBJECT_LIST, + PROP_STRUCTURED_OBJECT_LIST, + PROP_CONFIGURATION_FILES, + PROP_PROPERTY_LIST, + PROP_AUTHENTICATION_FACTORS, + PROP_ASSIGNED_ACCESS_RIGHTS, + PROP_ACTION, + PROP_ACTION_TEXT, + PROP_PRIORITY_ARRAY, + PROP_VALUE_SOURCE_ARRAY, + PROP_COMMAND_TIME_ARRAY, + PROP_ALARM_VALUES, + PROP_FAULT_VALUES, + PROP_EVENT_TIME_STAMPS, + PROP_EVENT_MESSAGE_TEXTS, + PROP_EVENT_MESSAGE_TEXTS_CONFIG, + PROP_SUPPORTED_FORMATS, + PROP_SUPPORTED_FORMAT_CLASSES, + PROP_SUBORDINATE_LIST, + PROP_SUBORDINATE_ANNOTATIONS, + PROP_SUBORDINATE_TAGS, + PROP_SUBORDINATE_NODE_TYPES, + PROP_SUBORDINATE_RELATIONSHIPS, + PROP_GROUP_MEMBERS, + PROP_GROUP_MEMBER_NAMES, + PROP_EXECUTION_DELAY, + PROP_CONTROL_GROUPS, + PROP_BIT_TEXT, + PROP_PORT_FILTER, + PROP_NOTIFICATION_CLASS, + PROP_STATE_CHANGE_VALUES, + PROP_LINK_SPEEDS, + PROP_IP_DNS_SERVER, + PROP_IPV6_DNS_SERVER, + PROP_FLOOR_TEXT, + PROP_CAR_DOOR_TEXT, + PROP_ASSIGNED_LANDING_CALLS, + PROP_MAKING_CAR_CALL, + PROP_REGISTERED_CAR_CALL, + PROP_CAR_DOOR_STATUS, + PROP_CAR_DOOR_COMMAND, + PROP_LANDING_DOOR_STATUS, + PROP_STAGES, + PROP_STAGE_NAMES, + PROP_STATE_TEXT, + PROP_TARGET_REFERENCES, + PROP_MONITORED_OBJECTS, + PROP_SHED_LEVELS, + PROP_SHED_LEVEL_DESCRIPTIONS, + PROP_WEEKLY_SCHEDULE, + PROP_EXCEPTION_SCHEDULE, + PROP_TAGS, + -1 +}; + +/** + * Function that returns the list of Required properties + * of known standard objects. + * + * @param object_type - enumerated BACNET_OBJECT_TYPE + * @return returns a pointer to a '-1' terminated array of + * type 'int' that contain BACnet object properties for the given object + * type. + */ +const int *property_list_bacnet_array(void) +{ + return Properties_BACnetARRAY; +} + +/** + * @brief Determine if the object property is a BACnetARRAY property + * @param object_type - object-type to be checked + * @param object_property - object-property to be checked + * @return true if the property is a BACnetARRAY property + */ +bool property_list_bacnet_array_member( + BACNET_OBJECT_TYPE object_type, BACNET_PROPERTY_ID object_property) +{ + /* exceptions where property is an BACnetARRAY only in specific objects */ + switch (object_type) { + case OBJECT_GLOBAL_GROUP: + switch (object_property) { + case PROP_PRESENT_VALUE: + return true; + default: + break; + } + break; + case OBJECT_CHANNEL: + switch (object_property) { + case PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES: + return true; + default: + break; + } + break; + default: + break; + } + if ((object_property >= PROP_PROPRIETARY_RANGE_MIN) && + (object_property <= PROP_PROPRIETARY_RANGE_MAX)) { + /* all proprietary properties could be a BACnetARRAY */ + return true; + } + + return property_list_member(Properties_BACnetARRAY, object_property); +} + +/* standard properties that are BACnetLIST */ +static const int Properties_BACnetLIST[] = { + /* unordered list of properties */ + PROP_DATE_LIST, + PROP_VT_CLASSES_SUPPORTED, + PROP_ACTIVE_VT_SESSIONS, + PROP_TIME_SYNCHRONIZATION_RECIPIENTS, + PROP_DEVICE_ADDRESS_BINDING, + PROP_ACTIVE_COV_SUBSCRIPTIONS, + PROP_RESTART_NOTIFICATION_RECIPIENTS, + PROP_UTC_TIME_SYNCHRONIZATION_RECIPIENTS, + PROP_ACTIVE_COV_MULTIPLE_SUBSCRIPTIONS, + PROP_LIST_OF_GROUP_MEMBERS, + PROP_ACCEPTED_MODES, + PROP_LIFE_SAFETY_ALARM_VALUES, + PROP_ALARM_VALUES, + PROP_FAULT_VALUES, + PROP_MEMBER_OF, + PROP_ZONE_MEMBERS, + PROP_RECIPIENT_LIST, + PROP_LOG_BUFFER, + PROP_MASKED_ALARM_VALUES, + PROP_FAILED_ATTEMPT_EVENTS, + PROP_ACCESS_ALARM_EVENTS, + PROP_ACCESS_TRANSACTION_EVENTS, + PROP_CREDENTIALS_IN_ZONE, + PROP_ENTRY_POINTS, + PROP_EXIT_POINTS, + PROP_MEMBERS, + PROP_CREDENTIALS, + PROP_REASON_FOR_DISABLE, + PROP_AUTHORIZATION_EXEMPTIONS, + PROP_COVU_RECIPIENTS, + PROP_SUBSCRIBED_RECIPIENTS, + PROP_BBMD_BROADCAST_DISTRIBUTION_TABLE, + PROP_BBMD_FOREIGN_DEVICE_TABLE, + PROP_MANUAL_SLAVE_ADDRESS_BINDING, + PROP_SLAVE_ADDRESS_BINDING, + PROP_VIRTUAL_MAC_ADDRESS_TABLE, + PROP_ROUTING_TABLE, + PROP_LANDING_CALLS, + PROP_FAULT_SIGNALS, + -1 +}; + +/** + * Returns the list of BACnetLIST properties of known standard objects. + * + * @param object_type - enumerated BACNET_OBJECT_TYPE + * @return returns a pointer to a '-1' terminated array of + * type 'int' that contain BACnet object properties for the given object + * type. + */ +const int *property_list_bacnet_list(void) +{ + return Properties_BACnetLIST; +} + +/** + * @brief Determine if the object property is a BACnetLIST property + * @param object_type - object-type to be checked + * @param object_property - object-property to be checked + * @return true if the property is a BACnetLIST property + */ +bool property_list_bacnet_list_member( + BACNET_OBJECT_TYPE object_type, BACNET_PROPERTY_ID object_property) +{ + /* exceptions where property is an BACnetLIST only in specific objects */ + switch (object_type) { + case OBJECT_GROUP: + switch (object_property) { + case PROP_PRESENT_VALUE: + return true; + default: + break; + } + break; + case OBJECT_SCHEDULE: + case OBJECT_TIMER: + switch (object_property) { + case PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES: + return true; + default: + break; + } + break; + default: + break; + } + if ((object_property >= PROP_PROPRIETARY_RANGE_MIN) && + (object_property <= PROP_PROPRIETARY_RANGE_MAX)) { + /* all proprietary properties could be a BACnetLIST */ + return true; + } + + return property_list_member(Properties_BACnetLIST, object_property); +} diff --git a/src/bacnet/proplist.h b/src/bacnet/proplist.h index 6af4273f..b9a9c2f8 100644 --- a/src/bacnet/proplist.h +++ b/src/bacnet/proplist.h @@ -52,6 +52,17 @@ int property_list_common_encode( BACNET_STACK_EXPORT bool property_list_common(BACNET_PROPERTY_ID property); +BACNET_STACK_EXPORT +const int *property_list_bacnet_array(void); +BACNET_STACK_EXPORT +bool property_list_bacnet_array_member( + BACNET_OBJECT_TYPE object_type, BACNET_PROPERTY_ID object_property); +BACNET_STACK_EXPORT +const int *property_list_bacnet_list(void); +BACNET_STACK_EXPORT +bool property_list_bacnet_list_member( + BACNET_OBJECT_TYPE object_type, BACNET_PROPERTY_ID object_property); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/test/bacnet/basic/object/access_rights/CMakeLists.txt b/test/bacnet/basic/object/access_rights/CMakeLists.txt index 45a82717..2d002fd0 100644 --- a/test/bacnet/basic/object/access_rights/CMakeLists.txt +++ b/test/bacnet/basic/object/access_rights/CMakeLists.txt @@ -22,7 +22,6 @@ set(ZTST_DIR "${TST_DIR}/ztest/src") add_compile_definitions( BIG_ENDIAN=0 - BACNET_PROPERTY_ARRAY_LISTS=1 CONFIG_ZTEST=1 ) diff --git a/test/bacnet/basic/object/bitstring_value/CMakeLists.txt b/test/bacnet/basic/object/bitstring_value/CMakeLists.txt index 09723d66..a714d87a 100644 --- a/test/bacnet/basic/object/bitstring_value/CMakeLists.txt +++ b/test/bacnet/basic/object/bitstring_value/CMakeLists.txt @@ -22,7 +22,6 @@ set(ZTST_DIR "${TST_DIR}/ztest/src") add_compile_definitions( BIG_ENDIAN=0 CONFIG_ZTEST=1 - BACNET_PROPERTY_ARRAY_LISTS=1 ) include_directories( diff --git a/test/bacnet/basic/object/device/CMakeLists.txt b/test/bacnet/basic/object/device/CMakeLists.txt index 20939800..a92bf8d8 100644 --- a/test/bacnet/basic/object/device/CMakeLists.txt +++ b/test/bacnet/basic/object/device/CMakeLists.txt @@ -23,7 +23,6 @@ set(ZTST_DIR "${TST_DIR}/ztest/src") add_compile_definitions( BIG_ENDIAN=0 CONFIG_ZTEST=1 - BACNET_PROPERTY_ARRAY_LISTS=1 ) include_directories( diff --git a/test/bacnet/basic/object/structured_view/CMakeLists.txt b/test/bacnet/basic/object/structured_view/CMakeLists.txt index 543eb026..a61bf2bb 100644 --- a/test/bacnet/basic/object/structured_view/CMakeLists.txt +++ b/test/bacnet/basic/object/structured_view/CMakeLists.txt @@ -23,7 +23,6 @@ set(ZTST_DIR "${TST_DIR}/ztest/src") add_compile_definitions( BIG_ENDIAN=0 CONFIG_ZTEST=1 - BACNET_PROPERTY_ARRAY_LISTS=1 ) include_directories( diff --git a/test/bacnet/property/CMakeLists.txt b/test/bacnet/property/CMakeLists.txt index 550f311e..d3e64964 100644 --- a/test/bacnet/property/CMakeLists.txt +++ b/test/bacnet/property/CMakeLists.txt @@ -24,7 +24,6 @@ add_compile_definitions( BIG_ENDIAN=0 CONFIG_ZTEST=1 BACNET_PROPERTY_LISTS=1 - BACNET_PROPERTY_ARRAY_LISTS=1 ) include_directories(