diff --git a/CMakeLists.txt b/CMakeLists.txt index 95e82ef1..59c83fb9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,9 +79,21 @@ if(BACNET_STACK_DEPRECATED_DISABLE) add_definitions(-DBACNET_STACK_DEPRECATED_DISABLE) endif() -set(CMAKE_CXX_FLAGS "-Wall -Wextra") -set(CMAKE_CXX_FLAGS_DEBUG "-g") -set(CMAKE_CXX_FLAGS_RELEASE "-O3") +if (CMAKE_C_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID MATCHES "AppleClang" OR CMAKE_C_COMPILER_ID MATCHES "GNU") + add_compile_options(-Wall -Wextra -pedantic) + add_compile_options(-Wfloat-equal -Wconversion -Wparentheses) + add_compile_options(-Wunused-value -Wreturn-type -Wswitch-default) + add_compile_options(-Wuninitialized -Winit-self) + add_compile_options(-Wno-sign-conversion -Wno-conversion) + add_compile_options(-Wno-sign-compare -Wno-long-long) + add_compile_options(-Wno-implicit-fallthrough -Wno-attributes) + add_compile_options(-Wunused-variable -Wunused-function) + add_compile_options(-Wunused-parameter) +endif() + +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "AppleClang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") + add_compile_options(-Wall -Wextra -pedantic -g -O3) +endif() # # library diff --git a/src/bacnet/basic/object/device.c b/src/bacnet/basic/object/device.c index b1ccdf65..8091cbd1 100644 --- a/src/bacnet/basic/object/device.c +++ b/src/bacnet/basic/object/device.c @@ -207,7 +207,7 @@ static object_functions_t My_Object_Table[] = { NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */, NULL /* Remove_List_Element */, - NULL /* Create */, NULL /* Delete */, NULL /* Timer */ }, + Life_Safety_Point_Create, Life_Safety_Point_Delete, NULL /* Timer */ }, { OBJECT_LOAD_CONTROL, Load_Control_Init, Load_Control_Count, Load_Control_Index_To_Instance, Load_Control_Valid_Instance, Load_Control_Object_Name, Load_Control_Read_Property, diff --git a/src/bacnet/basic/object/lsp.c b/src/bacnet/basic/object/lsp.c index e4efbc12..a29fa685 100644 --- a/src/bacnet/basic/object/lsp.c +++ b/src/bacnet/basic/object/lsp.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "bacnet/bacdef.h" #include "bacnet/bacdcode.h" #include "bacnet/bacenum.h" @@ -37,22 +38,23 @@ #include "bacnet/wp.h" #include "bacnet/basic/object/lsp.h" #include "bacnet/basic/services.h" +#include "bacnet/basic/sys/keylist.h" #include "bacnet/proplist.h" -#ifndef MAX_LIFE_SAFETY_POINTS -#define MAX_LIFE_SAFETY_POINTS 7 -#endif - -/* Here are our stored levels.*/ -static BACNET_LIFE_SAFETY_MODE Life_Safety_Point_Mode[MAX_LIFE_SAFETY_POINTS]; -static BACNET_LIFE_SAFETY_STATE Life_Safety_Point_State[MAX_LIFE_SAFETY_POINTS]; -static BACNET_SILENCED_STATE - Life_Safety_Point_Silenced_State[MAX_LIFE_SAFETY_POINTS]; -static BACNET_LIFE_SAFETY_OPERATION - Life_Safety_Point_Operation[MAX_LIFE_SAFETY_POINTS]; -/* Writable out-of-service allows others to play with our Present Value */ -/* without changing the physical output */ -static bool Life_Safety_Point_Out_Of_Service[MAX_LIFE_SAFETY_POINTS]; +struct object_data { + bool Out_Of_Service : 1; + BACNET_LIFE_SAFETY_STATE Present_Value; + BACNET_LIFE_SAFETY_STATE Tracking_Value; + BACNET_LIFE_SAFETY_MODE Mode; + BACNET_SILENCED_STATE Silenced; + BACNET_LIFE_SAFETY_OPERATION Operation_Expected; + uint8_t Reliability; + const char *Object_Name; +}; +/* Key List for storing the object data sorted by instance number */ +static OS_Keylist Object_List; +/* common object type */ +static const BACNET_OBJECT_TYPE Object_Type = OBJECT_LIFE_SAFETY_POINT; /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Life_Safety_Point_Properties_Required[] = { @@ -62,8 +64,7 @@ static const int Life_Safety_Point_Properties_Required[] = { PROP_ACCEPTED_MODES, PROP_SILENCED, PROP_OPERATION_EXPECTED, -1 }; -static const int Life_Safety_Point_Properties_Optional[] = { PROP_DESCRIPTION, - -1 }; +static const int Life_Safety_Point_Properties_Optional[] = { -1 }; static const int Life_Safety_Point_Properties_Proprietary[] = { -1 }; @@ -94,97 +95,341 @@ void Life_Safety_Point_Property_Lists( return; } -void Life_Safety_Point_Init(void) -{ - static bool initialized = false; - unsigned i; - - if (!initialized) { - initialized = true; - - /* initialize all the analog output priority arrays to NULL */ - for (i = 0; i < MAX_LIFE_SAFETY_POINTS; i++) { - Life_Safety_Point_Mode[i] = LIFE_SAFETY_MODE_DEFAULT; - Life_Safety_Point_State[i] = LIFE_SAFETY_STATE_QUIET; - Life_Safety_Point_Silenced_State[i] = SILENCED_STATE_UNSILENCED; - Life_Safety_Point_Operation[i] = LIFE_SAFETY_OP_NONE; - } - } - - return; -} - -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need validate that the */ -/* given instance exists */ +/** + * @brief Determines if a given object instance is valid + * @param object_instance - object-instance number of the object + * @return true if the instance is valid, and false if not + */ bool Life_Safety_Point_Valid_Instance(uint32_t object_instance) { - if (object_instance < MAX_LIFE_SAFETY_POINTS) { + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { return true; } return false; } -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then count how many you have */ +/** + * @brief Determines the number of objects + * @return Number of objects + */ unsigned Life_Safety_Point_Count(void) { - return MAX_LIFE_SAFETY_POINTS; + return Keylist_Count(Object_List); } -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need to return the instance */ -/* that correlates to the correct index */ +/** + * @brief Determines if a given object instance is valid + * @param object_instance - object-instance number of the object + * @return true if the instance is BACNET_SILENCED_STATE Silenced; + + * @brief Determines the object instance-number for a given 0..N index + * where N is Life_Safety_Point_Count(). + * @param index - 0..N value + * @return object instance-number for the given index + */ uint32_t Life_Safety_Point_Index_To_Instance(unsigned index) { - return index; + return Keylist_Key(Object_List, index); } -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need to return the index */ -/* that correlates to the correct instance number */ +/** + * @brief For a given object instance-number, determines a 0..N index + * of where N is Life_Safety_Point_Count(). + * @param object_instance - object-instance number of the object + * @return index for the given instance-number + */ unsigned Life_Safety_Point_Instance_To_Index(uint32_t object_instance) { - unsigned index = MAX_LIFE_SAFETY_POINTS; - - if (object_instance < MAX_LIFE_SAFETY_POINTS) { - index = object_instance; - } - - return index; + return Keylist_Index(Object_List, object_instance); } -static BACNET_LIFE_SAFETY_STATE Life_Safety_Point_Present_Value( +/** + * @brief For a given object instance-number, determines the present-value + * @param object_instance - object-instance number of the object + * @return present-value of the object + */ +BACNET_LIFE_SAFETY_STATE Life_Safety_Point_Present_Value( uint32_t object_instance) { - BACNET_LIFE_SAFETY_STATE present_value = LIFE_SAFETY_STATE_QUIET; - unsigned index = 0; + BACNET_LIFE_SAFETY_STATE value = LIFE_SAFETY_STATE_QUIET; + struct object_data *pObject; - index = Life_Safety_Point_Instance_To_Index(object_instance); - if (index < MAX_LIFE_SAFETY_POINTS) { - present_value = Life_Safety_Point_State[index]; + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + value = pObject->Present_Value; } - return present_value; + return value; } -/* note: the object name must be unique within this device */ -bool Life_Safety_Point_Object_Name( - uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) +/** + * @brief For a given object instance-number, sets the present-value + * @param object_instance - object-instance number of the object + * @param value - floating point analog output relinquish-default value + * @return true if values are within range and relinquish-default value is set. + */ +bool Life_Safety_Point_Present_Value_Set( + uint32_t object_instance, BACNET_LIFE_SAFETY_STATE value) { - static char text_string[32] = ""; /* okay for single thread */ bool status = false; + struct object_data *pObject; - if (object_instance < MAX_LIFE_SAFETY_POINTS) { - sprintf(text_string, "LS POINT %u", object_instance); - status = characterstring_init_ansi(object_name, text_string); + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + pObject->Present_Value = value; + status = true; } return status; } -/* return apdu len, or BACNET_STATUS_ERROR on error */ +/** + * For a given object instance-number, loads the object-name into + * 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 retrieved + * + * @return true if object-name was retrieved + */ +bool Life_Safety_Point_Object_Name( + uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) +{ + bool status = false; + struct object_data *pObject; + char name_text[32]; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (pObject->Object_Name) { + status = + characterstring_init_ansi(object_name, pObject->Object_Name); + } else { + snprintf(name_text, sizeof(name_text), "LIFE-SAFETY-POINT-%u", + object_instance); + status = characterstring_init_ansi(object_name, name_text); + } + } + + return status; +} + +/** + * @brief For a given object instance-number, gets the property value + * @param object_instance - object-instance number of the object + * @return property value + */ +BACNET_SILENCED_STATE Life_Safety_Point_Silenced(uint32_t object_instance) +{ + BACNET_SILENCED_STATE value = SILENCED_STATE_UNSILENCED; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + value = pObject->Silenced; + } + + return value; +} + +/** + * @brief For a given object instance-number, sets the property value + * @param object_instance - object-instance number of the object + * @param value - enumerated value + * @return true if values are within range and property is set. + */ +bool Life_Safety_Point_Silenced_Set( + uint32_t object_instance, BACNET_SILENCED_STATE value) +{ + struct object_data *pObject; + bool status = false; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (value <= SILENCED_STATE_PROPRIETARY_MAX) { + pObject->Silenced = value; + status = true; + } + } + + return status; +} + +/** + * @brief For a given object instance-number, gets the property value + * @param object_instance - object-instance number of the object + * @return property value + */ +BACNET_LIFE_SAFETY_MODE Life_Safety_Point_Mode(uint32_t object_instance) +{ + BACNET_LIFE_SAFETY_MODE value = LIFE_SAFETY_MODE_OFF; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + value = pObject->Mode; + } + + return value; +} + +/** + * @brief For a given object instance-number, sets the property value + * @param object_instance - object-instance number of the object + * @param value - enumerated value + * @return true if values are within range and property is set. + */ +bool Life_Safety_Point_Mode_Set( + uint32_t object_instance, BACNET_LIFE_SAFETY_MODE value) +{ + struct object_data *pObject; + bool status = false; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (value <= LIFE_SAFETY_MODE_PROPRIETARY_MAX) { + pObject->Mode = value; + status = true; + } + } + + return status; +} + +/** + * @brief For a given object instance-number, gets the property value + * @param object_instance - object-instance number of the object + * @return property value + */ +BACNET_LIFE_SAFETY_OPERATION Life_Safety_Point_Operation_Expected( + uint32_t object_instance) +{ + BACNET_LIFE_SAFETY_OPERATION value = LIFE_SAFETY_OP_NONE; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + value = pObject->Operation_Expected; + } + + return value; +} + +/** + * @brief For a given object instance-number, sets the property value + * @param object_instance - object-instance number of the object + * @param value - enumerated value + * @return true if values are within range and property is set. + */ +bool Life_Safety_Point_Operation_Expected_Set( + uint32_t object_instance, BACNET_LIFE_SAFETY_OPERATION value) +{ + struct object_data *pObject; + bool status = false; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (value <= LIFE_SAFETY_OP_PROPRIETARY_MAX) { + pObject->Operation_Expected = value; + status = true; + } + } + + return status; +} + +/** + * @brief For a given object instance-number, returns the out-of-service + * status flag + * @param object_instance - object-instance number of the object + * @return out-of-service status flag + */ +bool Life_Safety_Point_Out_Of_Service(uint32_t object_instance) +{ + bool value = false; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + value = pObject->Out_Of_Service; + } + + return value; +} + +/** + * @brief For a given object instance-number, sets the out-of-service status + * flag + * @param object_instance - object-instance number of the object + * @param value - boolean out-of-service value + * @return true if the out-of-service status flag was set + */ +void Life_Safety_Point_Out_Of_Service_Set(uint32_t object_instance, bool value) +{ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (pObject->Out_Of_Service != value) { + pObject->Out_Of_Service = value; + } + } +} + +/** + * @brief For a given object instance-number, gets the reliability. + * @param object_instance - object-instance number of the object + * @return reliability value + */ +BACNET_RELIABILITY Life_Safety_Point_Reliability(uint32_t object_instance) +{ + BACNET_RELIABILITY reliability = RELIABILITY_NO_FAULT_DETECTED; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + reliability = (BACNET_RELIABILITY)pObject->Reliability; + } + + return reliability; +} + +/** + * @brief For a given object instance-number, sets the reliability + * @param object_instance - object-instance number of the object + * @param value - reliability enumerated value + * @return true if values are within range and property is set. + */ +bool Life_Safety_Point_Reliability_Set( + uint32_t object_instance, BACNET_RELIABILITY value) +{ + struct object_data *pObject; + bool status = false; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (value <= 255) { + pObject->Reliability = value; + status = true; + } + } + + return status; +} + +/** + * @brief ReadProperty handler for this object. For the given ReadProperty + * data, the application_data is loaded or the error flags are set. + * @param rpdata - BACNET_READ_PROPERTY_DATA data, including + * requested data and space for the reply, or error response. + * @return number of APDU bytes in the response, or + * BACNET_STATUS_ERROR on error. + */ int Life_Safety_Point_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) { int len = 0; @@ -195,7 +440,6 @@ int Life_Safety_Point_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) BACNET_LIFE_SAFETY_MODE mode = LIFE_SAFETY_MODE_DEFAULT; BACNET_SILENCED_STATE silenced_state = SILENCED_STATE_UNSILENCED; BACNET_LIFE_SAFETY_OPERATION operation = LIFE_SAFETY_OP_NONE; - unsigned object_index = 0; bool state = false; BACNET_RELIABILITY reliability = RELIABILITY_NO_FAULT_DETECTED; uint8_t *apdu = NULL; @@ -208,18 +452,16 @@ int Life_Safety_Point_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) switch (rpdata->object_property) { case PROP_OBJECT_IDENTIFIER: apdu_len = encode_application_object_id( - &apdu[0], OBJECT_LIFE_SAFETY_POINT, rpdata->object_instance); + &apdu[0], Object_Type, rpdata->object_instance); break; case PROP_OBJECT_NAME: - case PROP_DESCRIPTION: Life_Safety_Point_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_LIFE_SAFETY_POINT); + apdu_len = encode_application_enumerated(&apdu[0], Object_Type); break; case PROP_PRESENT_VALUE: present_value = @@ -235,9 +477,20 @@ int Life_Safety_Point_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) case PROP_STATUS_FLAGS: bitstring_init(&bit_string); bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); - bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + if (Life_Safety_Point_Reliability(rpdata->object_instance) == + RELIABILITY_NO_FAULT_DETECTED) { + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + } else { + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, true); + } bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); - bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + if (Life_Safety_Point_Out_Of_Service(rpdata->object_instance)) { + bitstring_set_bit( + &bit_string, STATUS_FLAG_OUT_OF_SERVICE, true); + } else { + bitstring_set_bit( + &bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + } apdu_len = encode_application_bitstring(&apdu[0], &bit_string); break; case PROP_EVENT_STATE: @@ -245,20 +498,16 @@ int Life_Safety_Point_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); break; case PROP_OUT_OF_SERVICE: - object_index = - Life_Safety_Point_Instance_To_Index(rpdata->object_instance); - state = Life_Safety_Point_Out_Of_Service[object_index]; + state = Life_Safety_Point_Out_Of_Service(rpdata->object_instance); apdu_len = encode_application_boolean(&apdu[0], state); break; case PROP_RELIABILITY: - /* see standard for details about this property */ - reliability = RELIABILITY_NO_FAULT_DETECTED; + reliability = + Life_Safety_Point_Reliability(rpdata->object_instance); apdu_len = encode_application_enumerated(&apdu[0], reliability); break; case PROP_MODE: - object_index = - Life_Safety_Point_Instance_To_Index(rpdata->object_instance); - mode = Life_Safety_Point_Mode[object_index]; + mode = Life_Safety_Point_Mode(rpdata->object_instance); apdu_len = encode_application_enumerated(&apdu[0], mode); break; case PROP_ACCEPTED_MODES: @@ -269,15 +518,12 @@ int Life_Safety_Point_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) } break; case PROP_SILENCED: - object_index = - Life_Safety_Point_Instance_To_Index(rpdata->object_instance); - silenced_state = Life_Safety_Point_Silenced_State[object_index]; + silenced_state = + Life_Safety_Point_Silenced(rpdata->object_instance); apdu_len = encode_application_enumerated(&apdu[0], silenced_state); break; case PROP_OPERATION_EXPECTED: - object_index = - Life_Safety_Point_Instance_To_Index(rpdata->object_instance); - operation = Life_Safety_Point_Operation[object_index]; + operation = Life_Safety_Point_Operation_Expected(rpdata->object_instance); apdu_len = encode_application_enumerated(&apdu[0], operation); break; default: @@ -300,7 +546,6 @@ int Life_Safety_Point_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) bool Life_Safety_Point_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) { bool status = false; /* return value */ - unsigned int object_index = 0; int len = 0; BACNET_APPLICATION_DATA_VALUE value; @@ -326,10 +571,23 @@ bool Life_Safety_Point_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED); if (status) { if (value.type.Enumerated <= MAX_LIFE_SAFETY_MODE) { - object_index = Life_Safety_Point_Instance_To_Index( - wp_data->object_instance); - Life_Safety_Point_Mode[object_index] = - (BACNET_LIFE_SAFETY_MODE)value.type.Enumerated; + Life_Safety_Point_Mode_Set(wp_data->object_instance, + (BACNET_LIFE_SAFETY_MODE)value.type.Enumerated); + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + case PROP_PRESENT_VALUE: + status = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED); + if (status) { + if (value.type.Enumerated <= UINT16_MAX) { + Life_Safety_Point_Present_Value_Set( + wp_data->object_instance, + (BACNET_LIFE_SAFETY_STATE)value.type.Enumerated); } else { status = false; wp_data->error_class = ERROR_CLASS_PROPERTY; @@ -341,25 +599,48 @@ bool Life_Safety_Point_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_BOOLEAN); if (status) { - object_index = Life_Safety_Point_Instance_To_Index( - wp_data->object_instance); - Life_Safety_Point_Out_Of_Service[object_index] = - value.type.Boolean; + Life_Safety_Point_Out_Of_Service_Set( + wp_data->object_instance, value.type.Boolean); + } + break; + case PROP_SILENCED: + status = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED); + if (status) { + if (value.type.Enumerated <= UINT16_MAX) { + Life_Safety_Point_Silenced_Set(wp_data->object_instance, + (BACNET_SILENCED_STATE)value.type.Enumerated); + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + case PROP_OPERATION_EXPECTED: + status = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED); + if (status) { + if (value.type.Enumerated <= UINT16_MAX) { + Life_Safety_Point_Operation_Expected_Set( + wp_data->object_instance, + (BACNET_LIFE_SAFETY_OPERATION)value.type.Enumerated); + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } } break; - case PROP_OBJECT_IDENTIFIER: case PROP_OBJECT_NAME: case PROP_DESCRIPTION: case PROP_OBJECT_TYPE: - case PROP_PRESENT_VALUE: case PROP_TRACKING_VALUE: case PROP_STATUS_FLAGS: case PROP_EVENT_STATE: case PROP_RELIABILITY: case PROP_ACCEPTED_MODES: - case PROP_SILENCED: - case PROP_OPERATION_EXPECTED: wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; break; @@ -371,3 +652,94 @@ bool Life_Safety_Point_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) return status; } + +/** + * @brief Creates an object and initialize its properties to defaults + * @param object_instance - object-instance number of the object + * @return the object-instance that was created, or BACNET_MAX_INSTANCE + */ +uint32_t Life_Safety_Point_Create(uint32_t object_instance) +{ + struct object_data *pObject = NULL; + int index = 0; + + 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) { + pObject->Object_Name = NULL; + pObject->Reliability = RELIABILITY_NO_FAULT_DETECTED; + pObject->Mode = LIFE_SAFETY_MODE_DEFAULT; + pObject->Present_Value = LIFE_SAFETY_STATE_QUIET; + pObject->Silenced = SILENCED_STATE_UNSILENCED; + pObject->Operation_Expected = LIFE_SAFETY_OP_NONE; + pObject->Out_Of_Service = false; + /* add to list */ + index = Keylist_Data_Add(Object_List, object_instance, pObject); + if (index < 0) { + free(pObject); + return BACNET_MAX_INSTANCE; + } + } else { + return BACNET_MAX_INSTANCE; + } + } + + return object_instance; +} + +/** + * @brief Deletes an object and its property + * @param object_instance - object-instance number of the object + * @return true if the object-instance was deleted + */ +bool Life_Safety_Point_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; +} + +/** + * @brief Deletes all the objects and their property data + */ +void Life_Safety_Point_Cleanup(void) +{ + struct object_data *pObject; + + if (Object_List) { + do { + pObject = Keylist_Data_Pop(Object_List); + if (pObject) { + free(pObject); + } + } while (pObject); + Keylist_Delete(Object_List); + Object_List = NULL; + } +} + +/** + * @brief Initializes the object data + */ +void Life_Safety_Point_Init(void) +{ + Object_List = Keylist_Create(); +} diff --git a/src/bacnet/basic/object/lsp.h b/src/bacnet/basic/object/lsp.h index 4a721323..2206a8aa 100644 --- a/src/bacnet/basic/object/lsp.h +++ b/src/bacnet/basic/object/lsp.h @@ -54,13 +54,61 @@ extern "C" { BACNET_STACK_EXPORT unsigned Life_Safety_Point_Instance_To_Index( uint32_t object_instance); + BACNET_STACK_EXPORT bool Life_Safety_Point_Object_Name( uint32_t object_instance, BACNET_CHARACTER_STRING * object_name); BACNET_STACK_EXPORT - void Life_Safety_Point_Init( - void); + bool Life_Safety_Point_Name_Set( + uint32_t object_instance, + char *new_name); + + + BACNET_STACK_EXPORT + BACNET_LIFE_SAFETY_STATE Life_Safety_Point_Present_Value( + uint32_t object_instance); + BACNET_STACK_EXPORT + bool Life_Safety_Point_Present_Value_Set( + uint32_t object_instance, + BACNET_LIFE_SAFETY_STATE present_value); + + BACNET_STACK_EXPORT + BACNET_SILENCED_STATE Life_Safety_Point_Silenced( + uint32_t object_instance); + BACNET_STACK_EXPORT + bool Life_Safety_Point_Silenced_Set( + uint32_t object_instance, + BACNET_SILENCED_STATE value); + BACNET_STACK_EXPORT + BACNET_LIFE_SAFETY_MODE Life_Safety_Point_Mode( + uint32_t object_instance); + BACNET_STACK_EXPORT + bool Life_Safety_Point_Mode_Set( + uint32_t object_instance, + BACNET_LIFE_SAFETY_MODE value); + BACNET_STACK_EXPORT + BACNET_LIFE_SAFETY_OPERATION Life_Safety_Point_Operation_Expected( + uint32_t object_instance); + BACNET_STACK_EXPORT + bool Life_Safety_Point_Operation_Expected_Set( + uint32_t object_instance, + BACNET_LIFE_SAFETY_OPERATION value); + + BACNET_STACK_EXPORT + bool Life_Safety_Point_Out_Of_Service( + uint32_t instance); + BACNET_STACK_EXPORT + void Life_Safety_Point_Out_Of_Service_Set( + uint32_t instance, + bool oos_flag); + + BACNET_STACK_EXPORT + BACNET_RELIABILITY Life_Safety_Point_Reliability( + uint32_t object_instance); + BACNET_STACK_EXPORT + bool Life_Safety_Point_Reliability_Set( + uint32_t object_instance, BACNET_RELIABILITY value); BACNET_STACK_EXPORT int Life_Safety_Point_Read_Property( @@ -70,6 +118,20 @@ extern "C" { bool Life_Safety_Point_Write_Property( BACNET_WRITE_PROPERTY_DATA * wp_data); + BACNET_STACK_EXPORT + uint32_t Life_Safety_Point_Create( + uint32_t object_instance); + BACNET_STACK_EXPORT + bool Life_Safety_Point_Delete( + uint32_t object_instance); + BACNET_STACK_EXPORT + void Life_Safety_Point_Cleanup( + void); + BACNET_STACK_EXPORT + void Life_Safety_Point_Init( + void); + + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cd14bbb0..7d611590 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,9 +4,6 @@ cmake_minimum_required(VERSION 3.10 FATAL_ERROR) # set the project name project(Unit_Tests) -# add definitions -add_definitions(-fprofile-arcs -ftest-coverage) - option(BACNET_STACK_DEPRECATED_DISABLE "Disable deprecation compile warnings" ON) if(BACNET_STACK_DEPRECATED_DISABLE) @@ -14,11 +11,19 @@ if(BACNET_STACK_DEPRECATED_DISABLE) endif() # Set the compiler options -if (NOT MSVC) - add_compile_options(-Wall -g -O0 -W -fprofile-arcs -ftest-coverage) +if (CMAKE_C_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID MATCHES "GNU") + add_definitions(-fprofile-arcs -ftest-coverage) + add_compile_options(-g -O0 -W -fprofile-arcs -ftest-coverage) + add_compile_options(-Wall -Wextra -pedantic) + add_compile_options(-Wfloat-equal -Wconversion -Wparentheses) + add_compile_options(-Wunused-value -Wreturn-type -Wswitch-default) + add_compile_options(-Wuninitialized -Winit-self) + add_compile_options(-Wno-sign-conversion -Wno-conversion) + add_compile_options(-Wno-sign-compare -Wno-long-long) + add_compile_options(-Wno-implicit-fallthrough -Wno-attributes) # ignore some warnings that occur during unit testing add_compile_options(-Wno-unused-variable -Wno-unused-function) - add_compile_options(-Wno-sign-compare -Wno-unused-parameter) + add_compile_options(-Wno-unused-parameter) add_link_options(-fprofile-arcs -ftest-coverage) endif() diff --git a/test/bacnet/basic/object/lsp/CMakeLists.txt b/test/bacnet/basic/object/lsp/CMakeLists.txt index 82eeffa9..4ce23ebf 100644 --- a/test/bacnet/basic/object/lsp/CMakeLists.txt +++ b/test/bacnet/basic/object/lsp/CMakeLists.txt @@ -7,7 +7,6 @@ project(test_${basename} VERSION 1.0.0 LANGUAGES C) - string(REGEX REPLACE "/test/bacnet/[a-zA-Z_/-]*$" "/src" @@ -45,8 +44,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/datetime.c ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/basic/sys/keylist.c + ${SRC_DIR}/bacnet/datetime.c ${SRC_DIR}/bacnet/indtext.c ${SRC_DIR}/bacnet/hostnport.c ${SRC_DIR}/bacnet/lighting.c diff --git a/test/bacnet/basic/object/lsp/src/main.c b/test/bacnet/basic/object/lsp/src/main.c index 8f54519d..39a20f43 100644 --- a/test/bacnet/basic/object/lsp/src/main.c +++ b/test/bacnet/basic/object/lsp/src/main.c @@ -9,6 +9,7 @@ */ #include +#include #include /** @@ -25,26 +26,108 @@ ZTEST(lsp_tests, testLifeSafetyPoint) static void testLifeSafetyPoint(void) #endif { + BACNET_OBJECT_TYPE object_type = OBJECT_LIFE_SAFETY_POINT; uint8_t apdu[MAX_APDU] = { 0 }; - int len = 0, test_len = 0; - BACNET_OBJECT_TYPE decoded_type = 0; - uint32_t decoded_instance = 0; - BACNET_READ_PROPERTY_DATA rpdata; + int len = 0; + int test_len = 0; + BACNET_READ_PROPERTY_DATA rpdata = { 0 }; + BACNET_APPLICATION_DATA_VALUE value = { 0 }; + const int *pRequired = NULL; + const int *pOptional = NULL; + const int *pProprietary = NULL; + const uint32_t instance = 123; + BACNET_WRITE_PROPERTY_DATA wpdata = { 0 }; + bool status = false; + unsigned index; Life_Safety_Point_Init(); + Life_Safety_Point_Create(instance); + status = Life_Safety_Point_Valid_Instance(instance); + zassert_true(status, NULL); + index = Life_Safety_Point_Instance_To_Index(instance); + zassert_equal(index, 0, NULL); + rpdata.application_data = &apdu[0]; rpdata.application_data_len = sizeof(apdu); - rpdata.object_type = OBJECT_LIFE_SAFETY_POINT; - rpdata.object_instance = 1; + rpdata.object_type = object_type; + rpdata.object_instance = instance; rpdata.object_property = PROP_OBJECT_IDENTIFIER; - rpdata.array_index = BACNET_ARRAY_ALL; + + Life_Safety_Point_Property_Lists(&pRequired, &pOptional, &pProprietary); + while ((*pRequired) >= 0) { + rpdata.object_property = *pRequired; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Life_Safety_Point_Read_Property(&rpdata); + zassert_not_equal(len, BACNET_STATUS_ERROR, + "property '%s': failed to ReadProperty!\n", + bactext_property_name(rpdata.object_property)); + if (len >= 0) { + test_len = bacapp_decode_known_property(rpdata.application_data, + len, &value, rpdata.object_type, rpdata.object_property); + if (len != test_len) { + printf("property '%s': failed to decode!\n", + bactext_property_name(rpdata.object_property)); + } else { + zassert_equal(len, test_len, NULL); + } + /* check WriteProperty properties */ + wpdata.object_type = rpdata.object_type; + wpdata.object_instance = rpdata.object_instance; + wpdata.object_property = rpdata.object_property; + wpdata.array_index = rpdata.array_index; + memcpy(&wpdata.application_data, rpdata.application_data, MAX_APDU); + wpdata.application_data_len = len; + wpdata.error_code = ERROR_CODE_SUCCESS; + status = Life_Safety_Point_Write_Property(&wpdata); + if (!status) { + /* verify WriteProperty property is known */ + zassert_not_equal(wpdata.error_code, + ERROR_CODE_UNKNOWN_PROPERTY, + "property '%s': WriteProperty Unknown!\n", + bactext_property_name(rpdata.object_property)); + } + } + pRequired++; + } + while ((*pOptional) != -1) { + rpdata.object_property = *pOptional; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Life_Safety_Point_Read_Property(&rpdata); + zassert_not_equal(len, BACNET_STATUS_ERROR, + "property '%s': failed to ReadProperty!\n", + bactext_property_name(rpdata.object_property)); + if (len > 0) { + test_len = bacapp_decode_application_data(rpdata.application_data, + (uint8_t)rpdata.application_data_len, &value); + zassert_equal(len, test_len, "property '%s': failed to decode!\n", + bactext_property_name(rpdata.object_property)); + /* check WriteProperty properties */ + wpdata.object_type = rpdata.object_type; + wpdata.object_instance = rpdata.object_instance; + wpdata.object_property = rpdata.object_property; + wpdata.array_index = rpdata.array_index; + memcpy(&wpdata.application_data, rpdata.application_data, MAX_APDU); + wpdata.application_data_len = len; + wpdata.error_code = ERROR_CODE_SUCCESS; + status = Life_Safety_Point_Write_Property(&wpdata); + if (!status) { + /* verify WriteProperty property is known */ + zassert_not_equal(wpdata.error_code, + ERROR_CODE_UNKNOWN_PROPERTY, + "property '%s': WriteProperty Unknown!\n", + bactext_property_name(rpdata.object_property)); + } + } + pOptional++; + } + rpdata.object_property = PROP_ALL; len = Life_Safety_Point_Read_Property(&rpdata); - zassert_not_equal(len, 0, NULL); - test_len = bacnet_object_id_application_decode( - apdu, len, &decoded_type, &decoded_instance); - zassert_not_equal(test_len, BACNET_STATUS_ERROR, NULL); - zassert_equal(decoded_type, rpdata.object_type, NULL); - zassert_equal(decoded_instance, rpdata.object_instance, NULL); + zassert_equal(len, BACNET_STATUS_ERROR, NULL); + wpdata.object_property = PROP_ALL; + status = Life_Safety_Point_Write_Property(&wpdata); + zassert_false(status, NULL); + status = Life_Safety_Point_Delete(instance); + zassert_true(status, NULL); return; } diff --git a/test/ztest/src/ztest.c b/test/ztest/src/ztest.c index bfac3508..ae03a702 100644 --- a/test/ztest/src/ztest.c +++ b/test/ztest/src/ztest.c @@ -251,6 +251,7 @@ static void handle_signal(int sig) PRINT(" at %s function\n", phase_str[phase]); longjmp(test_fail, 1); case TEST_PHASE_FRAMEWORK: + default: PRINT("\n"); longjmp(stack_fail, 1); } diff --git a/zephyr/tests/bacnet/basic/object/lsp/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/lsp/CMakeLists.txt index da4f3355..ec0dcf79 100644 --- a/zephyr/tests/bacnet/basic/object/lsp/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/lsp/CMakeLists.txt @@ -42,6 +42,7 @@ if(BOARD STREQUAL unit_testing) ${BACNET_SRC}/datetime.c ${BACNET_SRC}/timestamp.c ${BACNET_SRC}/basic/sys/days.c + ${BACNET_SRC}/basic/sys/keylist.c ${BACNET_SRC}/bacdevobjpropref.c ${BACNET_SRC}/bactext.c ${BACNET_SRC}/indtext.c