diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a8915ff..5ac92f02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,9 @@ The git repositories are hosted at the following sites: ### Added +* Added CreateObject and DeleteObject for basic Accumulator objects and + WriteProperty handling for object-name, scale, out-of-service, units, + and max-pres-value. (#1234) * Added text and parser for BACnet ReinitializeDevice service states. (#1228) * Added Device Management-Backup and Restore-B to example object/device.c and basic/server/bacnet-device.c so that when ReinitializeDevice STARTBACKUP diff --git a/src/bacnet/basic/object/acc.c b/src/bacnet/basic/object/acc.c index 6cd67d89..6ca19c4d 100644 --- a/src/bacnet/basic/object/acc.c +++ b/src/bacnet/basic/object/acc.c @@ -8,24 +8,33 @@ #include #include #include +#include /* BACnet Stack defines - first */ #include "bacnet/bacdef.h" /* BACnet Stack API */ #include "bacnet/bacdcode.h" #include "bacnet/proplist.h" +#include "bacnet/rp.h" +#include "bacnet/wp.h" +#include "bacnet/basic/sys/keylist.h" #include "bacnet/basic/object/acc.h" -#ifndef MAX_ACCUMULATORS -#define MAX_ACCUMULATORS 64 -#endif +/* Key List for storing the object data sorted by instance number */ +static OS_Keylist Object_List = NULL; +/* common object type */ +static const BACNET_OBJECT_TYPE Object_Type = OBJECT_ACCUMULATOR; struct object_data { BACNET_UNSIGNED_INTEGER Present_Value; + BACNET_UNSIGNED_INTEGER Max_Pres_Value; + BACNET_CHARACTER_STRING Object_Name; + const char *Description; + BACNET_ENGINEERING_UNITS Units; int32_t Scale; + bool Out_Of_Service : 1; + void *Context; }; -static struct object_data Object_List[MAX_ACCUMULATORS]; - /* These three arrays are used by the ReadPropertyMultiple handler */ static const int32_t Properties_Required[] = { /* unordered list of required properties */ @@ -50,7 +59,16 @@ static const int32_t Properties_Proprietary[] = { -1 }; which is a BACnetARRAY of property identifiers, one property identifier for each property within this object that is always writable. */ -static const int32_t Writable_Properties[] = { PROP_PRESENT_VALUE, -1 }; +static const int32_t Writable_Properties[] = { + /* unordered list of writable properties */ + PROP_OBJECT_NAME, + PROP_PRESENT_VALUE, + PROP_OUT_OF_SERVICE, + PROP_SCALE, + PROP_UNITS, + PROP_MAX_PRES_VALUE, + -1 +}; /** * Returns the list of required, optional, and proprietary properties. @@ -95,6 +113,16 @@ void Accumulator_Writable_Property_List( } } +/** + * @brief Gets an object from the list using an instance number as the key + * @param object_instance - object-instance number of the object + * @return object found in the list, or NULL if not found + */ +static struct object_data *Object_Data(uint32_t object_instance) +{ + return Keylist_Data(Object_List, object_instance); +} + /** * Determines if a given Accumulator instance is valid * @@ -104,11 +132,9 @@ void Accumulator_Writable_Property_List( */ bool Accumulator_Valid_Instance(uint32_t object_instance) { - if (object_instance < MAX_ACCUMULATORS) { - return true; - } + struct object_data *pObject = Object_Data(object_instance); - return false; + return (pObject != NULL); } /** @@ -118,7 +144,7 @@ bool Accumulator_Valid_Instance(uint32_t object_instance) */ unsigned Accumulator_Count(void) { - return MAX_ACCUMULATORS; + return Keylist_Count(Object_List); } /** @@ -131,7 +157,11 @@ unsigned Accumulator_Count(void) */ uint32_t Accumulator_Index_To_Instance(unsigned index) { - return index; + KEY key = UINT32_MAX; + + Keylist_Index_Key(Object_List, index, &key); + + return key; } /** @@ -145,13 +175,7 @@ uint32_t Accumulator_Index_To_Instance(unsigned index) */ unsigned Accumulator_Instance_To_Index(uint32_t object_instance) { - unsigned index = MAX_ACCUMULATORS; - - if (object_instance < MAX_ACCUMULATORS) { - index = object_instance; - } - - return index; + return Keylist_Index(Object_List, object_instance); } /** @@ -167,14 +191,34 @@ unsigned Accumulator_Instance_To_Index(uint32_t object_instance) bool Accumulator_Object_Name( uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) { - char text[32]; bool status = false; + struct object_data *pObject = Object_Data(object_instance); - if (object_instance < MAX_ACCUMULATORS) { - snprintf( - text, sizeof(text), "ACCUMULATOR-%lu", - (unsigned long)object_instance); - status = characterstring_init_ansi(object_name, text); + if (pObject) { + status = characterstring_copy(object_name, &pObject->Object_Name); + } + + return status; +} + +/** + * For a given object instance-number, sets the object-name from + * a characterstring. Note that the object name must be unique + * within this device. + * + * @param object_instance - object-instance number of the object + * @param object_name - holds the object-name to be set + * + * @return true if object-name was set + */ +bool Accumulator_Object_Name_Set( + uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) +{ + bool status = false; + struct object_data *pObject = Object_Data(object_instance); + + if (pObject) { + status = characterstring_copy(&pObject->Object_Name, object_name); } return status; @@ -190,9 +234,10 @@ bool Accumulator_Object_Name( BACNET_UNSIGNED_INTEGER Accumulator_Present_Value(uint32_t object_instance) { BACNET_UNSIGNED_INTEGER value = 0; + struct object_data *pObject = Object_Data(object_instance); - if (object_instance < MAX_ACCUMULATORS) { - value = Object_List[object_instance].Present_Value; + if (pObject) { + value = pObject->Present_Value; } return value; @@ -210,9 +255,10 @@ bool Accumulator_Present_Value_Set( uint32_t object_instance, BACNET_UNSIGNED_INTEGER value) { bool status = false; + struct object_data *pObject = Object_Data(object_instance); - if (object_instance < MAX_ACCUMULATORS) { - Object_List[object_instance].Present_Value = value; + if (pObject) { + pObject->Present_Value = value; status = true; } @@ -229,14 +275,35 @@ bool Accumulator_Present_Value_Set( BACNET_ENGINEERING_UNITS Accumulator_Units(uint32_t object_instance) { BACNET_ENGINEERING_UNITS units = UNITS_NO_UNITS; + struct object_data *pObject = Object_Data(object_instance); - if (object_instance < MAX_ACCUMULATORS) { - units = UNITS_WATT_HOURS; + if (pObject) { + units = pObject->Units; } return units; } +/** + * For a given object instance-number, sets the units property value + * + * @param object_instance - object-instance number of the object + * @param units - BACNET_ENGINEERING_UNITS value + * @return true if the units is set successfully + */ +bool Accumulator_Units_Set(uint32_t instance, BACNET_ENGINEERING_UNITS units) +{ + bool status = false; + struct object_data *pObject = Object_Data(instance); + + if (pObject) { + pObject->Units = units; + status = true; + } + + return status; +} + /** * For a given object instance-number, returns the scale property value * @@ -251,9 +318,10 @@ BACNET_ENGINEERING_UNITS Accumulator_Units(uint32_t object_instance) int32_t Accumulator_Scale_Integer(uint32_t object_instance) { int32_t scale = 0; + struct object_data *pObject = Object_Data(object_instance); - if (object_instance < MAX_ACCUMULATORS) { - scale = Object_List[object_instance].Scale; + if (pObject) { + scale = pObject->Scale; } return scale; @@ -274,9 +342,10 @@ int32_t Accumulator_Scale_Integer(uint32_t object_instance) bool Accumulator_Scale_Integer_Set(uint32_t object_instance, int32_t scale) { bool status = false; + struct object_data *pObject = Object_Data(object_instance); - if (object_instance < MAX_ACCUMULATORS) { - Object_List[object_instance].Scale = scale; + if (pObject) { + pObject->Scale = scale; status = true; } @@ -284,27 +353,122 @@ bool Accumulator_Scale_Integer_Set(uint32_t object_instance, int32_t scale) } /** - * For a given object instance-number, returns the scale property value - * - * Option Datatype Indicated Value in Units - * float-scale REAL Present_Value x Scale - * integer-scale INTEGER Present_Value x 10 Scale + * For a given object instance-number, returns the max-present-value * * @param object_instance - object-instance number of the object - * - * @return scale property integer value + * @return max-present-value of the object */ BACNET_UNSIGNED_INTEGER Accumulator_Max_Pres_Value(uint32_t object_instance) { BACNET_UNSIGNED_INTEGER max_value = 0; + struct object_data *pObject = Object_Data(object_instance); - if (object_instance < MAX_ACCUMULATORS) { - max_value = BACNET_UNSIGNED_INTEGER_MAX; + if (pObject) { + max_value = pObject->Max_Pres_Value; } return max_value; } +/** + * For a given object instance-number, sets the max-present-value + * + * @param object_instance - object-instance number of the object + * @param value - BACNET_UNSIGNED_INTEGER value + * + * @return true if values are within range and max-present-value is set. + */ +bool Accumulator_Max_Pres_Value_Set( + uint32_t object_instance, BACNET_UNSIGNED_INTEGER value) +{ + bool status = false; + struct object_data *pObject = Object_Data(object_instance); + + if (pObject) { + pObject->Max_Pres_Value = value; + status = true; + } + + return status; +} + +/** + * For a given object instance-number, returns the description + * + * @param object_instance - object-instance number of the object + * + * @return description text or NULL if not found + */ +const char *Accumulator_Description(uint32_t instance) +{ + const char *name = NULL; + struct object_data *pObject = Object_Data(instance); + + if (pObject) { + name = pObject->Description; + } + + return name; +} + +/** + * For a given object instance-number, sets the description + * + * @param object_instance - object-instance number of the object + * @param new_name - holds the description to be set + */ +bool Accumulator_Description_Set(uint32_t instance, const char *new_name) +{ + bool status = false; + struct object_data *pObject = Object_Data(instance); + + if (pObject) { + pObject->Description = new_name; + status = true; + } + + return status; +} + +/** + * For a given object instance-number, returns the Out_Of_Service property value + * + * @param object_instance - object-instance number of the object + * + * @return Out_Of_Service property value + */ +bool Accumulator_Out_Of_Service(uint32_t object_instance) +{ + bool value = false; + struct object_data *pObject = Object_Data(object_instance); + + if (pObject) { + value = pObject->Out_Of_Service; + } + + return value; +} + +/** + * For a given object instance-number, sets the Out_Of_Service property value + * + * @param object_instance - object-instance number of the object + * @param value - Out_Of_Service property value to be set + * @return true if the value is set successfully + */ +bool Accumulator_Out_Of_Service_Set(uint32_t object_instance, bool value) +{ + bool status = false; + struct object_data *pObject = Object_Data(object_instance); + + if (pObject) { + pObject->Out_Of_Service = value; + status = true; + } + + return status; +} + /** * ReadProperty handler for this object. For the given ReadProperty * data, the application_data is loaded or the error flags are set. @@ -330,17 +494,15 @@ int Accumulator_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) switch ((int)rpdata->object_property) { case PROP_OBJECT_IDENTIFIER: apdu_len = encode_application_object_id( - &apdu[0], OBJECT_ACCUMULATOR, rpdata->object_instance); + &apdu[0], Object_Type, rpdata->object_instance); break; case PROP_OBJECT_NAME: - case PROP_DESCRIPTION: Accumulator_Object_Name(rpdata->object_instance, &char_string); apdu_len = encode_application_character_string(&apdu[0], &char_string); break; case PROP_OBJECT_TYPE: - apdu_len = - encode_application_enumerated(&apdu[0], OBJECT_ACCUMULATOR); + apdu_len = encode_application_enumerated(&apdu[0], Object_Type); break; case PROP_PRESENT_VALUE: apdu_len = encode_application_unsigned( @@ -361,7 +523,9 @@ int Accumulator_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); - bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + bitstring_set_bit( + &bit_string, STATUS_FLAG_OUT_OF_SERVICE, + Accumulator_Out_Of_Service(rpdata->object_instance)); apdu_len = encode_application_bitstring(&apdu[0], &bit_string); break; case PROP_EVENT_STATE: @@ -369,12 +533,19 @@ int Accumulator_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); break; case PROP_OUT_OF_SERVICE: - apdu_len = encode_application_boolean(&apdu[0], false); + apdu_len = encode_application_boolean( + &apdu[0], Accumulator_Out_Of_Service(rpdata->object_instance)); break; case PROP_UNITS: apdu_len = encode_application_enumerated( &apdu[0], Accumulator_Units(rpdata->object_instance)); break; + case PROP_DESCRIPTION: + characterstring_init_ansi( + &char_string, Accumulator_Description(rpdata->object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; default: rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; @@ -397,11 +568,13 @@ int Accumulator_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) bool Accumulator_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) { int len = 0; + bool status = false; BACNET_APPLICATION_DATA_VALUE value = { 0 }; /* decode the some of the request */ - len = bacapp_decode_application_data( - wp_data->application_data, wp_data->application_data_len, &value); + len = bacapp_decode_known_array_property( + wp_data->application_data, wp_data->application_data_len, &value, + wp_data->object_type, wp_data->object_property, wp_data->array_index); /* FIXME: len < application_data_len: more data? */ if (len < 0) { /* error while decoding - a value larger than we can handle */ @@ -411,7 +584,9 @@ bool Accumulator_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) } switch ((int)wp_data->object_property) { case PROP_PRESENT_VALUE: - if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + status = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT); + if (status) { if (value.type.Unsigned_Int <= Accumulator_Max_Pres_Value(wp_data->object_instance)) { Accumulator_Present_Value_Set( @@ -419,12 +594,62 @@ bool Accumulator_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) } else { wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - return false; + status = false; } - } else { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; + } + break; + case PROP_OBJECT_NAME: + status = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_CHARACTER_STRING); + if (status) { + Accumulator_Object_Name_Set( + wp_data->object_instance, &value.type.Character_String); + } + break; + case PROP_SCALE: + status = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_SCALE); + if (status) { + if (value.type.Scale.float_scale) { + /* we only support integer scale, so reject if float scale + * is used */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + status = false; + } else { + Accumulator_Scale_Integer_Set( + wp_data->object_instance, value.type.Signed_Int); + } + } + break; + case PROP_OUT_OF_SERVICE: + status = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_BOOLEAN); + if (status) { + Accumulator_Out_Of_Service_Set( + wp_data->object_instance, value.type.Boolean); + } + break; + case PROP_UNITS: + status = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED); + if (status) { + if (value.type.Enumerated <= UINT16_MAX) { + Accumulator_Units_Set( + wp_data->object_instance, value.type.Enumerated); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + status = false; + } + } + break; + case PROP_MAX_PRES_VALUE: + status = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT); + if (status) { + Accumulator_Max_Pres_Value_Set( + wp_data->object_instance, value.type.Unsigned_Int); } break; default: @@ -440,20 +665,119 @@ bool Accumulator_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) break; } - return false; + return status; } /** - * Initializes the Accumulator object data + * @brief Set the context used with a specific object instance + * @param object_instance [in] BACnet object instance number + * @param context [in] pointer to the context + */ +void *Accumulator_Context_Get(uint32_t object_instance) +{ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + return pObject->Context; + } + + return NULL; +} + +/** + * @brief Set the context used with a specific object instance + * @param object_instance [in] BACnet object instance number + * @param context [in] pointer to the context + */ +void Accumulator_Context_Set(uint32_t object_instance, void *context) +{ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + pObject->Context = context; + } +} + +/** + * @brief Creates a CharacterString Value object + * @param object_instance - object-instance number of the object + * @return the object-instance that was created, or BACNET_MAX_INSTANCE + */ +uint32_t Accumulator_Create(uint32_t object_instance) +{ + struct object_data *pObject = NULL; + char text[32] = ""; + int index; + + if (!Object_List) { + Object_List = Keylist_Create(); + } + if (object_instance > BACNET_MAX_INSTANCE) { + return BACNET_MAX_INSTANCE; + } else if (object_instance == BACNET_MAX_INSTANCE) { + /* wildcard instance */ + /* the Object_Identifier property of the newly created object + shall be initialized to a value that is unique within the + responding BACnet-user device. The method used to generate + the object identifier is a local matter.*/ + object_instance = Keylist_Next_Empty_Key(Object_List, 1); + } + pObject = Keylist_Data(Object_List, object_instance); + if (!pObject) { + pObject = calloc(1, sizeof(struct object_data)); + if (pObject) { + /* add to list */ + index = Keylist_Data_Add(Object_List, object_instance, pObject); + if (index < 0) { + free(pObject); + return BACNET_MAX_INSTANCE; + } + characterstring_init_ansi( + &pObject->Object_Name, + bacnet_snprintf_to_ascii( + text, sizeof(text), "ACCUMULATOR-%lu", + (unsigned long)object_instance)); + pObject->Description = ""; + pObject->Present_Value = 0; + pObject->Units = UNITS_WATT_HOURS; + pObject->Scale = 1; + pObject->Max_Pres_Value = BACNET_UNSIGNED_INTEGER_MAX; + /* Out_Of_Service is used to simulate a fault condition, + so set to false by default */ + pObject->Out_Of_Service = false; + } else { + return BACNET_MAX_INSTANCE; + } + } + + return object_instance; +} + +/** + * @brief Delete an object and its data from the object list + * @param object_instance - object-instance number of the object + * @return true if the object is deleted + */ +bool Accumulator_Delete(uint32_t object_instance) +{ + bool status = false; + struct object_data *pObject = NULL; + + pObject = Keylist_Data_Delete(Object_List, object_instance); + if (pObject) { + free(pObject); + status = true; + } + + return status; +} + +/** + * Initialize the character string values. */ void Accumulator_Init(void) { - BACNET_UNSIGNED_INTEGER unsigned_value = 1; - unsigned i = 0; - - for (i = 0; i < MAX_ACCUMULATORS; i++) { - Accumulator_Scale_Integer_Set(i, i + 1); - Accumulator_Present_Value_Set(i, unsigned_value); - unsigned_value |= (unsigned_value << 1); - } + /* nothing to do */ } diff --git a/src/bacnet/basic/object/acc.h b/src/bacnet/basic/object/acc.h index ff0259e2..6bdaa7d7 100644 --- a/src/bacnet/basic/object/acc.h +++ b/src/bacnet/basic/object/acc.h @@ -29,6 +29,15 @@ BACNET_STACK_EXPORT void Accumulator_Writable_Property_List( uint32_t object_instance, const int32_t **properties); +BACNET_STACK_EXPORT +void Accumulator_Proprietary_Property_List_Set(const int32_t *pProprietary); +BACNET_STACK_EXPORT +void Accumulator_Read_Property_Proprietary_Callback_Set( + read_property_function cb); +BACNET_STACK_EXPORT +void Accumulator_Write_Property_Proprietary_Callback_Set( + write_property_function cb); + BACNET_STACK_EXPORT bool Accumulator_Valid_Instance(uint32_t object_instance); BACNET_STACK_EXPORT @@ -44,15 +53,16 @@ BACNET_STACK_EXPORT char *Accumulator_Name(uint32_t object_instance); BACNET_STACK_EXPORT bool Accumulator_Name_Set(uint32_t object_instance, char *new_name); - -BACNET_STACK_EXPORT -char *Accumulator_Description(uint32_t instance); -BACNET_STACK_EXPORT -bool Accumulator_Description_Set(uint32_t instance, const char *new_name); - BACNET_STACK_EXPORT bool Accumulator_Object_Name( uint32_t object_instance, BACNET_CHARACTER_STRING *object_name); +bool Accumulator_Object_Name_Set( + uint32_t object_instance, BACNET_CHARACTER_STRING *object_name); + +BACNET_STACK_EXPORT +const char *Accumulator_Description(uint32_t instance); +BACNET_STACK_EXPORT +bool Accumulator_Description_Set(uint32_t instance, const char *new_name); BACNET_STACK_EXPORT bool Accumulator_Units_Set(uint32_t instance, BACNET_ENGINEERING_UNITS units); @@ -81,6 +91,21 @@ int32_t Accumulator_Scale_Integer(uint32_t object_instance); BACNET_STACK_EXPORT bool Accumulator_Scale_Integer_Set(uint32_t object_instance, int32_t); +BACNET_STACK_EXPORT +bool Accumulator_Out_Of_Service(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Accumulator_Out_Of_Service_Set(uint32_t object_instance, bool value); + +BACNET_STACK_EXPORT +uint32_t Accumulator_Create(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Accumulator_Delete(uint32_t object_instance); + +BACNET_STACK_EXPORT +void *Accumulator_Context_Get(uint32_t object_instance); +BACNET_STACK_EXPORT +void Accumulator_Context_Set(uint32_t object_instance, void *context); + BACNET_STACK_EXPORT void Accumulator_Init(void); diff --git a/test/bacnet/basic/object/acc/CMakeLists.txt b/test/bacnet/basic/object/acc/CMakeLists.txt index 373a3d07..1b7f83f3 100644 --- a/test/bacnet/basic/object/acc/CMakeLists.txt +++ b/test/bacnet/basic/object/acc/CMakeLists.txt @@ -27,6 +27,7 @@ add_compile_definitions( include_directories( ${SRC_DIR} + ${TST_DIR}/bacnet/basic/object/test ${TST_DIR}/ztest/include ) @@ -47,6 +48,9 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/bacstr.c ${SRC_DIR}/bacnet/bactext.c ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + ${SRC_DIR}/bacnet/basic/sys/keylist.c ${SRC_DIR}/bacnet/datetime.c ${SRC_DIR}/bacnet/indtext.c ${SRC_DIR}/bacnet/hostnport.c @@ -55,7 +59,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/shed_level.c ${SRC_DIR}/bacnet/timer_value.c ${SRC_DIR}/bacnet/timestamp.c - ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/wp.c ${SRC_DIR}/bacnet/weeklyschedule.c ${SRC_DIR}/bacnet/bactimevalue.c ${SRC_DIR}/bacnet/dailyschedule.c @@ -65,6 +69,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/secure_connect.c # Test and test library files ./src/main.c + ${TST_DIR}/bacnet/basic/object/test/property_test.c ${ZTST_DIR}/ztest_mock.c ${ZTST_DIR}/ztest.c ) diff --git a/test/bacnet/basic/object/acc/src/main.c b/test/bacnet/basic/object/acc/src/main.c index d6af89a9..478579b4 100644 --- a/test/bacnet/basic/object/acc/src/main.c +++ b/test/bacnet/basic/object/acc/src/main.c @@ -8,6 +8,7 @@ #include #include #include +#include /** * @addtogroup bacnet_tests @@ -23,55 +24,77 @@ ZTEST(acc_tests, test_Accumulator) static void test_Accumulator(void) #endif { - uint8_t apdu[MAX_APDU] = { 0 }; + const uint32_t instance = 123; + unsigned count = 0; + unsigned index = 0; int len = 0; int test_len = 0; + uint32_t test_instance = 0; + uint8_t apdu[MAX_APDU] = { 0 }; BACNET_READ_PROPERTY_DATA rpdata = { 0 }; BACNET_APPLICATION_DATA_VALUE value = { 0 }; - const int32_t *required_property = NULL; BACNET_UNSIGNED_INTEGER unsigned_value = 1; + bool status = false; + const int32_t *writable_properties; + const int32_t skip_fail_property_list[] = { -1 }; + char *sample_context = "context"; Accumulator_Init(); - rpdata.application_data = &apdu[0]; - rpdata.application_data_len = sizeof(apdu); - rpdata.object_type = OBJECT_ACCUMULATOR; - rpdata.object_instance = Accumulator_Index_To_Instance(0); - - Accumulator_Property_Lists(&required_property, NULL, NULL); - while ((*required_property) >= 0) { - rpdata.object_property = *required_property; - rpdata.array_index = BACNET_ARRAY_ALL; - len = Accumulator_Read_Property(&rpdata); - zassert_true(len >= 0, NULL); - if (len >= 0) { - if (IS_CONTEXT_SPECIFIC(rpdata.application_data[0])) { - test_len = bacapp_decode_context_data( - rpdata.application_data, len, &value, - rpdata.object_property); - } else { - test_len = bacapp_decode_application_data( - rpdata.application_data, len, &value); - } - if (len != test_len) { - printf( - "property '%s': failed to decode!\n", - bactext_property_name(rpdata.object_property)); - } - zassert_equal(len, test_len, NULL); - } - required_property++; - } + test_instance = Accumulator_Create(BACNET_MAX_INSTANCE + 1); + zassert_equal(test_instance, BACNET_MAX_INSTANCE, NULL); + test_instance = Accumulator_Create(BACNET_MAX_INSTANCE); + zassert_not_equal(test_instance, BACNET_MAX_INSTANCE, NULL); + status = Accumulator_Delete(test_instance); + zassert_true(status, NULL); + test_instance = Accumulator_Create(instance); + zassert_equal(test_instance, instance, NULL); + status = Accumulator_Valid_Instance(instance); + zassert_true(status, NULL); + status = Accumulator_Valid_Instance(instance - 1); + zassert_false(status, NULL); + index = Accumulator_Instance_To_Index(instance); + zassert_equal(index, 0, NULL); + test_instance = Accumulator_Index_To_Instance(index); + zassert_equal(instance, test_instance, NULL); + count = Accumulator_Count(); + zassert_true(count > 0, NULL); + /* perform a general test for RP/WP */ + bacnet_object_properties_read_write_test( + OBJECT_ACCUMULATOR, instance, Accumulator_Property_Lists, + Accumulator_Read_Property, Accumulator_Write_Property, + skip_fail_property_list); /* test 1-bit to 64-bit encode/decode of present-value */ + rpdata.object_type = OBJECT_ACCUMULATOR; + rpdata.object_instance = instance; rpdata.object_property = PROP_PRESENT_VALUE; + rpdata.array_index = BACNET_ARRAY_ALL; + rpdata.error_class = ERROR_CLASS_PROPERTY; + rpdata.error_code = ERROR_CODE_SUCCESS; + rpdata.application_data = apdu; + rpdata.application_data_len = sizeof(apdu); while (unsigned_value != BACNET_UNSIGNED_INTEGER_MAX) { - Accumulator_Present_Value_Set(0, unsigned_value); + Accumulator_Present_Value_Set(instance, unsigned_value); len = Accumulator_Read_Property(&rpdata); zassert_not_equal(len, 0, NULL); test_len = bacapp_decode_application_data( rpdata.application_data, len, &value); zassert_equal(len, test_len, NULL); + zassert_equal(rpdata.error_code, ERROR_CODE_SUCCESS, NULL); + zassert_equal(value.tag, BACNET_APPLICATION_TAG_UNSIGNED_INT, NULL); + zassert_equal(value.type.Unsigned_Int, unsigned_value, NULL); unsigned_value |= (unsigned_value << 1); } + status = Accumulator_Description_Set(instance, "Test Accumulator"); + zassert_true(status, NULL); + Accumulator_Writable_Property_List(instance, &writable_properties); + zassert_not_null(writable_properties, NULL); + /* context API */ + Accumulator_Context_Set(instance, sample_context); + zassert_true(sample_context == Accumulator_Context_Get(instance), NULL); + zassert_true(NULL == Accumulator_Context_Get(instance + 1), NULL); + Accumulator_Delete(instance); + status = Accumulator_Valid_Instance(instance); + zassert_false(status, NULL); return; }