From 76d3680b5e475b327655b3ffb9c2b314b9215575 Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Wed, 3 Apr 2024 13:27:03 -0500 Subject: [PATCH] Feature - added create/delete/COV services for analog, binary, and multistate objects (#612) * Added Create/Delete object services to Analog Input, Analog Value, Binary Input, Binary Value, Multistate Input, Multistate Value object examples, and updated their units tests. --- src/bacnet/basic/object/ai.c | 868 +++++++------ src/bacnet/basic/object/ai.h | 58 +- src/bacnet/basic/object/ao.c | 6 +- src/bacnet/basic/object/av.c | 829 ++++++------ src/bacnet/basic/object/av.h | 99 +- src/bacnet/basic/object/bi.c | 784 ++++++++---- src/bacnet/basic/object/bi.h | 55 +- src/bacnet/basic/object/bo.c | 43 +- src/bacnet/basic/object/bv.c | 995 ++++++++++----- src/bacnet/basic/object/bv.h | 49 +- src/bacnet/basic/object/device.c | 75 +- src/bacnet/basic/object/ms-input.c | 1127 ++++++++++------- src/bacnet/basic/object/ms-input.h | 48 +- src/bacnet/basic/object/mso.c | 28 +- src/bacnet/basic/object/msv.c | 1061 +++++++++++----- src/bacnet/basic/object/msv.h | 48 +- test/bacnet/basic/object/ai/CMakeLists.txt | 10 +- test/bacnet/basic/object/ai/src/main.c | 61 +- test/bacnet/basic/object/ai/stubs.c | 66 + test/bacnet/basic/object/ao/CMakeLists.txt | 11 +- test/bacnet/basic/object/ao/src/main.c | 71 +- test/bacnet/basic/object/av/CMakeLists.txt | 10 +- test/bacnet/basic/object/av/src/main.c | 64 +- test/bacnet/basic/object/av/stubs.c | 66 + test/bacnet/basic/object/bi/CMakeLists.txt | 9 +- test/bacnet/basic/object/bi/src/main.c | 62 +- test/bacnet/basic/object/bo/CMakeLists.txt | 11 +- test/bacnet/basic/object/bo/src/main.c | 69 +- test/bacnet/basic/object/bv/CMakeLists.txt | 10 +- test/bacnet/basic/object/bv/src/main.c | 52 +- test/bacnet/basic/object/channel/src/main.c | 2 +- .../basic/object/ms-input/CMakeLists.txt | 11 +- test/bacnet/basic/object/ms-input/src/main.c | 86 +- test/bacnet/basic/object/mso/CMakeLists.txt | 11 +- test/bacnet/basic/object/mso/src/main.c | 71 +- test/bacnet/basic/object/msv/CMakeLists.txt | 10 +- test/bacnet/basic/object/msv/src/main.c | 88 +- .../bacnet/basic/object/ai/CMakeLists.txt | 26 +- .../bacnet/basic/object/ao/CMakeLists.txt | 62 +- .../bacnet/basic/object/av/CMakeLists.txt | 25 +- .../bacnet/basic/object/bi/CMakeLists.txt | 20 +- .../bacnet/basic/object/bo/CMakeLists.txt | 33 +- .../bacnet/basic/object/bv/CMakeLists.txt | 62 +- .../basic/object/ms-input/CMakeLists.txt | 63 +- .../bacnet/basic/object/mso/CMakeLists.txt | 26 +- .../bacnet/basic/object/msv/CMakeLists.txt | 26 +- .../basic/object/time_value/CMakeLists.txt | 2 +- 47 files changed, 4450 insertions(+), 2919 deletions(-) create mode 100644 test/bacnet/basic/object/ai/stubs.c create mode 100644 test/bacnet/basic/object/av/stubs.c diff --git a/src/bacnet/basic/object/ai.c b/src/bacnet/basic/object/ai.c index 321de09e..9b6532de 100644 --- a/src/bacnet/basic/object/ai.c +++ b/src/bacnet/basic/object/ai.c @@ -14,25 +14,27 @@ #include #include #include +#include /* BACnet Stack defines - first */ #include "bacnet/bacdef.h" /* BACnet Stack API */ #include "bacnet/bacdcode.h" #include "bacnet/bactext.h" -#include "bacnet/basic/object/device.h" -#include "bacnet/basic/services.h" +#include "bacnet/datetime.h" #include "bacnet/proplist.h" #include "bacnet/timestamp.h" +#include "bacnet/basic/services.h" +#include "bacnet/basic/sys/keylist.h" #include "bacnet/basic/sys/debug.h" +/* me! */ #include "bacnet/basic/object/ai.h" #define PRINTF debug_perror -#ifndef MAX_ANALOG_INPUTS -#define MAX_ANALOG_INPUTS 4 -#endif - -static ANALOG_INPUT_DESCR AI_Descr[MAX_ANALOG_INPUTS]; +/* 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_ANALOG_INPUT; /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER, @@ -48,7 +50,7 @@ static const int Properties_Optional[] = { PROP_DESCRIPTION, PROP_RELIABILITY, #endif -1 }; -static const int Properties_Proprietary[] = { 9997, 9998, 9999, -1 }; +static const int Properties_Proprietary[] = { -1 }; void Analog_Input_Property_Lists( const int **pRequired, const int **pOptional, const int **pProprietary) @@ -66,43 +68,24 @@ void Analog_Input_Property_Lists( return; } -void Analog_Input_Init(void) +/** + * @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 analog_input_descr *Analog_Input_Object(uint32_t object_instance) { - unsigned i; -#if defined(INTRINSIC_REPORTING) - unsigned j; -#endif + return Keylist_Data(Object_List, object_instance); +} - for (i = 0; i < MAX_ANALOG_INPUTS; i++) { - AI_Descr[i].Present_Value = 0.0f; - AI_Descr[i].Out_Of_Service = false; - AI_Descr[i].Units = UNITS_PERCENT; - AI_Descr[i].Reliability = RELIABILITY_NO_FAULT_DETECTED; - AI_Descr[i].Prior_Value = 0.0f; - AI_Descr[i].COV_Increment = 1.0f; - AI_Descr[i].Changed = false; - AI_Descr[i].Object_Name = NULL; -#if defined(INTRINSIC_REPORTING) - AI_Descr[i].Event_State = EVENT_STATE_NORMAL; - /* notification class not connected */ - AI_Descr[i].Notification_Class = BACNET_MAX_INSTANCE; - /* initialize Event time stamps using wildcards - and set Acked_transitions */ - for (j = 0; j < MAX_BACNET_EVENT_TRANSITION; j++) { - datetime_wildcard_set(&AI_Descr[i].Event_Time_Stamps[j]); - AI_Descr[i].Acked_Transitions[j].bIsAcked = true; - } - - /* Set handler for GetEventInformation function */ - handler_get_event_information_set( - OBJECT_ANALOG_INPUT, Analog_Input_Event_Information); - /* Set handler for AcknowledgeAlarm function */ - handler_alarm_ack_set(OBJECT_ANALOG_INPUT, Analog_Input_Alarm_Ack); - /* Set handler for GetAlarmSummary Service */ - handler_get_alarm_summary_set( - OBJECT_ANALOG_INPUT, Analog_Input_Alarm_Summary); -#endif - } +/** + * @brief Gets an object from the list using its index in the list + * @param index - index of the object in the list + * @return object found in the list, or NULL if not found + */ +static struct analog_input_descr *Analog_Input_Object_Index(int index) +{ + return Keylist_Data_Index(Object_List, index); } /* we simply have 0-n object instances. Yours might be */ @@ -110,10 +93,10 @@ void Analog_Input_Init(void) /* given instance exists */ bool Analog_Input_Valid_Instance(uint32_t object_instance) { - unsigned int index; + struct analog_input_descr *pObject; - index = Analog_Input_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_INPUTS) { + pObject = Analog_Input_Object(object_instance); + if (pObject) { return true; } @@ -124,7 +107,7 @@ bool Analog_Input_Valid_Instance(uint32_t object_instance) /* more complex, and then count how many you have */ unsigned Analog_Input_Count(void) { - return MAX_ANALOG_INPUTS; + return Keylist_Count(Object_List); } /* we simply have 0-n object instances. Yours might be */ @@ -132,7 +115,11 @@ unsigned Analog_Input_Count(void) /* that correlates to the correct index */ uint32_t Analog_Input_Index_To_Instance(unsigned index) { - return index; + KEY key = UINT32_MAX; + + Keylist_Index_Key(Object_List, index, &key); + + return key; } /* we simply have 0-n object instances. Yours might be */ @@ -140,57 +127,70 @@ uint32_t Analog_Input_Index_To_Instance(unsigned index) /* that correlates to the correct instance number */ unsigned Analog_Input_Instance_To_Index(uint32_t object_instance) { - unsigned index = MAX_ANALOG_INPUTS; - - if (object_instance < MAX_ANALOG_INPUTS) { - index = object_instance; - } - - return index; + return Keylist_Index(Object_List, object_instance); } +/** + * @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 + */ float Analog_Input_Present_Value(uint32_t object_instance) { float value = 0.0f; - unsigned int index; + struct analog_input_descr *pObject; - index = Analog_Input_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_INPUTS) { - value = AI_Descr[index].Present_Value; + pObject = Analog_Input_Object(object_instance); + if (pObject) { + value = pObject->Present_Value; } return value; } -static void Analog_Input_COV_Detect(unsigned int index, float value) +/** + * For a given object instance-number, checks the present-value for COV + * + * @param pObject - specific object with valid data + * @param value - floating point analog value + */ +static void Analog_Input_COV_Detect( + struct analog_input_descr *pObject, float value) { float prior_value = 0.0f; float cov_increment = 0.0f; float cov_delta = 0.0f; - if (index < MAX_ANALOG_INPUTS) { - prior_value = AI_Descr[index].Prior_Value; - cov_increment = AI_Descr[index].COV_Increment; + if (pObject) { + prior_value = pObject->Prior_Value; + cov_increment = pObject->COV_Increment; if (prior_value > value) { cov_delta = prior_value - value; } else { cov_delta = value - prior_value; } if (cov_delta >= cov_increment) { - AI_Descr[index].Changed = true; - AI_Descr[index].Prior_Value = value; + pObject->Changed = true; + pObject->Prior_Value = value; } } } +/** + * For a given object instance-number, sets the present-value + * + * @param object_instance - object-instance number of the object + * @param value - floating point analog value + * @return true if values are within range and present-value is set. + */ void Analog_Input_Present_Value_Set(uint32_t object_instance, float value) { - unsigned int index = 0; + struct analog_input_descr *pObject; - index = Analog_Input_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_INPUTS) { - Analog_Input_COV_Detect(index, value); - AI_Descr[index].Present_Value = value; + pObject = Analog_Input_Object(object_instance); + if (pObject) { + Analog_Input_COV_Detect(pObject, value); + pObject->Present_Value = value; } } @@ -207,14 +207,15 @@ void Analog_Input_Present_Value_Set(uint32_t object_instance, float value) bool Analog_Input_Object_Name( uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) { - unsigned int index = 0; static char text_string[32] = ""; bool status = false; + struct analog_input_descr *pObject; - index = Analog_Input_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_INPUTS) { - if (AI_Descr[index].Object_Name) { - status = characterstring_init_ansi(object_name, AI_Descr[index].Object_Name); + pObject = Analog_Input_Object(object_instance); + if (pObject) { + if (pObject->Object_Name) { + status = + characterstring_init_ansi(object_name, pObject->Object_Name); } else { snprintf(text_string, sizeof(text_string), "ANALOG INPUT %u", object_instance); @@ -235,13 +236,13 @@ bool Analog_Input_Object_Name( */ bool Analog_Input_Name_Set(uint32_t object_instance, char *new_name) { - unsigned int index = 0; bool status = false; + struct analog_input_descr *pObject; - index = Analog_Input_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_INPUTS) { + pObject = Analog_Input_Object(object_instance); + if (pObject) { status = true; - AI_Descr[index].Object_Name = new_name; + pObject->Object_Name = new_name; } return status; @@ -258,11 +259,11 @@ unsigned Analog_Input_Event_State(uint32_t object_instance) { unsigned state = EVENT_STATE_NORMAL; #if defined(INTRINSIC_REPORTING) - unsigned index = 0; + struct analog_input_descr *pObject; - index = Analog_Input_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_INPUTS) { - state = AI_Descr[index].Event_State; + pObject = Analog_Input_Object(object_instance); + if (pObject) { + state = pObject->Event_State; } #else (void)object_instance; @@ -279,11 +280,11 @@ unsigned Analog_Input_Event_State(uint32_t object_instance) char *Analog_Input_Description(uint32_t object_instance) { char *name = NULL; - unsigned index = 0; + struct analog_input_descr *pObject; - index = Analog_Input_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_INPUTS) { - name = AI_Descr[index].Description; + pObject = Analog_Input_Object(object_instance); + if (pObject) { + name = pObject->Description; } return name; @@ -298,25 +299,32 @@ char *Analog_Input_Description(uint32_t object_instance) bool Analog_Input_Description_Set(uint32_t object_instance, char *new_name) { bool status = false; /* return value */ - unsigned index = 0; + struct analog_input_descr *pObject; - index = Analog_Input_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_INPUTS && new_name) { - AI_Descr[index].Description = new_name; - status = true; + pObject = Analog_Input_Object(object_instance); + if (pObject) { + if (new_name) { + pObject->Description = new_name; + status = true; + } } return status; } +/** + * @brief For a given object instance-number, returns the COV status + * @param object_instance - object-instance number of the object + * @return true if the COV flag is set + */ bool Analog_Input_Change_Of_Value(uint32_t object_instance) { - unsigned index = 0; bool changed = false; + struct analog_input_descr *pObject; - index = Analog_Input_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_INPUTS) { - changed = AI_Descr[index].Changed; + pObject = Analog_Input_Object(object_instance); + if (pObject) { + changed = pObject->Changed; } return changed; @@ -324,11 +332,11 @@ bool Analog_Input_Change_Of_Value(uint32_t object_instance) void Analog_Input_Change_Of_Value_Clear(uint32_t object_instance) { - unsigned index = 0; + struct analog_input_descr *pObject; - index = Analog_Input_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_INPUTS) { - AI_Descr[index].Changed = false; + pObject = Analog_Input_Object(object_instance); + if (pObject) { + pObject->Changed = false; } } @@ -346,18 +354,21 @@ bool Analog_Input_Encode_Value_List( bool status = false; bool in_alarm = false; bool out_of_service = false; - const bool fault = false; + bool fault = false; const bool overridden = false; float present_value = 0.0f; - unsigned index = 0; /* offset from instance lookup */ + struct analog_input_descr *pObject; - index = Analog_Input_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_INPUTS) { - if (AI_Descr[index].Event_State != EVENT_STATE_NORMAL) { + pObject = Analog_Input_Object(object_instance); + if (pObject) { + if (pObject->Event_State != EVENT_STATE_NORMAL) { in_alarm = true; } - out_of_service = AI_Descr[index].Out_Of_Service; - present_value = AI_Descr[index].Present_Value; + if (pObject->Reliability != RELIABILITY_NO_FAULT_DETECTED) { + fault = true; + } + out_of_service = pObject->Out_Of_Service; + present_value = pObject->Present_Value; status = cov_value_list_encode_real(value_list, present_value, in_alarm, fault, overridden, out_of_service); } @@ -365,27 +376,37 @@ bool Analog_Input_Encode_Value_List( return status; } +/** + * @brief For a given object instance-number, returns the COV-Increment value + * @param object_instance - object-instance number of the object + * @return COV-Increment value + */ float Analog_Input_COV_Increment(uint32_t object_instance) { - unsigned index = 0; float value = 0; + struct analog_input_descr *pObject; - index = Analog_Input_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_INPUTS) { - value = AI_Descr[index].COV_Increment; + pObject = Analog_Input_Object(object_instance); + if (pObject) { + value = pObject->COV_Increment; } return value; } +/** + * @brief For a given object instance-number, sets the COV-Increment value + * @param object_instance - object-instance number of the object + * @param value - COV-Increment value + */ void Analog_Input_COV_Increment_Set(uint32_t object_instance, float value) { - unsigned index = 0; + struct analog_input_descr *pObject; - index = Analog_Input_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_INPUTS) { - AI_Descr[index].COV_Increment = value; - Analog_Input_COV_Detect(index, AI_Descr[index].Present_Value); + pObject = Analog_Input_Object(object_instance); + if (pObject) { + pObject->COV_Increment = value; + Analog_Input_COV_Detect(pObject, pObject->Present_Value); } } @@ -399,11 +420,11 @@ void Analog_Input_COV_Increment_Set(uint32_t object_instance, float value) uint16_t Analog_Input_Units(uint32_t object_instance) { uint16_t units = UNITS_NO_UNITS; - unsigned index = 0; + struct analog_input_descr *pObject; - index = Analog_Input_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_INPUTS) { - units = AI_Descr[index].Units; + pObject = Analog_Input_Object(object_instance); + if (pObject) { + units = pObject->Units; } return units; @@ -420,11 +441,11 @@ uint16_t Analog_Input_Units(uint32_t object_instance) bool Analog_Input_Units_Set(uint32_t object_instance, uint16_t units) { bool status = false; - unsigned index = 0; + struct analog_input_descr *pObject; - index = Analog_Input_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_INPUTS) { - AI_Descr[index].Units = units; + pObject = Analog_Input_Object(object_instance); + if (pObject) { + pObject->Units = units; status = true; } @@ -433,12 +454,12 @@ bool Analog_Input_Units_Set(uint32_t object_instance, uint16_t units) bool Analog_Input_Out_Of_Service(uint32_t object_instance) { - unsigned index = 0; bool value = false; + struct analog_input_descr *pObject; - index = Analog_Input_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_INPUTS) { - value = AI_Descr[index].Out_Of_Service; + pObject = Analog_Input_Object(object_instance); + if (pObject) { + value = pObject->Out_Of_Service; } return value; @@ -446,245 +467,218 @@ bool Analog_Input_Out_Of_Service(uint32_t object_instance) void Analog_Input_Out_Of_Service_Set(uint32_t object_instance, bool value) { - unsigned index = 0; + struct analog_input_descr *pObject; - index = Analog_Input_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_INPUTS) { - /* BACnet Testing Observed Incident oi00104 - The Changed flag was not being set when a client wrote to the - Out-of-Service bit. Revealed by BACnet Test Client v1.8.16 ( - www.bac-test.com/bacnet-test-client-download ) BC 135.1: 8.2.1-A BC - 135.1: 8.2.2-A Any discussions can be directed to edward@bac-test.com - Please feel free to remove this comment when my changes accepted after - suitable time for review by all interested parties. Say 6 months -> - September 2016 */ - if (AI_Descr[index].Out_Of_Service != value) { - AI_Descr[index].Changed = true; + pObject = Analog_Input_Object(object_instance); + if (pObject) { + if (pObject->Out_Of_Service != value) { + pObject->Changed = true; } - AI_Descr[index].Out_Of_Service = value; + pObject->Out_Of_Service = value; } } -/* return apdu length, or BACNET_STATUS_ERROR on error */ -/* assumption - object already exists */ +#if defined(INTRINSIC_REPORTING) +/** + * @brief Encode a EventTimeStamps property element + * @param object_instance [in] BACnet network port object instance number + * @param index [in] array index requested: + * 0 to N for individual array members + * @param apdu [out] Buffer in which the APDU contents are built, or NULL to + * return the length of buffer if it had been built + * @return The length of the apdu encoded or + * BACNET_STATUS_ERROR for ERROR_CODE_INVALID_ARRAY_INDEX + */ +static int Analog_Input_Event_Time_Stamps_Encode( + uint32_t object_instance, BACNET_ARRAY_INDEX index, uint8_t *apdu) +{ + int apdu_len = 0, len = 0; + struct analog_input_descr *pObject; + + pObject = Analog_Input_Object(object_instance); + if (pObject) { + if (index < MAX_BACNET_EVENT_TRANSITION) { + len = encode_opening_tag(apdu, TIME_STAMP_DATETIME); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_application_date( + apdu, &pObject->Event_Time_Stamps[index].date); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_application_time( + apdu, &pObject->Event_Time_Stamps[index].time); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag(apdu, TIME_STAMP_DATETIME); + apdu_len += len; + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else { + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} +#endif + +/** + * @brief For a given object instance-number, handles the ReadProperty service + * @param rpdata - BACNET_READ_PROPERTY_DATA data, including the requested + * property + * @return number of bytes encoded in the APDU + */ int Analog_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) { int apdu_len = 0; /* return value */ + uint8_t *apdu = NULL; BACNET_BIT_STRING bit_string; BACNET_CHARACTER_STRING char_string; - ANALOG_INPUT_DESCR *CurrentAI; - unsigned object_index = 0; #if defined(INTRINSIC_REPORTING) - unsigned i = 0; - int len = 0; + int apdu_size = 0; #endif - uint8_t *apdu = NULL; + struct analog_input_descr *pObject; if ((rpdata == NULL) || (rpdata->application_data == NULL) || (rpdata->application_data_len == 0)) { return 0; } - - object_index = Analog_Input_Instance_To_Index(rpdata->object_instance); - if (object_index < MAX_ANALOG_INPUTS) { - CurrentAI = &AI_Descr[object_index]; - } else { + pObject = Analog_Input_Object(rpdata->object_instance); + if (!pObject) { return BACNET_STATUS_ERROR; } - apdu = rpdata->application_data; +#if defined(INTRINSIC_REPORTING) + apdu_size = rpdata->application_data_len; +#endif switch ((int)rpdata->object_property) { case PROP_OBJECT_IDENTIFIER: apdu_len = encode_application_object_id( - &apdu[0], OBJECT_ANALOG_INPUT, rpdata->object_instance); + &apdu[0], Object_Type, rpdata->object_instance); break; - case PROP_OBJECT_NAME: Analog_Input_Object_Name(rpdata->object_instance, &char_string); apdu_len = encode_application_character_string(&apdu[0], &char_string); break; - - case PROP_DESCRIPTION: - characterstring_init_ansi(&char_string, - Analog_Input_Description(rpdata->object_instance)); - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; - case PROP_OBJECT_TYPE: apdu_len = - encode_application_enumerated(&apdu[0], OBJECT_ANALOG_INPUT); + encode_application_enumerated(&apdu[0], Object_Type); break; - case PROP_PRESENT_VALUE: apdu_len = encode_application_real( &apdu[0], Analog_Input_Present_Value(rpdata->object_instance)); break; - case PROP_STATUS_FLAGS: bitstring_init(&bit_string); bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, Analog_Input_Event_State(rpdata->object_instance) != EVENT_STATE_NORMAL); 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_OVERRIDDEN, + pObject->Reliability != RELIABILITY_NO_FAULT_DETECTED); bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, - CurrentAI->Out_Of_Service); - + pObject->Out_Of_Service); apdu_len = encode_application_bitstring(&apdu[0], &bit_string); break; - case PROP_EVENT_STATE: apdu_len = encode_application_enumerated( &apdu[0], Analog_Input_Event_State(rpdata->object_instance)); break; - case PROP_RELIABILITY: apdu_len = - encode_application_enumerated(&apdu[0], CurrentAI->Reliability); + encode_application_enumerated(&apdu[0], pObject->Reliability); break; - case PROP_OUT_OF_SERVICE: apdu_len = - encode_application_boolean(&apdu[0], CurrentAI->Out_Of_Service); + encode_application_boolean(&apdu[0], pObject->Out_Of_Service); break; - case PROP_UNITS: - apdu_len = - encode_application_enumerated(&apdu[0], CurrentAI->Units); + apdu_len = encode_application_enumerated(&apdu[0], pObject->Units); + break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Analog_Input_Description(rpdata->object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); break; - case PROP_COV_INCREMENT: apdu_len = - encode_application_real(&apdu[0], CurrentAI->COV_Increment); + encode_application_real(&apdu[0], pObject->COV_Increment); break; - #if defined(INTRINSIC_REPORTING) case PROP_TIME_DELAY: apdu_len = - encode_application_unsigned(&apdu[0], CurrentAI->Time_Delay); + encode_application_unsigned(&apdu[0], pObject->Time_Delay); break; - case PROP_NOTIFICATION_CLASS: apdu_len = encode_application_unsigned( - &apdu[0], CurrentAI->Notification_Class); + &apdu[0], pObject->Notification_Class); break; - case PROP_HIGH_LIMIT: - apdu_len = encode_application_real(&apdu[0], CurrentAI->High_Limit); + apdu_len = encode_application_real(&apdu[0], pObject->High_Limit); break; - case PROP_LOW_LIMIT: - apdu_len = encode_application_real(&apdu[0], CurrentAI->Low_Limit); + apdu_len = encode_application_real(&apdu[0], pObject->Low_Limit); break; - case PROP_DEADBAND: - apdu_len = encode_application_real(&apdu[0], CurrentAI->Deadband); + apdu_len = encode_application_real(&apdu[0], pObject->Deadband); break; - case PROP_LIMIT_ENABLE: bitstring_init(&bit_string); bitstring_set_bit(&bit_string, 0, - (CurrentAI->Limit_Enable & EVENT_LOW_LIMIT_ENABLE) ? true - : false); + (pObject->Limit_Enable & EVENT_LOW_LIMIT_ENABLE) ? true + : false); bitstring_set_bit(&bit_string, 1, - (CurrentAI->Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) ? true - : false); - + (pObject->Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) ? true + : false); apdu_len = encode_application_bitstring(&apdu[0], &bit_string); break; - case PROP_EVENT_ENABLE: bitstring_init(&bit_string); bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, - (CurrentAI->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true - : false); + (pObject->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true + : false); bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, - (CurrentAI->Event_Enable & EVENT_ENABLE_TO_FAULT) ? true - : false); + (pObject->Event_Enable & EVENT_ENABLE_TO_FAULT) ? true : false); bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, - (CurrentAI->Event_Enable & EVENT_ENABLE_TO_NORMAL) ? true - : false); - + (pObject->Event_Enable & EVENT_ENABLE_TO_NORMAL) ? true + : false); apdu_len = encode_application_bitstring(&apdu[0], &bit_string); break; - case PROP_ACKED_TRANSITIONS: bitstring_init(&bit_string); bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, - CurrentAI->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked); + pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked); bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, - CurrentAI->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, - CurrentAI->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); - + pObject->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); apdu_len = encode_application_bitstring(&apdu[0], &bit_string); break; - case PROP_NOTIFY_TYPE: apdu_len = encode_application_enumerated( - &apdu[0], CurrentAI->Notify_Type ? NOTIFY_EVENT : NOTIFY_ALARM); + &apdu[0], pObject->Notify_Type ? NOTIFY_EVENT : NOTIFY_ALARM); break; - case PROP_EVENT_TIME_STAMPS: - /* Array element zero is the number of elements in the array */ - if (rpdata->array_index == 0) - apdu_len = encode_application_unsigned( - &apdu[0], MAX_BACNET_EVENT_TRANSITION); - /* if no index was specified, then try to encode the entire list */ - /* into one packet. */ - else if (rpdata->array_index == BACNET_ARRAY_ALL) { - for (i = 0; i < MAX_BACNET_EVENT_TRANSITION; i++) { - len = encode_opening_tag( - &apdu[apdu_len], TIME_STAMP_DATETIME); - len += encode_application_date(&apdu[apdu_len + len], - &CurrentAI->Event_Time_Stamps[i].date); - len += encode_application_time(&apdu[apdu_len + len], - &CurrentAI->Event_Time_Stamps[i].time); - len += encode_closing_tag( - &apdu[apdu_len + len], TIME_STAMP_DATETIME); - - /* add it if we have room */ - if ((apdu_len + len) < MAX_APDU) - apdu_len += len; - else { - rpdata->error_code = - ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; - apdu_len = BACNET_STATUS_ABORT; - break; - } - } - } else if (rpdata->array_index <= MAX_BACNET_EVENT_TRANSITION) { - apdu_len = - encode_opening_tag(&apdu[apdu_len], TIME_STAMP_DATETIME); - apdu_len += encode_application_date(&apdu[apdu_len], - &CurrentAI->Event_Time_Stamps[rpdata->array_index].date); - apdu_len += encode_application_time(&apdu[apdu_len], - &CurrentAI->Event_Time_Stamps[rpdata->array_index].time); - apdu_len += - encode_closing_tag(&apdu[apdu_len], TIME_STAMP_DATETIME); - } else { + apdu_len = bacnet_array_encode(rpdata->object_instance, + rpdata->array_index, Analog_Input_Event_Time_Stamps_Encode, + MAX_BACNET_EVENT_TRANSITION, apdu, apdu_size); + if (apdu_len == BACNET_STATUS_ABORT) { + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + } else if (apdu_len == BACNET_STATUS_ERROR) { rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; - apdu_len = BACNET_STATUS_ERROR; } break; #endif - case 9997: - /* test case for real encoding-decoding real value correctly */ - apdu_len = encode_application_real(&apdu[0], 90.510F); - break; - case 9998: - /* test case for unsigned encoding-decoding unsigned value correctly - */ - apdu_len = encode_application_unsigned(&apdu[0], 90); - break; - case 9999: - /* test case for signed encoding-decoding negative value correctly - */ - apdu_len = encode_application_signed(&apdu[0], -200); - break; default: rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; @@ -703,15 +697,31 @@ int Analog_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) return apdu_len; } -/* returns true if successful */ +/** + * @brief WriteProperty handler for this object. For the given WriteProperty + * data, the application_data is loaded or the error flags are set. + * @param wp_data - BACNET_WRITE_PROPERTY_DATA data, including + * requested data and space for the reply, or error response. + * @return false if an error is loaded, true if no errors + */ bool Analog_Input_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; - ANALOG_INPUT_DESCR *CurrentAI; + struct analog_input_descr *pObject; + /* Valid data? */ + if (wp_data == NULL) { + return false; + } + if (wp_data->application_data_len == 0) { + return false; + } + pObject = Analog_Input_Object(wp_data->object_instance); + if (!pObject) { + return false; + } /* decode the some of the request */ len = bacapp_decode_application_data( wp_data->application_data, wp_data->application_data_len, &value); @@ -729,19 +739,12 @@ bool Analog_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; return false; } - object_index = Analog_Input_Instance_To_Index(wp_data->object_instance); - if (object_index < MAX_ANALOG_INPUTS) { - CurrentAI = &AI_Descr[object_index]; - } else { - return false; - } - - switch ((int)wp_data->object_property) { + switch (wp_data->object_property) { case PROP_PRESENT_VALUE: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_REAL); if (status) { - if (CurrentAI->Out_Of_Service == true) { + if (pObject->Out_Of_Service == true) { Analog_Input_Present_Value_Set( wp_data->object_instance, value.type.Real); } else { @@ -751,7 +754,6 @@ bool Analog_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) } } break; - case PROP_OUT_OF_SERVICE: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_BOOLEAN); @@ -760,15 +762,13 @@ bool Analog_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) 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) { - CurrentAI->Units = value.type.Enumerated; + pObject->Units = value.type.Enumerated; } break; - case PROP_COV_INCREMENT: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_REAL); @@ -783,55 +783,49 @@ bool Analog_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) } } break; - #if defined(INTRINSIC_REPORTING) case PROP_TIME_DELAY: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT); if (status) { - CurrentAI->Time_Delay = value.type.Unsigned_Int; - CurrentAI->Remaining_Time_Delay = CurrentAI->Time_Delay; + pObject->Time_Delay = value.type.Unsigned_Int; + pObject->Remaining_Time_Delay = pObject->Time_Delay; } break; - case PROP_NOTIFICATION_CLASS: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT); if (status) { - CurrentAI->Notification_Class = value.type.Unsigned_Int; + pObject->Notification_Class = value.type.Unsigned_Int; } break; - case PROP_HIGH_LIMIT: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_REAL); if (status) { - CurrentAI->High_Limit = value.type.Real; + pObject->High_Limit = value.type.Real; } break; - case PROP_LOW_LIMIT: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_REAL); if (status) { - CurrentAI->Low_Limit = value.type.Real; + pObject->Low_Limit = value.type.Real; } break; - case PROP_DEADBAND: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_REAL); if (status) { - CurrentAI->Deadband = value.type.Real; + pObject->Deadband = value.type.Real; } break; - case PROP_LIMIT_ENABLE: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_BIT_STRING); if (status) { if (value.type.Bit_String.bits_used == 2) { - CurrentAI->Limit_Enable = value.type.Bit_String.value[0]; + pObject->Limit_Enable = value.type.Bit_String.value[0]; } else { wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; @@ -839,13 +833,12 @@ bool Analog_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) } } break; - case PROP_EVENT_ENABLE: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_BIT_STRING); if (status) { if (value.type.Bit_String.bits_used == 3) { - CurrentAI->Event_Enable = value.type.Bit_String.value[0]; + pObject->Event_Enable = value.type.Bit_String.value[0]; } else { wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; @@ -853,17 +846,16 @@ bool Analog_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) } } break; - case PROP_NOTIFY_TYPE: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED); if (status) { switch ((BACNET_NOTIFY_TYPE)value.type.Enumerated) { case NOTIFY_EVENT: - CurrentAI->Notify_Type = 1; + pObject->Notify_Type = 1; break; case NOTIFY_ALARM: - CurrentAI->Notify_Type = 0; + pObject->Notify_Type = 0; break; default: wp_data->error_class = ERROR_CLASS_PROPERTY; @@ -875,9 +867,8 @@ bool Analog_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) break; #endif default: - if (property_lists_member( - Properties_Required, Properties_Optional, - Properties_Proprietary, wp_data->object_property)) { + if (property_lists_member(Properties_Required, Properties_Optional, + Properties_Proprietary, wp_data->object_property)) { wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; } else { @@ -890,30 +881,30 @@ bool Analog_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) return status; } +/** + * @brief Handles the Intrinsic Reporting Service for the Analog Input Object + * @param object_instance - object-instance number of the object + */ void Analog_Input_Intrinsic_Reporting(uint32_t object_instance) { #if defined(INTRINSIC_REPORTING) BACNET_EVENT_NOTIFICATION_DATA event_data = { 0 }; BACNET_CHARACTER_STRING msgText = { 0 }; - ANALOG_INPUT_DESCR *CurrentAI = NULL; - unsigned int object_index = 0; + struct analog_input_descr *CurrentAI = NULL; uint8_t FromState = 0; uint8_t ToState = 0; float ExceededLimit = 0.0f; float PresentVal = 0.0f; bool SendNotify = false; - object_index = Analog_Input_Instance_To_Index(object_instance); - if (object_index < MAX_ANALOG_INPUTS) { - CurrentAI = &AI_Descr[object_index]; - } else { + CurrentAI = Analog_Input_Object(object_instance); + if (!CurrentAI) { return; } /* check limits */ if (!CurrentAI->Limit_Enable) { return; /* limits are not configured */ } - if (CurrentAI->Ack_notify_data.bSendAckNotify) { /* clean bSendAckNotify flag */ CurrentAI->Ack_notify_data.bSendAckNotify = false; @@ -921,10 +912,8 @@ void Analog_Input_Intrinsic_Reporting(uint32_t object_instance) ToState = CurrentAI->Ack_notify_data.EventState; PRINTF("Analog-Input[%d]: Send AckNotification.\n", object_instance); characterstring_init_ansi(&msgText, "AckNotification"); - /* Notify Type */ event_data.notifyType = NOTIFY_ACK_NOTIFICATION; - /* Send EventNotification. */ SendNotify = true; } else { @@ -951,7 +940,6 @@ void Analog_Input_Intrinsic_Reporting(uint32_t object_instance) CurrentAI->Remaining_Time_Delay--; break; } - /* A TO-OFFNORMAL event is generated under these conditions: (a) the Present_Value must exceed the Low_Limit plus the Deadband for a minimum period of time, specified in the @@ -973,7 +961,6 @@ void Analog_Input_Intrinsic_Reporting(uint32_t object_instance) /* value of the object is still in the same event state */ CurrentAI->Remaining_Time_Delay = CurrentAI->Time_Delay; break; - case EVENT_STATE_HIGH_LIMIT: /* Once exceeded, the Present_Value must fall below the High_Limit minus the Deadband before a TO-NORMAL event is @@ -998,7 +985,6 @@ void Analog_Input_Intrinsic_Reporting(uint32_t object_instance) /* value of the object is still in the same event state */ CurrentAI->Remaining_Time_Delay = CurrentAI->Time_Delay; break; - case EVENT_STATE_LOW_LIMIT: /* Once the Present_Value has fallen below the Low_Limit, the Present_Value must exceed the Low_Limit plus the Deadband @@ -1023,18 +1009,14 @@ void Analog_Input_Intrinsic_Reporting(uint32_t object_instance) /* value of the object is still in the same event state */ CurrentAI->Remaining_Time_Delay = CurrentAI->Time_Delay; break; - default: return; /* shouldn't happen */ } /* switch (FromState) */ - ToState = CurrentAI->Event_State; - if (FromState != ToState) { /* Event_State has changed. Need to fill only the basic parameters of this type of event. Other parameters will be filled in common function. */ - switch (ToState) { case EVENT_STATE_HIGH_LIMIT: ExceededLimit = CurrentAI->High_Limit; @@ -1072,16 +1054,15 @@ void Analog_Input_Intrinsic_Reporting(uint32_t object_instance) SendNotify = true; } } - if (SendNotify) { /* Event Object Identifier */ - event_data.eventObjectIdentifier.type = OBJECT_ANALOG_INPUT; + event_data.eventObjectIdentifier.type = Object_Type; event_data.eventObjectIdentifier.instance = object_instance; - /* Time Stamp */ event_data.timeStamp.tag = TIME_STAMP_DATETIME; if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) { - Device_getCurrentDateTime(&event_data.timeStamp.value.dateTime); + datetime_local(&event_data.timeStamp.value.dateTime.date, + &event_data.timeStamp.value.dateTime.time, NULL, NULL); /* fill Event_Time_Stamps */ switch (ToState) { case EVENT_STATE_HIGH_LIMIT: @@ -1123,26 +1104,19 @@ void Analog_Input_Intrinsic_Reporting(uint32_t object_instance) break; } } - /* Notification Class */ event_data.notificationClass = CurrentAI->Notification_Class; - /* Event Type */ event_data.eventType = EVENT_OUT_OF_RANGE; - /* Message Text */ event_data.messageText = &msgText; - /* Notify Type */ /* filled before */ - /* From State */ if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) event_data.fromState = FromState; - /* To State */ event_data.toState = CurrentAI->Event_State; - /* Event Values */ if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) { /* Value that exceeded a limit. */ @@ -1171,7 +1145,6 @@ void Analog_Input_Intrinsic_Reporting(uint32_t object_instance) event_data.notificationParams.outOfRange.exceededLimit = ExceededLimit; } - /* add data from notification class */ PRINTF("Analog-Input[%d]: Notification Class[%d]-%s " "%u/%u/%u-%u:%u:%u.%u!\n", @@ -1185,7 +1158,6 @@ void Analog_Input_Intrinsic_Reporting(uint32_t object_instance) (unsigned)event_data.timeStamp.value.dateTime.time.sec, (unsigned)event_data.timeStamp.value.dateTime.time.hundredths); Notification_Class_common_reporting_function(&event_data); - /* Ack required */ if ((event_data.notifyType != NOTIFY_ACK_NOTIFICATION) && (event_data.ackRequired == true)) { @@ -1199,21 +1171,18 @@ void Analog_Input_Intrinsic_Reporting(uint32_t object_instance) CurrentAI->Acked_Transitions[TRANSITION_TO_OFFNORMAL] .Time_Stamp = event_data.timeStamp.value.dateTime; break; - case EVENT_STATE_FAULT: CurrentAI->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked = false; CurrentAI->Acked_Transitions[TRANSITION_TO_FAULT] .Time_Stamp = event_data.timeStamp.value.dateTime; break; - case EVENT_STATE_NORMAL: CurrentAI->Acked_Transitions[TRANSITION_TO_NORMAL] .bIsAcked = false; CurrentAI->Acked_Transitions[TRANSITION_TO_NORMAL] .Time_Stamp = event_data.timeStamp.value.dateTime; break; - default: /* shouldn't happen */ break; } @@ -1225,95 +1194,101 @@ void Analog_Input_Intrinsic_Reporting(uint32_t object_instance) } #if defined(INTRINSIC_REPORTING) +/** + * @brief Handles getting the Event Information for the Analog Input Object + * @param index - index number of the object 0..count + * @param getevent_data - data for the Event Information + * @return 1 if an active event is found, 0 if no active event, -1 if + * end of list + */ int Analog_Input_Event_Information( unsigned index, BACNET_GET_EVENT_INFORMATION_DATA *getevent_data) { bool IsNotAckedTransitions; bool IsActiveEvent; int i; + struct analog_input_descr *pObject; - /* check index */ - if (index < MAX_ANALOG_INPUTS) { + pObject = Analog_Input_Object_Index(index); + if (pObject) { /* Event_State not equal to NORMAL */ - IsActiveEvent = (AI_Descr[index].Event_State != EVENT_STATE_NORMAL); + IsActiveEvent = (pObject->Event_State != EVENT_STATE_NORMAL); /* Acked_Transitions property, which has at least one of the bits (TO-OFFNORMAL, TO-FAULT, TONORMAL) set to FALSE. */ IsNotAckedTransitions = - (AI_Descr[index] - .Acked_Transitions[TRANSITION_TO_OFFNORMAL] - .bIsAcked == false) | - (AI_Descr[index].Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked == + (pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked == false) | - (AI_Descr[index].Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked == + (pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked == + false) | + (pObject->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked == false); } else return -1; /* end of list */ if ((IsActiveEvent) || (IsNotAckedTransitions)) { /* Object Identifier */ - getevent_data->objectIdentifier.type = OBJECT_ANALOG_INPUT; + getevent_data->objectIdentifier.type = Object_Type; getevent_data->objectIdentifier.instance = Analog_Input_Index_To_Instance(index); /* Event State */ - getevent_data->eventState = AI_Descr[index].Event_State; + getevent_data->eventState = pObject->Event_State; /* Acknowledged Transitions */ bitstring_init(&getevent_data->acknowledgedTransitions); bitstring_set_bit(&getevent_data->acknowledgedTransitions, TRANSITION_TO_OFFNORMAL, - AI_Descr[index] - .Acked_Transitions[TRANSITION_TO_OFFNORMAL] - .bIsAcked); + pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked); bitstring_set_bit(&getevent_data->acknowledgedTransitions, TRANSITION_TO_FAULT, - AI_Descr[index].Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); bitstring_set_bit(&getevent_data->acknowledgedTransitions, TRANSITION_TO_NORMAL, - AI_Descr[index].Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); + pObject->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); /* Event Time Stamps */ for (i = 0; i < 3; i++) { getevent_data->eventTimeStamps[i].tag = TIME_STAMP_DATETIME; getevent_data->eventTimeStamps[i].value.dateTime = - AI_Descr[index].Event_Time_Stamps[i]; + pObject->Event_Time_Stamps[i]; } /* Notify Type */ - getevent_data->notifyType = AI_Descr[index].Notify_Type; + getevent_data->notifyType = pObject->Notify_Type; /* Event Enable */ bitstring_init(&getevent_data->eventEnable); bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_OFFNORMAL, - (AI_Descr[index].Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true - : false); + (pObject->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true : false); bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_FAULT, - (AI_Descr[index].Event_Enable & EVENT_ENABLE_TO_FAULT) ? true - : false); + (pObject->Event_Enable & EVENT_ENABLE_TO_FAULT) ? true : false); bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_NORMAL, - (AI_Descr[index].Event_Enable & EVENT_ENABLE_TO_NORMAL) ? true - : false); + (pObject->Event_Enable & EVENT_ENABLE_TO_NORMAL) ? true : false); /* Event Priorities */ Notification_Class_Get_Priorities( - AI_Descr[index].Notification_Class, getevent_data->eventPriorities); + pObject->Notification_Class, getevent_data->eventPriorities); return 1; /* active event */ } else return 0; /* no active event at this index */ } +/** + * @brief Acknowledges the Event Information for the Analog Input Object + * @param alarmack_data - data for the Event Acknowledgement + * @param error_code - error code for the Event Acknowledgement + * @return 1 if successful, -1 if error, -2 if request is out-of-range + */ int Analog_Input_Alarm_Ack( BACNET_ALARM_ACK_DATA *alarmack_data, BACNET_ERROR_CODE *error_code) { - ANALOG_INPUT_DESCR *CurrentAI; - unsigned int object_index; + struct analog_input_descr *CurrentAI; - object_index = Analog_Input_Instance_To_Index( - alarmack_data->eventObjectIdentifier.instance); - - if (object_index < MAX_ANALOG_INPUTS) - CurrentAI = &AI_Descr[object_index]; - else { + if (!alarmack_data) { + return -1; + } + CurrentAI = + Analog_Input_Object(alarmack_data->eventObjectIdentifier.instance); + if (!CurrentAI) { *error_code = ERROR_CODE_UNKNOWN_OBJECT; return -1; } - switch (alarmack_data->eventStateAcked) { case EVENT_STATE_OFFNORMAL: case EVENT_STATE_HIGH_LIMIT: @@ -1404,43 +1379,166 @@ int Analog_Input_Alarm_Ack( return 1; } +/** + * @brief Handles getting the Alarm Summary for the Analog Input Object + * @param index - index number of the object 0..count + * @param getalarm_data - data for the Alarm Summary + * @return 1 if an active alarm is found, 0 if no active alarm, -1 if + * end of list + */ int Analog_Input_Alarm_Summary( unsigned index, BACNET_GET_ALARM_SUMMARY_DATA *getalarm_data) { - /* check index */ - if (index < MAX_ANALOG_INPUTS) { + struct analog_input_descr *pObject; + + pObject = Analog_Input_Object_Index(index); + if (pObject) { /* Event_State is not equal to NORMAL and Notify_Type property value is ALARM */ - if ((AI_Descr[index].Event_State != EVENT_STATE_NORMAL) && - (AI_Descr[index].Notify_Type == NOTIFY_ALARM)) { + if ((pObject->Event_State != EVENT_STATE_NORMAL) && + (pObject->Notify_Type == NOTIFY_ALARM)) { /* Object Identifier */ - getalarm_data->objectIdentifier.type = OBJECT_ANALOG_INPUT; + getalarm_data->objectIdentifier.type = Object_Type; getalarm_data->objectIdentifier.instance = Analog_Input_Index_To_Instance(index); /* Alarm State */ - getalarm_data->alarmState = AI_Descr[index].Event_State; + getalarm_data->alarmState = pObject->Event_State; /* Acknowledged Transitions */ bitstring_init(&getalarm_data->acknowledgedTransitions); bitstring_set_bit(&getalarm_data->acknowledgedTransitions, TRANSITION_TO_OFFNORMAL, - AI_Descr[index] - .Acked_Transitions[TRANSITION_TO_OFFNORMAL] - .bIsAcked); + pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked); bitstring_set_bit(&getalarm_data->acknowledgedTransitions, TRANSITION_TO_FAULT, - AI_Descr[index] - .Acked_Transitions[TRANSITION_TO_FAULT] - .bIsAcked); + pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); bitstring_set_bit(&getalarm_data->acknowledgedTransitions, TRANSITION_TO_NORMAL, - AI_Descr[index] - .Acked_Transitions[TRANSITION_TO_NORMAL] - .bIsAcked); - + pObject->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); return 1; /* active alarm */ - } else + } else { return 0; /* no active alarm at this index */ - } else + } + } else { return -1; /* end of list */ + } } #endif /* defined(INTRINSIC_REPORTING) */ + +/** + * @brief Creates a Analog Value object + * @param object_instance - object-instance number of the object + * @return the object-instance that was created, or BACNET_MAX_INSTANCE + */ +uint32_t Analog_Input_Create(uint32_t object_instance) +{ + struct analog_input_descr *pObject = NULL; + int index = 0; +#if defined(INTRINSIC_REPORTING) + unsigned j; +#endif + + 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 analog_input_descr)); + if (pObject) { + pObject->Object_Name = NULL; + pObject->Description = NULL; + pObject->Reliability = RELIABILITY_NO_FAULT_DETECTED; + pObject->COV_Increment = 1.0; + pObject->Present_Value = 0.0f; + pObject->Prior_Value = 0.0; + pObject->Units = UNITS_PERCENT; + pObject->Out_Of_Service = false; + pObject->Changed = false; + pObject->Event_State = EVENT_STATE_NORMAL; +#if defined(INTRINSIC_REPORTING) + /* notification class not connected */ + pObject->Notification_Class = BACNET_MAX_INSTANCE; + /* initialize Event time stamps using wildcards + and set Acked_transitions */ + for (j = 0; j < MAX_BACNET_EVENT_TRANSITION; j++) { + datetime_wildcard_set(&pObject->Event_Time_Stamps[j]); + pObject->Acked_Transitions[j].bIsAcked = true; + } +#endif + /* 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 Analog Value object + * @param object_instance - object-instance number of the object + * @return true if the object-instance was deleted + */ +bool Analog_Input_Delete(uint32_t object_instance) +{ + bool status = false; + struct analog_input_descr *pObject = NULL; + + pObject = Keylist_Data_Delete(Object_List, object_instance); + if (pObject) { + free(pObject); + status = true; + } + + return status; +} + +/** + * @brief Deletes all the Analog Values and their data + */ +void Analog_Input_Cleanup(void) +{ + struct analog_input_descr *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 Analog Value object data + */ +void Analog_Input_Init(void) +{ + if (!Object_List) { + Object_List = Keylist_Create(); + } +#if defined(INTRINSIC_REPORTING) + /* Set handler for GetEventInformation function */ + handler_get_event_information_set( + Object_Type, Analog_Input_Event_Information); + /* Set handler for AcknowledgeAlarm function */ + handler_alarm_ack_set(Object_Type, Analog_Input_Alarm_Ack); + /* Set handler for GetAlarmSummary Service */ + handler_get_alarm_summary_set( + Object_Type, Analog_Input_Alarm_Summary); +#endif +} diff --git a/src/bacnet/basic/object/ai.h b/src/bacnet/basic/object/ai.h index a382bbc9..7adb4b42 100644 --- a/src/bacnet/basic/object/ai.h +++ b/src/bacnet/basic/object/ai.h @@ -40,39 +40,39 @@ #include "bacnet/get_alarm_sum.h" #endif +typedef struct analog_input_descr { + unsigned Event_State:3; + float Present_Value; + BACNET_RELIABILITY Reliability; + bool Out_Of_Service; + uint8_t Units; + float Prior_Value; + float COV_Increment; + bool Changed; + char* Object_Name; + char* Description; +#if defined(INTRINSIC_REPORTING) + uint32_t Time_Delay; + uint32_t Notification_Class; + float High_Limit; + float Low_Limit; + float Deadband; + unsigned Limit_Enable:2; + unsigned Event_Enable:3; + unsigned Notify_Type:1; + ACKED_INFO Acked_Transitions[MAX_BACNET_EVENT_TRANSITION]; + BACNET_DATE_TIME Event_Time_Stamps[MAX_BACNET_EVENT_TRANSITION]; + /* time to generate event notification */ + uint32_t Remaining_Time_Delay; + /* AckNotification information */ + ACK_NOTIFICATION Ack_notify_data; +#endif +} ANALOG_INPUT_DESCR; + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ - typedef struct analog_input_descr { - unsigned Event_State:3; - float Present_Value; - BACNET_RELIABILITY Reliability; - bool Out_Of_Service; - uint8_t Units; - float Prior_Value; - float COV_Increment; - bool Changed; - char* Object_Name; - char* Description; -#if defined(INTRINSIC_REPORTING) - uint32_t Time_Delay; - uint32_t Notification_Class; - float High_Limit; - float Low_Limit; - float Deadband; - unsigned Limit_Enable:2; - unsigned Event_Enable:3; - unsigned Notify_Type:1; - ACKED_INFO Acked_Transitions[MAX_BACNET_EVENT_TRANSITION]; - BACNET_DATE_TIME Event_Time_Stamps[MAX_BACNET_EVENT_TRANSITION]; - /* time to generate event notification */ - uint32_t Remaining_Time_Delay; - /* AckNotification information */ - ACK_NOTIFICATION Ack_notify_data; -#endif - } ANALOG_INPUT_DESCR; - BACNET_STACK_EXPORT void Analog_Input_Property_Lists( const int **pRequired, diff --git a/src/bacnet/basic/object/ao.c b/src/bacnet/basic/object/ao.c index 9ed92767..34b664a1 100644 --- a/src/bacnet/basic/object/ao.c +++ b/src/bacnet/basic/object/ao.c @@ -903,14 +903,18 @@ bool Analog_Output_Encode_Value_List( bool status = false; struct object_data *pObject; const bool in_alarm = false; - const bool fault = false; + bool fault = false; const bool overridden = false; pObject = Keylist_Data(Object_List, object_instance); if (pObject) { + if (pObject->Reliability != RELIABILITY_NO_FAULT_DETECTED) { + fault = true; + } status = cov_value_list_encode_real(value_list, pObject->Prior_Value, in_alarm, fault, overridden, pObject->Out_Of_Service); } + return status; } diff --git a/src/bacnet/basic/object/av.c b/src/bacnet/basic/object/av.c index c7ebf33c..66334db8 100644 --- a/src/bacnet/basic/object/av.c +++ b/src/bacnet/basic/object/av.c @@ -1,34 +1,18 @@ -/************************************************************************** - * +/** + * @file + * @author Steve Karg + * @date 2006 + * @brief Analog Value object is an input object with a present-value that + * uses an single precision floating point data type. + * @section LICENSE * Copyright (C) 2006 Steve Karg * Copyright (C) 2011 Krzysztof Malorny - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - *********************************************************************/ - -/* Analog Value Objects - customize for your use */ - + * SPDX-License-Identifier: MIT + */ #include #include #include +#include #include /* BACnet Stack defines - first */ #include "bacnet/bacdef.h" @@ -36,15 +20,16 @@ #include "bacnet/bacdcode.h" #include "bacnet/bacapp.h" #include "bacnet/bactext.h" -#include "bacnet/basic/object/device.h" #include "bacnet/basic/services.h" +#include "bacnet/basic/sys/keylist.h" +#include "bacnet/basic/sys/debug.h" +/* me! */ #include "bacnet/basic/object/av.h" -#ifndef MAX_ANALOG_VALUES -#define MAX_ANALOG_VALUES 4 -#endif - -static ANALOG_VALUE_DESCR AV_Descr[MAX_ANALOG_VALUES]; +/* 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_ANALOG_VALUE; /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Analog_Value_Properties_Required[] = { PROP_OBJECT_IDENTIFIER, @@ -87,58 +72,36 @@ void Analog_Value_Property_Lists( } /** - * Initialize the analog values. + * @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 */ -void Analog_Value_Init(void) +static struct analog_value_descr *Analog_Value_Object(uint32_t object_instance) { - unsigned i; -#if defined(INTRINSIC_REPORTING) - unsigned j; -#endif - - for (i = 0; i < MAX_ANALOG_VALUES; i++) { - memset(&AV_Descr[i], 0x00, sizeof(ANALOG_VALUE_DESCR)); - AV_Descr[i].Present_Value = 0.0f; - AV_Descr[i].Units = UNITS_NO_UNITS; - AV_Descr[i].Prior_Value = 0.0f; - AV_Descr[i].COV_Increment = 1.0f; - AV_Descr[i].Changed = false; - AV_Descr[i].Object_Name = NULL; -#if defined(INTRINSIC_REPORTING) - AV_Descr[i].Event_State = EVENT_STATE_NORMAL; - /* notification class not connected */ - AV_Descr[i].Notification_Class = BACNET_MAX_INSTANCE; - /* initialize Event time stamps using wildcards - and set Acked_transitions */ - for (j = 0; j < MAX_BACNET_EVENT_TRANSITION; j++) { - datetime_wildcard_set(&AV_Descr[i].Event_Time_Stamps[j]); - AV_Descr[i].Acked_Transitions[j].bIsAcked = true; - } - - /* Set handler for GetEventInformation function */ - handler_get_event_information_set( - OBJECT_ANALOG_VALUE, Analog_Value_Event_Information); - /* Set handler for AcknowledgeAlarm function */ - handler_alarm_ack_set(OBJECT_ANALOG_VALUE, Analog_Value_Alarm_Ack); - /* Set handler for GetAlarmSummary Service */ - handler_get_alarm_summary_set( - OBJECT_ANALOG_VALUE, Analog_Value_Alarm_Summary); -#endif - } + return Keylist_Data(Object_List, object_instance); } /** - * We simply have 0-n object instances. Yours might be - * more complex, and then you need validate that the - * given instance exists. - * - * @param object_instance Object instance - * - * @return true/false + * @brief Gets an object from the list using its index in the list + * @param index - index of the object in the list + * @return object found in the list, or NULL if not found + */ +static struct analog_value_descr *Analog_Value_Object_Index(int index) +{ + return Keylist_Data_Index(Object_List, index); +} + +/** + * @brief Determines if a given Analog Value instance is valid + * @param object_instance - object-instance number of the object + * @return true if the instance is valid, and false if not */ bool Analog_Value_Valid_Instance(uint32_t object_instance) { - if (object_instance < MAX_ANALOG_VALUES) { + struct analog_value_descr *pObject; + + pObject = Analog_Value_Object(object_instance); + if (pObject) { return true; } @@ -146,47 +109,39 @@ bool Analog_Value_Valid_Instance(uint32_t object_instance) } /** - * Return the count of analog values. - * - * @return Count of analog values. + * @brief Determines the number of Analog Value objects + * @return Number of Analog Value objects */ unsigned Analog_Value_Count(void) { - return MAX_ANALOG_VALUES; + 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. - * - * @param index Index - * - * @return Object instance + * @brief Determines the object instance-number for a given 0..N index + * of Analog Value objects where N is Analog_Output_Count(). + * @param index - 0..MAX_ANALOG_OUTPUTS value + * @return object instance-number for the given index */ uint32_t Analog_Value_Index_To_Instance(unsigned index) { - return index; + KEY key = UINT32_MAX; + + Keylist_Index_Key(Object_List, index, &key); + + return key; } /** - * 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 - * - * @param object_instance Object instance - * - * @return Index in the object table. + * @brief For a given object instance-number, determines a 0..N index + * of Analog Value objects where N is Analog_Output_Count(). + * @param object_instance - object-instance number of the object + * @return index for the given instance-number, or MAX_ANALOG_OUTPUTS + * if not valid. */ unsigned Analog_Value_Instance_To_Index(uint32_t object_instance) { - unsigned index = MAX_ANALOG_VALUES; - - if (object_instance < MAX_ANALOG_VALUES) { - index = object_instance; - } - - return index; + return Keylist_Index(Object_List, object_instance); } /** @@ -199,23 +154,23 @@ unsigned Analog_Value_Instance_To_Index(uint32_t object_instance) * @param index Object index * @param value Given present value. */ -static void Analog_Value_COV_Detect(unsigned int index, float value) +static void Analog_Value_COV_Detect(struct analog_value_descr *pObject, float value) { float prior_value = 0.0f; float cov_increment = 0.0f; float cov_delta = 0.0f; - if (index < MAX_ANALOG_VALUES) { - prior_value = AV_Descr[index].Prior_Value; - cov_increment = AV_Descr[index].COV_Increment; + if (pObject) { + prior_value = pObject->Prior_Value; + cov_increment = pObject->COV_Increment; if (prior_value > value) { cov_delta = prior_value - value; } else { cov_delta = value - prior_value; } if (cov_delta >= cov_increment) { - AV_Descr[index].Changed = true; - AV_Descr[index].Prior_Value = value; + pObject->Changed = true; + pObject->Prior_Value = value; } } } @@ -233,14 +188,14 @@ static void Analog_Value_COV_Detect(unsigned int index, float value) bool Analog_Value_Present_Value_Set( uint32_t object_instance, float value, uint8_t priority) { - unsigned index = 0; bool status = false; + struct analog_value_descr *pObject; (void)priority; - index = Analog_Value_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_VALUES) { - Analog_Value_COV_Detect(index, value); - AV_Descr[index].Present_Value = value; + pObject = Analog_Value_Object(object_instance); + if (pObject) { + Analog_Value_COV_Detect(pObject, value); + pObject->Present_Value = value; status = true; } @@ -257,11 +212,11 @@ bool Analog_Value_Present_Value_Set( float Analog_Value_Present_Value(uint32_t object_instance) { float value = 0; - unsigned index = 0; + struct analog_value_descr *pObject; - index = Analog_Value_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_VALUES) { - value = AV_Descr[index].Present_Value; + pObject = Analog_Value_Object(object_instance); + if (pObject) { + value = pObject->Present_Value; } return value; @@ -282,10 +237,13 @@ bool Analog_Value_Object_Name( { static char text_string[32] = ""; bool status = false; + struct analog_value_descr *pObject; - if (object_instance < MAX_ANALOG_VALUES) { - if (AV_Descr[object_instance].Object_Name) { - status = characterstring_init_ansi(object_name, AV_Descr[object_instance].Object_Name); + pObject = Analog_Value_Object(object_instance); + if (pObject) { + if (pObject->Object_Name) { + status = characterstring_init_ansi(object_name, + pObject->Object_Name); } else { snprintf(text_string, sizeof(text_string), "ANALOG VALUE %u", object_instance); @@ -307,9 +265,12 @@ bool Analog_Value_Object_Name( bool Analog_Value_Name_Set(uint32_t object_instance, char *new_name) { bool status = false; - if (object_instance < MAX_ANALOG_VALUES) { + struct analog_value_descr *pObject; + + pObject = Analog_Value_Object(object_instance); + if (pObject) { status = true; - AV_Descr[object_instance].Object_Name = new_name; + pObject->Object_Name = new_name; } return status; @@ -325,16 +286,12 @@ bool Analog_Value_Name_Set(uint32_t object_instance, char *new_name) unsigned Analog_Value_Event_State(uint32_t object_instance) { unsigned state = EVENT_STATE_NORMAL; -#if defined(INTRINSIC_REPORTING) - unsigned index = 0; + struct analog_value_descr *pObject; - index = Analog_Value_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_VALUES) { - state = AV_Descr[index].Event_State; + pObject = Analog_Value_Object(object_instance); + if (pObject) { + state = pObject->Event_State; } -#else - (void)object_instance; -#endif return state; } @@ -347,9 +304,11 @@ unsigned Analog_Value_Event_State(uint32_t object_instance) char *Analog_Value_Description(uint32_t object_instance) { char *name = NULL; + struct analog_value_descr *pObject; - if (object_instance < MAX_ANALOG_VALUES) { - name = AV_Descr[object_instance].Description; + pObject = Analog_Value_Object(object_instance); + if (pObject) { + name = pObject->Description; } return name; @@ -364,15 +323,59 @@ char *Analog_Value_Description(uint32_t object_instance) bool Analog_Value_Description_Set(uint32_t object_instance, char *new_name) { bool status = false; /* return value */ + struct analog_value_descr *pObject; - if (object_instance < MAX_ANALOG_VALUES && new_name) { + pObject = Analog_Value_Object(object_instance); + if (pObject) { status = true; - AV_Descr[object_instance].Description = new_name; + pObject->Description = new_name; } return status; } +/** + * @brief For a given object instance-number, returns the reliability + * @param object_instance - object-instance number of the object + * @return reliability property value +*/ +BACNET_RELIABILITY Analog_Value_Reliability( + uint32_t object_instance) +{ + BACNET_RELIABILITY value = RELIABILITY_NO_FAULT_DETECTED; + struct analog_value_descr *pObject; + + pObject = Analog_Value_Object(object_instance); + if (pObject) { + value = pObject->Reliability; + } + + return value; +} + +/** + * @brief For a given object instance-number, sets the reliability + * @param object_instance - object-instance number of the object + * @param value - reliability property value + * @return true if the reliability property value was set + */ +bool Analog_Value_Reliability_Set( + uint32_t object_instance, + BACNET_RELIABILITY value) +{ + bool status = false; + struct analog_value_descr *pObject; + + pObject = Analog_Value_Object(object_instance); + if (pObject) { + pObject->Reliability = value; + status = true; + } + + return status; + +} + /** * For a given object instance-number, determines if the COV flag * has been triggered. @@ -383,12 +386,12 @@ bool Analog_Value_Description_Set(uint32_t object_instance, char *new_name) */ bool Analog_Value_Change_Of_Value(uint32_t object_instance) { - unsigned index = 0; bool changed = false; + struct analog_value_descr *pObject; - index = Analog_Value_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_VALUES) { - changed = AV_Descr[index].Changed; + pObject = Analog_Value_Object(object_instance); + if (pObject) { + changed = pObject->Changed; } return changed; @@ -401,11 +404,11 @@ bool Analog_Value_Change_Of_Value(uint32_t object_instance) */ void Analog_Value_Change_Of_Value_Clear(uint32_t object_instance) { - unsigned index = 0; + struct analog_value_descr *pObject; - index = Analog_Value_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_VALUES) { - AV_Descr[index].Changed = false; + pObject = Analog_Value_Object(object_instance); + if (pObject) { + pObject->Changed = false; } } @@ -421,72 +424,61 @@ bool Analog_Value_Encode_Value_List( uint32_t object_instance, BACNET_PROPERTY_VALUE *value_list) { bool status = false; + bool in_alarm = false; + bool out_of_service = false; + bool fault = false; + const bool overridden = false; + float present_value = 0.0f; + struct analog_value_descr *pObject; - if (value_list) { - value_list->propertyIdentifier = PROP_PRESENT_VALUE; - value_list->propertyArrayIndex = BACNET_ARRAY_ALL; - value_list->value.context_specific = false; - value_list->value.tag = BACNET_APPLICATION_TAG_REAL; - value_list->value.type.Real = - Analog_Value_Present_Value(object_instance); - value_list->value.next = NULL; - value_list->priority = BACNET_NO_PRIORITY; - value_list = value_list->next; - } - if (value_list) { - value_list->propertyIdentifier = PROP_STATUS_FLAGS; - value_list->propertyArrayIndex = BACNET_ARRAY_ALL; - value_list->value.context_specific = false; - value_list->value.tag = BACNET_APPLICATION_TAG_BIT_STRING; - bitstring_init(&value_list->value.type.Bit_String); - if (Analog_Value_Event_State(object_instance) == EVENT_STATE_NORMAL) { - bitstring_set_bit(&value_list->value.type.Bit_String, - STATUS_FLAG_IN_ALARM, false); - } else { - bitstring_set_bit( - &value_list->value.type.Bit_String, STATUS_FLAG_IN_ALARM, true); + pObject = Analog_Value_Object(object_instance); + if (pObject) { + if (pObject->Event_State != EVENT_STATE_NORMAL) { + in_alarm = true; } - bitstring_set_bit( - &value_list->value.type.Bit_String, STATUS_FLAG_FAULT, false); - bitstring_set_bit( - &value_list->value.type.Bit_String, STATUS_FLAG_OVERRIDDEN, false); - if (Analog_Value_Out_Of_Service(object_instance)) { - bitstring_set_bit(&value_list->value.type.Bit_String, - STATUS_FLAG_OUT_OF_SERVICE, true); - } else { - bitstring_set_bit(&value_list->value.type.Bit_String, - STATUS_FLAG_OUT_OF_SERVICE, false); + if (pObject->Reliability != RELIABILITY_NO_FAULT_DETECTED) { + fault = true; } - value_list->value.next = NULL; - value_list->priority = BACNET_NO_PRIORITY; - value_list->next = NULL; - status = true; + out_of_service = pObject->Out_Of_Service; + present_value = pObject->Present_Value; + status = cov_value_list_encode_real(value_list, present_value, + in_alarm, fault, overridden, out_of_service); } return status; } +/** + * @brief For a given object instance-number, returns the COV-Increment value + * @param object_instance - object-instance number of the object + * @return COV-Increment value + */ float Analog_Value_COV_Increment(uint32_t object_instance) { - unsigned index = 0; float value = 0; + struct analog_value_descr *pObject; - index = Analog_Value_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_VALUES) { - value = AV_Descr[index].COV_Increment; + pObject = Analog_Value_Object(object_instance); + if (pObject) { + value = pObject->COV_Increment; } return value; } +/** + * @brief For a given object instance-number, sets the COV-Increment value + * @param object_instance - object-instance number of the object + * @param value - COV-Increment value +*/ void Analog_Value_COV_Increment_Set(uint32_t object_instance, float value) { - unsigned index = 0; + struct analog_value_descr *pObject; - index = Analog_Value_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_VALUES) { - AV_Descr[index].COV_Increment = value; - Analog_Value_COV_Detect(index, AV_Descr[index].Present_Value); + pObject = Analog_Value_Object(object_instance); + if (pObject) { + pObject->COV_Increment = value; + Analog_Value_COV_Detect(pObject, pObject->Present_Value); } } @@ -500,9 +492,11 @@ void Analog_Value_COV_Increment_Set(uint32_t object_instance, float value) uint16_t Analog_Value_Units(uint32_t object_instance) { uint16_t units = UNITS_NO_UNITS; + struct analog_value_descr *pObject; - if (object_instance < MAX_ANALOG_VALUES) { - units = AV_Descr[object_instance].Units; + pObject = Analog_Value_Object(object_instance); + if (pObject) { + units = pObject->Units; } return units; @@ -519,41 +513,105 @@ uint16_t Analog_Value_Units(uint32_t object_instance) bool Analog_Value_Units_Set(uint32_t object_instance, uint16_t units) { bool status = false; + struct analog_value_descr *pObject; - if (object_instance < MAX_ANALOG_VALUES) { - AV_Descr[object_instance].Units = units; + pObject = Analog_Value_Object(object_instance); + if (pObject) { + pObject->Units = units; status = true; } return status; } +/** + * @brief 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 Analog_Value_Out_Of_Service(uint32_t object_instance) { - unsigned index = 0; bool value = false; + struct analog_value_descr *pObject; - index = Analog_Value_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_VALUES) { - value = AV_Descr[index].Out_Of_Service; + pObject = Analog_Value_Object(object_instance); + if (pObject) { + value = pObject->Out_Of_Service; } return value; } +/** + * @brief For a given object instance-number, sets the out-of-service property value + * @param object_instance - object-instance number of the object + * @param value - boolean out-of-service value + * @return true if the out-of-service property value was set + */ void Analog_Value_Out_Of_Service_Set(uint32_t object_instance, bool value) { - unsigned index = 0; + struct analog_value_descr *pObject; - index = Analog_Value_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_VALUES) { - if (AV_Descr[index].Out_Of_Service != value) { - AV_Descr[index].Changed = true; + pObject = Analog_Value_Object(object_instance); + if (pObject) { + if (pObject->Out_Of_Service != value) { + pObject->Changed = true; } - AV_Descr[index].Out_Of_Service = value; + pObject->Out_Of_Service = value; } } +#if defined(INTRINSIC_REPORTING) +/** + * @brief Encode a EventTimeStamps property element + * @param object_instance [in] BACnet network port object instance number + * @param index [in] array index requested: + * 0 to N for individual array members + * @param apdu [out] Buffer in which the APDU contents are built, or NULL to + * return the length of buffer if it had been built + * @return The length of the apdu encoded or + * BACNET_STATUS_ERROR for ERROR_CODE_INVALID_ARRAY_INDEX + */ +static int Analog_Value_Event_Time_Stamps_Encode( + uint32_t object_instance, BACNET_ARRAY_INDEX index, uint8_t *apdu) +{ + int apdu_len = 0, len = 0; + struct analog_value_descr *pObject; + + pObject = Analog_Value_Object(object_instance); + if (pObject) { + if (index < MAX_BACNET_EVENT_TRANSITION) { + len = encode_opening_tag(apdu, TIME_STAMP_DATETIME); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_application_date( + apdu, &pObject->Event_Time_Stamps[index].date); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_application_time( + apdu, &pObject->Event_Time_Stamps[index].time); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag(apdu, TIME_STAMP_DATETIME); + apdu_len += len; + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else { + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} +#endif + /** * Return the requested property of the analog value. * @@ -567,13 +625,11 @@ int Analog_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) BACNET_BIT_STRING bit_string; BACNET_CHARACTER_STRING char_string; float real_value = (float)1.414; - unsigned object_index = 0; bool state = false; uint8_t *apdu = NULL; ANALOG_VALUE_DESCR *CurrentAV; #if defined(INTRINSIC_REPORTING) - int len = 0; - unsigned i = 0; + int apdu_size = 0; #endif /* Valid data? */ @@ -584,24 +640,21 @@ int Analog_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) (rpdata->application_data_len == 0)) { return 0; } - - apdu = rpdata->application_data; - - object_index = Analog_Value_Instance_To_Index(rpdata->object_instance); - if (object_index >= MAX_ANALOG_VALUES) { + CurrentAV = Analog_Value_Object(rpdata->object_instance); + if (!CurrentAV) { rpdata->error_class = ERROR_CLASS_OBJECT; rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; return BACNET_STATUS_ERROR; } - - CurrentAV = &AV_Descr[object_index]; - + apdu = rpdata->application_data; +#if defined(INTRINSIC_REPORTING) + apdu_size = rpdata->application_data_len; +#endif switch (rpdata->object_property) { case PROP_OBJECT_IDENTIFIER: apdu_len = encode_application_object_id( - &apdu[0], OBJECT_ANALOG_VALUE, rpdata->object_instance); + &apdu[0], Object_Type, rpdata->object_instance); break; - case PROP_OBJECT_NAME: if (Analog_Value_Object_Name( rpdata->object_instance, &char_string)) { @@ -609,62 +662,47 @@ int Analog_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) encode_application_character_string(&apdu[0], &char_string); } break; - case PROP_DESCRIPTION: characterstring_init_ansi(&char_string, Analog_Value_Description(rpdata->object_instance)); apdu_len = encode_application_character_string(&apdu[0], &char_string); break; - case PROP_OBJECT_TYPE: apdu_len = - encode_application_enumerated(&apdu[0], OBJECT_ANALOG_VALUE); + encode_application_enumerated(&apdu[0], Object_Type); break; - case PROP_PRESENT_VALUE: real_value = Analog_Value_Present_Value(rpdata->object_instance); apdu_len = encode_application_real(&apdu[0], real_value); break; - case PROP_STATUS_FLAGS: bitstring_init(&bit_string); bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, - Analog_Value_Event_State(rpdata->object_instance) != - EVENT_STATE_NORMAL); - bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + (CurrentAV->Event_State != EVENT_STATE_NORMAL)); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, + (CurrentAV->Reliability != RELIABILITY_NO_FAULT_DETECTED)); bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, CurrentAV->Out_Of_Service); - apdu_len = encode_application_bitstring(&apdu[0], &bit_string); break; - case PROP_EVENT_STATE: -#if defined(INTRINSIC_REPORTING) apdu_len = encode_application_enumerated(&apdu[0], CurrentAV->Event_State); -#else - apdu_len = - encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); -#endif break; - case PROP_OUT_OF_SERVICE: state = CurrentAV->Out_Of_Service; apdu_len = encode_application_boolean(&apdu[0], state); break; - case PROP_UNITS: apdu_len = encode_application_enumerated(&apdu[0], CurrentAV->Units); break; - case PROP_COV_INCREMENT: apdu_len = encode_application_real(&apdu[0], CurrentAV->COV_Increment); break; - #if defined(INTRINSIC_REPORTING) case PROP_TIME_DELAY: apdu_len = @@ -696,7 +734,6 @@ int Analog_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) bitstring_set_bit(&bit_string, 1, (CurrentAV->Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) ? true : false); - apdu_len = encode_application_bitstring(&apdu[0], &bit_string); break; @@ -711,7 +748,6 @@ int Analog_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, (CurrentAV->Event_Enable & EVENT_ENABLE_TO_NORMAL) ? true : false); - apdu_len = encode_application_bitstring(&apdu[0], &bit_string); break; @@ -723,7 +759,6 @@ int Analog_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) CurrentAV->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, CurrentAV->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); - apdu_len = encode_application_bitstring(&apdu[0], &bit_string); break; @@ -731,52 +766,19 @@ int Analog_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) apdu_len = encode_application_enumerated( &apdu[0], CurrentAV->Notify_Type ? NOTIFY_EVENT : NOTIFY_ALARM); break; - case PROP_EVENT_TIME_STAMPS: - /* Array element zero is the number of elements in the array */ - if (rpdata->array_index == 0) - apdu_len = encode_application_unsigned( - &apdu[0], MAX_BACNET_EVENT_TRANSITION); - /* if no index was specified, then try to encode the entire list */ - /* into one packet. */ - else if (rpdata->array_index == BACNET_ARRAY_ALL) { - for (i = 0; i < MAX_BACNET_EVENT_TRANSITION; i++) { - len = encode_opening_tag( - &apdu[apdu_len], TIME_STAMP_DATETIME); - len += encode_application_date(&apdu[apdu_len + len], - &CurrentAV->Event_Time_Stamps[i].date); - len += encode_application_time(&apdu[apdu_len + len], - &CurrentAV->Event_Time_Stamps[i].time); - len += encode_closing_tag( - &apdu[apdu_len + len], TIME_STAMP_DATETIME); - - /* add it if we have room */ - if ((apdu_len + len) < MAX_APDU) - apdu_len += len; - else { - rpdata->error_code = - ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; - apdu_len = BACNET_STATUS_ABORT; - break; - } - } - } else if (rpdata->array_index <= MAX_BACNET_EVENT_TRANSITION) { - apdu_len = - encode_opening_tag(&apdu[apdu_len], TIME_STAMP_DATETIME); - apdu_len += encode_application_date(&apdu[apdu_len], - &CurrentAV->Event_Time_Stamps[rpdata->array_index].date); - apdu_len += encode_application_time(&apdu[apdu_len], - &CurrentAV->Event_Time_Stamps[rpdata->array_index].time); - apdu_len += - encode_closing_tag(&apdu[apdu_len], TIME_STAMP_DATETIME); - } else { + apdu_len = bacnet_array_encode(rpdata->object_instance, + rpdata->array_index, Analog_Value_Event_Time_Stamps_Encode, + MAX_BACNET_EVENT_TRANSITION, apdu, apdu_size); + if (apdu_len == BACNET_STATUS_ABORT) { + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + } else if (apdu_len == BACNET_STATUS_ERROR) { rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; - apdu_len = BACNET_STATUS_ERROR; } break; #endif - default: rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; @@ -806,7 +808,6 @@ int Analog_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) bool Analog_Value_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; ANALOG_VALUE_DESCR *CurrentAV; @@ -818,7 +819,6 @@ bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) if (wp_data->application_data_len == 0) { return false; } - /* decode the some of the request */ len = bacapp_decode_application_data( wp_data->application_data, wp_data->application_data_len, &value); @@ -837,17 +837,12 @@ bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; return false; } - - /* Valid object? */ - object_index = Analog_Value_Instance_To_Index(wp_data->object_instance); - if (object_index >= MAX_ANALOG_VALUES) { + CurrentAV = Analog_Value_Object(wp_data->object_instance); + if (!CurrentAV) { wp_data->error_class = ERROR_CLASS_OBJECT; wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; return false; } - - CurrentAV = &AV_Descr[object_index]; - switch (wp_data->object_property) { case PROP_PRESENT_VALUE: status = write_property_type_valid( @@ -875,7 +870,6 @@ bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; } break; - case PROP_OUT_OF_SERVICE: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_BOOLEAN); @@ -883,7 +877,6 @@ bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) CurrentAV->Out_Of_Service = value.type.Boolean; } break; - case PROP_UNITS: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED); @@ -891,7 +884,6 @@ bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) CurrentAV->Units = value.type.Enumerated; } break; - case PROP_COV_INCREMENT: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_REAL); @@ -906,7 +898,6 @@ bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) } } break; - #if defined(INTRINSIC_REPORTING) case PROP_TIME_DELAY: status = write_property_type_valid( @@ -916,7 +907,6 @@ bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) CurrentAV->Remaining_Time_Delay = CurrentAV->Time_Delay; } break; - case PROP_NOTIFICATION_CLASS: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT); @@ -924,7 +914,6 @@ bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) CurrentAV->Notification_Class = value.type.Unsigned_Int; } break; - case PROP_HIGH_LIMIT: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_REAL); @@ -932,7 +921,6 @@ bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) CurrentAV->High_Limit = value.type.Real; } break; - case PROP_LOW_LIMIT: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_REAL); @@ -940,7 +928,6 @@ bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) CurrentAV->Low_Limit = value.type.Real; } break; - case PROP_DEADBAND: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_REAL); @@ -948,7 +935,6 @@ bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) CurrentAV->Deadband = value.type.Real; } break; - case PROP_LIMIT_ENABLE: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_BIT_STRING); @@ -962,7 +948,6 @@ bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) } } break; - case PROP_EVENT_ENABLE: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_BIT_STRING); @@ -976,7 +961,6 @@ bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) } } break; - case PROP_NOTIFY_TYPE: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED); @@ -999,9 +983,9 @@ bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) #endif default: if (property_lists_member( - Analog_Value_Properties_Required, - Analog_Value_Properties_Optional, - Analog_Value_Properties_Proprietary, + Analog_Value_Properties_Required, + Analog_Value_Properties_Optional, + Analog_Value_Properties_Proprietary, wp_data->object_property)) { wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; @@ -1015,29 +999,30 @@ bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) return status; } +/** + * @brief Analog Value intrinsic reporting function. + * @param object_instance [in] BACnet object-instance number of the object + */ void Analog_Value_Intrinsic_Reporting(uint32_t object_instance) { #if defined(INTRINSIC_REPORTING) BACNET_EVENT_NOTIFICATION_DATA event_data; BACNET_CHARACTER_STRING msgText; ANALOG_VALUE_DESCR *CurrentAV; - unsigned int object_index; uint8_t FromState = 0; uint8_t ToState; float ExceededLimit = 0.0f; float PresentVal = 0.0f; bool SendNotify = false; - object_index = Analog_Value_Instance_To_Index(object_instance); - if (object_index < MAX_ANALOG_VALUES) - CurrentAV = &AV_Descr[object_index]; - else + CurrentAV = Analog_Value_Object(object_instance); + if (!CurrentAV) { return; - + } /* check limits */ - if (!CurrentAV->Limit_Enable) + if (!CurrentAV->Limit_Enable) { return; /* limits are not configured */ - + } if (CurrentAV->Ack_notify_data.bSendAckNotify) { /* clean bSendAckNotify flag */ CurrentAV->Ack_notify_data.bSendAckNotify = false; @@ -1046,7 +1031,7 @@ void Analog_Value_Intrinsic_Reporting(uint32_t object_instance) #if PRINT_ENABLED fprintf(stderr, "Send Acknotification for (%s,%u).\n", - bactext_object_type_name(OBJECT_ANALOG_VALUE), + bactext_object_type_name(Object_Type), (unsigned)object_instance); #endif /* PRINT_ENABLED */ @@ -1195,7 +1180,7 @@ void Analog_Value_Intrinsic_Reporting(uint32_t object_instance) #if PRINT_ENABLED fprintf(stderr, "Event_State for (%s,%u) goes from %s to %s.\n", - bactext_object_type_name(OBJECT_ANALOG_VALUE), + bactext_object_type_name(Object_Type), (unsigned)object_instance, bactext_event_state_name(FromState), bactext_event_state_name(ToState)); #endif /* PRINT_ENABLED */ @@ -1210,12 +1195,13 @@ void Analog_Value_Intrinsic_Reporting(uint32_t object_instance) if (SendNotify) { /* Event Object Identifier */ - event_data.eventObjectIdentifier.type = OBJECT_ANALOG_VALUE; + event_data.eventObjectIdentifier.type = Object_Type; event_data.eventObjectIdentifier.instance = object_instance; /* Time Stamp */ event_data.timeStamp.tag = TIME_STAMP_DATETIME; - Device_getCurrentDateTime(&event_data.timeStamp.value.dateTime); + datetime_local(&event_data.timeStamp.value.dateTime.date, + &event_data.timeStamp.value.dateTime.time, NULL, NULL); if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) { /* fill Event_Time_Stamps */ @@ -1329,95 +1315,103 @@ void Analog_Value_Intrinsic_Reporting(uint32_t object_instance) } #if defined(INTRINSIC_REPORTING) +/** + * @brief Handles getting the Event Information for this object. + * @param index - index number of the object 0..count + * @param getevent_data - data for the Event Information + * @return 1 if an active event is found, 0 if no active event, -1 if + * end of list + */ int Analog_Value_Event_Information( unsigned index, BACNET_GET_EVENT_INFORMATION_DATA *getevent_data) { bool IsNotAckedTransitions; bool IsActiveEvent; int i; + struct analog_value_descr *pObject; - /* check index */ - if (index < MAX_ANALOG_VALUES) { + pObject = Analog_Value_Object_Index(index); + if (pObject) { /* Event_State not equal to NORMAL */ - IsActiveEvent = (AV_Descr[index].Event_State != EVENT_STATE_NORMAL); - + IsActiveEvent = (pObject->Event_State != EVENT_STATE_NORMAL); /* Acked_Transitions property, which has at least one of the bits (TO-OFFNORMAL, TO-FAULT, TONORMAL) set to FALSE. */ IsNotAckedTransitions = - (AV_Descr[index] - .Acked_Transitions[TRANSITION_TO_OFFNORMAL] - .bIsAcked == false) | - (AV_Descr[index].Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked == + (pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked == false) | + (pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked == false) | - (AV_Descr[index].Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked == + (pObject->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked == false); - } else + } else { return -1; /* end of list */ - + } if ((IsActiveEvent) || (IsNotAckedTransitions)) { /* Object Identifier */ - getevent_data->objectIdentifier.type = OBJECT_ANALOG_VALUE; + getevent_data->objectIdentifier.type = Object_Type; getevent_data->objectIdentifier.instance = Analog_Value_Index_To_Instance(index); /* Event State */ - getevent_data->eventState = AV_Descr[index].Event_State; + getevent_data->eventState = pObject->Event_State; /* Acknowledged Transitions */ bitstring_init(&getevent_data->acknowledgedTransitions); bitstring_set_bit(&getevent_data->acknowledgedTransitions, TRANSITION_TO_OFFNORMAL, - AV_Descr[index] - .Acked_Transitions[TRANSITION_TO_OFFNORMAL] + pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL] .bIsAcked); bitstring_set_bit(&getevent_data->acknowledgedTransitions, TRANSITION_TO_FAULT, - AV_Descr[index].Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); bitstring_set_bit(&getevent_data->acknowledgedTransitions, TRANSITION_TO_NORMAL, - AV_Descr[index].Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); + pObject->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); /* Event Time Stamps */ for (i = 0; i < 3; i++) { getevent_data->eventTimeStamps[i].tag = TIME_STAMP_DATETIME; getevent_data->eventTimeStamps[i].value.dateTime = - AV_Descr[index].Event_Time_Stamps[i]; + pObject->Event_Time_Stamps[i]; } /* Notify Type */ - getevent_data->notifyType = AV_Descr[index].Notify_Type; + getevent_data->notifyType = pObject->Notify_Type; /* Event Enable */ bitstring_init(&getevent_data->eventEnable); bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_OFFNORMAL, - (AV_Descr[index].Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true + (pObject->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true : false); bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_FAULT, - (AV_Descr[index].Event_Enable & EVENT_ENABLE_TO_FAULT) ? true + (pObject->Event_Enable & EVENT_ENABLE_TO_FAULT) ? true : false); bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_NORMAL, - (AV_Descr[index].Event_Enable & EVENT_ENABLE_TO_NORMAL) ? true + (pObject->Event_Enable & EVENT_ENABLE_TO_NORMAL) ? true : false); /* Event Priorities */ Notification_Class_Get_Priorities( - AV_Descr[index].Notification_Class, getevent_data->eventPriorities); + pObject->Notification_Class, getevent_data->eventPriorities); return 1; /* active event */ } else return 0; /* no active event at this index */ } +/** + * @brief Acknowledges the Event Information for this object. + * @param alarmack_data - data for the Event Acknowledgement + * @param error_code - error code for the Event Acknowledgement + * @return 1 if successful, -1 if error, -2 if request is out-of-range + */ int Analog_Value_Alarm_Ack( BACNET_ALARM_ACK_DATA *alarmack_data, BACNET_ERROR_CODE *error_code) { ANALOG_VALUE_DESCR *CurrentAV; - unsigned int object_index; - object_index = Analog_Value_Instance_To_Index( - alarmack_data->eventObjectIdentifier.instance); - - if (object_index < MAX_ANALOG_VALUES) - CurrentAV = &AV_Descr[object_index]; - else { + if (!alarmack_data) { + return -1; + } + CurrentAV = + Analog_Value_Object(alarmack_data->eventObjectIdentifier.instance); + if (!CurrentAV) { *error_code = ERROR_CODE_UNKNOWN_OBJECT; return -1; } - switch (alarmack_data->eventStateAcked) { case EVENT_STATE_OFFNORMAL: case EVENT_STATE_HIGH_LIMIT: @@ -1514,37 +1508,43 @@ int Analog_Value_Alarm_Ack( return 1; } +/** + * @brief Handles getting the Alarm Summary for this object. + * @param index - index number of the object 0..count + * @param getalarm_data - data for the Alarm Summary + * @return 1 if an active alarm is found, 0 if no active alarm, -1 if + * end of list + */ int Analog_Value_Alarm_Summary( unsigned index, BACNET_GET_ALARM_SUMMARY_DATA *getalarm_data) { - /* check index */ - if (index < MAX_ANALOG_VALUES) { + struct analog_value_descr *pObject; + + pObject = Analog_Value_Object_Index(index); + if (pObject) { /* Event_State is not equal to NORMAL and Notify_Type property value is ALARM */ - if ((AV_Descr[index].Event_State != EVENT_STATE_NORMAL) && - (AV_Descr[index].Notify_Type == NOTIFY_ALARM)) { + if ((pObject->Event_State != EVENT_STATE_NORMAL) && + (pObject->Notify_Type == NOTIFY_ALARM)) { /* Object Identifier */ - getalarm_data->objectIdentifier.type = OBJECT_ANALOG_VALUE; + getalarm_data->objectIdentifier.type = Object_Type; getalarm_data->objectIdentifier.instance = Analog_Value_Index_To_Instance(index); /* Alarm State */ - getalarm_data->alarmState = AV_Descr[index].Event_State; + getalarm_data->alarmState = pObject->Event_State; /* Acknowledged Transitions */ bitstring_init(&getalarm_data->acknowledgedTransitions); bitstring_set_bit(&getalarm_data->acknowledgedTransitions, TRANSITION_TO_OFFNORMAL, - AV_Descr[index] - .Acked_Transitions[TRANSITION_TO_OFFNORMAL] + pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL] .bIsAcked); bitstring_set_bit(&getalarm_data->acknowledgedTransitions, TRANSITION_TO_FAULT, - AV_Descr[index] - .Acked_Transitions[TRANSITION_TO_FAULT] + pObject->Acked_Transitions[TRANSITION_TO_FAULT] .bIsAcked); bitstring_set_bit(&getalarm_data->acknowledgedTransitions, TRANSITION_TO_NORMAL, - AV_Descr[index] - .Acked_Transitions[TRANSITION_TO_NORMAL] + pObject->Acked_Transitions[TRANSITION_TO_NORMAL] .bIsAcked); return 1; /* active alarm */ @@ -1554,3 +1554,122 @@ int Analog_Value_Alarm_Summary( return -1; /* end of list */ } #endif /* defined(INTRINSIC_REPORTING) */ + +/** + * @brief Creates a Analog Value object + * @param object_instance - object-instance number of the object + * @return the object-instance that was created, or BACNET_MAX_INSTANCE + */ +uint32_t Analog_Value_Create(uint32_t object_instance) +{ + struct analog_value_descr *pObject = NULL; + int index = 0; +#if defined(INTRINSIC_REPORTING) + unsigned j; +#endif + + 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 analog_value_descr)); + if (pObject) { + pObject->Object_Name = NULL; + pObject->Description = NULL; + pObject->Reliability = RELIABILITY_NO_FAULT_DETECTED; + pObject->COV_Increment = 1.0; + pObject->Present_Value = 0.0f; + pObject->Prior_Value = 0.0; + pObject->Units = UNITS_PERCENT; + pObject->Out_Of_Service = false; + pObject->Changed = false; + pObject->Event_State = EVENT_STATE_NORMAL; +#if defined(INTRINSIC_REPORTING) + /* notification class not connected */ + pObject->Notification_Class = BACNET_MAX_INSTANCE; + /* initialize Event time stamps using wildcards + and set Acked_transitions */ + for (j = 0; j < MAX_BACNET_EVENT_TRANSITION; j++) { + datetime_wildcard_set(&pObject->Event_Time_Stamps[j]); + pObject->Acked_Transitions[j].bIsAcked = true; + } +#endif + /* 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 Analog Value object + * @param object_instance - object-instance number of the object + * @return true if the object-instance was deleted + */ +bool Analog_Value_Delete(uint32_t object_instance) +{ + bool status = false; + struct analog_value_descr *pObject = NULL; + + pObject = Keylist_Data_Delete(Object_List, object_instance); + if (pObject) { + free(pObject); + status = true; + } + + return status; +} + +/** + * @brief Deletes all the Analog Values and their data + */ +void Analog_Value_Cleanup(void) +{ + struct analog_value_descr *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 Analog Value object data + */ +void Analog_Value_Init(void) +{ + if (!Object_List) { + Object_List = Keylist_Create(); + } +#if defined(INTRINSIC_REPORTING) + /* Set handler for GetEventInformation function */ + handler_get_event_information_set( + Object_Type, Analog_Value_Event_Information); + /* Set handler for AcknowledgeAlarm function */ + handler_alarm_ack_set(Object_Type, Analog_Value_Alarm_Ack); + /* Set handler for GetAlarmSummary Service */ + handler_get_alarm_summary_set( + Object_Type, Analog_Value_Alarm_Summary); +#endif +} diff --git a/src/bacnet/basic/object/av.h b/src/bacnet/basic/object/av.h index b272fb60..66e0de59 100644 --- a/src/bacnet/basic/object/av.h +++ b/src/bacnet/basic/object/av.h @@ -1,30 +1,16 @@ -/************************************************************************** -* -* Copyright (C) 2006 Steve Karg -* Copyright (C) 2011 Krzysztof Malorny -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -* -*********************************************************************/ -#ifndef AV_H -#define AV_H +/** + * @file + * @author Steve Karg + * @date 2006 + * @brief Analog Value object is an input object with a present-value that + * uses an single precision floating point data type. + * @section LICENSE + * Copyright (C) 2006 Steve Karg + * Copyright (C) 2011 Krzysztof Malorny + * SPDX-License-Identifier: MIT + */ +#ifndef BACNET_ANALOG_VALUE_OBJECT_H +#define BACNET_ANALOG_VALUE_OBJECT_H #include #include @@ -41,39 +27,38 @@ #include "bacnet/get_alarm_sum.h" #endif +typedef struct analog_value_descr { + unsigned Event_State:3; + bool Out_Of_Service; + uint16_t Units; + float Present_Value; + float Prior_Value; + float COV_Increment; + bool Changed; + char* Object_Name; + char* Description; + BACNET_RELIABILITY Reliability; +#if defined(INTRINSIC_REPORTING) + uint32_t Time_Delay; + uint32_t Notification_Class; + float High_Limit; + float Low_Limit; + float Deadband; + unsigned Limit_Enable:2; + unsigned Event_Enable:3; + unsigned Notify_Type:1; + ACKED_INFO Acked_Transitions[MAX_BACNET_EVENT_TRANSITION]; + BACNET_DATE_TIME Event_Time_Stamps[MAX_BACNET_EVENT_TRANSITION]; + /* time to generate event notification */ + uint32_t Remaining_Time_Delay; + /* AckNotification information */ + ACK_NOTIFICATION Ack_notify_data; +#endif +} ANALOG_VALUE_DESCR; + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ - - typedef struct analog_value_descr { - unsigned Event_State:3; - bool Out_Of_Service; - uint16_t Units; - float Present_Value; - float Prior_Value; - float COV_Increment; - bool Changed; - char* Object_Name; - char* Description; -#if defined(INTRINSIC_REPORTING) - uint32_t Time_Delay; - uint32_t Notification_Class; - float High_Limit; - float Low_Limit; - float Deadband; - unsigned Limit_Enable:2; - unsigned Event_Enable:3; - unsigned Notify_Type:1; - ACKED_INFO Acked_Transitions[MAX_BACNET_EVENT_TRANSITION]; - BACNET_DATE_TIME Event_Time_Stamps[MAX_BACNET_EVENT_TRANSITION]; - /* time to generate event notification */ - uint32_t Remaining_Time_Delay; - /* AckNotification information */ - ACK_NOTIFICATION Ack_notify_data; -#endif - } ANALOG_VALUE_DESCR; - - BACNET_STACK_EXPORT void Analog_Value_Property_Lists( const int **pRequired, diff --git a/src/bacnet/basic/object/bi.c b/src/bacnet/basic/object/bi.c index d0c73e4f..eea3278d 100644 --- a/src/bacnet/basic/object/bi.c +++ b/src/bacnet/basic/object/bi.c @@ -1,33 +1,17 @@ -/************************************************************************** - * +/** + * @file + * @author Steve Karg + * @date 2006 + * @brief Binary Input object is an input object with a present-value that + * uses an enumerated two state active/inactive data type. + * @section LICENSE * Copyright (C) 2006 Steve Karg - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - *********************************************************************/ - -/* Binary Input Objects customize for your use */ - + * SPDX-License-Identifier: MIT + */ #include #include #include +#include /* BACnet Stack defines - first */ #include "bacnet/bacdef.h" /* BACnet Stack API */ @@ -36,25 +20,33 @@ #include "bacnet/rp.h" #include "bacnet/wp.h" #include "bacnet/cov.h" -#include "bacnet/basic/object/bi.h" +#include "bacnet/basic/object/device.h" #include "bacnet/basic/services.h" +#include "bacnet/basic/sys/keylist.h" +/* me! */ +#include "bacnet/basic/object/bi.h" -#ifndef MAX_BINARY_INPUTS -#define MAX_BINARY_INPUTS 5 -#endif - -/* stores the current value */ -static BACNET_BINARY_PV Present_Value[MAX_BINARY_INPUTS]; -/* out of service decouples physical input from Present_Value */ -static bool Out_Of_Service[MAX_BINARY_INPUTS]; -/* Change of Value flag */ -static bool Change_Of_Value[MAX_BINARY_INPUTS]; -/* Polarity of Input */ -static BACNET_POLARITY Polarity[MAX_BINARY_INPUTS]; -/* stores the object name */ -static char* Object_Name[MAX_BINARY_INPUTS]; -/* stores the description */ -static char* Description[MAX_BINARY_INPUTS]; +static const char *Default_Active_Text = "Active"; +static const char *Default_Inactive_Text = "Inactive"; +struct object_data { + bool Out_Of_Service : 1; + bool Change_Of_Value : 1; + bool Present_Value : 1; + bool Polarity : 1; + bool Write_Enabled : 1; + uint8_t Reliability; + const char *Object_Name; + const char *Active_Text; + const char *Inactive_Text; + const char *Description; +}; +/* 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_BINARY_INPUT; +/* callback for present value writes */ +static binary_input_write_present_value_callback + Binary_Input_Write_Present_Value_Callback; /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Binary_Input_Properties_Required[] = { PROP_OBJECT_IDENTIFIER, @@ -65,6 +57,14 @@ static const int Binary_Input_Properties_Optional[] = { PROP_DESCRIPTION, -1 }; static const int Binary_Input_Properties_Proprietary[] = { -1 }; +/** + * Initialize the pointers for the required, the optional and the properitary + * value properties. + * + * @param pRequired - Pointer to the pointer of required values. + * @param pOptional - Pointer to the pointer of optional values. + * @param pProprietary - Pointer to the pointer of properitary values. + */ void Binary_Input_Property_Lists( const int **pRequired, const int **pOptional, const int **pProprietary) { @@ -81,73 +81,80 @@ void Binary_Input_Property_Lists( return; } -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need validate that the */ -/* given instance exists */ +/** + * @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 *Binary_Input_Object(uint32_t object_instance) +{ + return Keylist_Data(Object_List, object_instance); +} + +/** + * @brief Determines if a given Binary Input instance is valid + * @param object_instance - object-instance number of the object + * @return true if the instance is valid, and false if not + */ bool Binary_Input_Valid_Instance(uint32_t object_instance) { - if (object_instance < MAX_BINARY_INPUTS) { + struct object_data *pObject; + + pObject = Binary_Input_Object(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 Analog Value objects + */ unsigned Binary_Input_Count(void) { - return MAX_BINARY_INPUTS; + 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 the object instance-number for a given 0..N index + * of Binary Input objects where N is Binary_Input_Count(). + * @param index - 0..MAX_BINARY_INPUTS value + * @return object instance-number for the given index + */ uint32_t Binary_Input_Index_To_Instance(unsigned index) { - return index; + return Keylist_Key(Object_List, index); } -void Binary_Input_Init(void) -{ - static bool initialized = false; - unsigned i; - - if (!initialized) { - initialized = true; - - /* initialize all the values */ - for (i = 0; i < MAX_BINARY_INPUTS; i++) { - Present_Value[i] = BINARY_INACTIVE; - } - } - - return; -} - -/* 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 objects where N is the count. + * @param object_instance - object-instance number of the object + * @return index for the given instance-number, or count if not valid. + */ unsigned Binary_Input_Instance_To_Index(uint32_t object_instance) { - unsigned index = MAX_BINARY_INPUTS; - - if (object_instance < MAX_BINARY_INPUTS) { - index = object_instance; - } - - return index; + return Keylist_Index(Object_List, object_instance); } +/** + * 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_BINARY_PV Binary_Input_Present_Value(uint32_t object_instance) { BACNET_BINARY_PV value = BINARY_INACTIVE; - unsigned index = 0; + struct object_data *pObject; - index = Binary_Input_Instance_To_Index(object_instance); - if (index < MAX_BINARY_INPUTS) { - value = Present_Value[index]; - if (Polarity[index] != POLARITY_NORMAL) { + pObject = Binary_Input_Object(object_instance); + if (pObject) { + value = pObject->Present_Value; + if (pObject->Polarity != POLARITY_NORMAL) { if (value == BINARY_INACTIVE) { value = BINARY_ACTIVE; } else { @@ -159,151 +166,186 @@ BACNET_BINARY_PV Binary_Input_Present_Value(uint32_t object_instance) return value; } +/** + * @brief For a given object instance-number, checks the present-value for COV + * @param pObject - specific object with valid data + * @param value - floating point analog value + */ +static void Binary_Input_Present_Value_COV_Detect( + struct object_data *pObject, BACNET_BINARY_PV value) +{ + if (pObject) { + if (pObject->Present_Value != value) { + pObject->Change_Of_Value = true; + } + } +} + +/** + * @brief 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 Binary_Input_Out_Of_Service(uint32_t object_instance) { bool value = false; - unsigned index = 0; + struct object_data *pObject; - index = Binary_Input_Instance_To_Index(object_instance); - if (index < MAX_BINARY_INPUTS) { - value = Out_Of_Service[index]; + pObject = Binary_Input_Object(object_instance); + if (pObject) { + value = pObject->Out_Of_Service; } return value; } -bool Binary_Input_Change_Of_Value(uint32_t object_instance) +/** + * @brief For a given object instance-number, sets the out-of-service property value + * @param object_instance - object-instance number of the object + * @param value - boolean out-of-service value + * @return true if the out-of-service property value was set + */ +void Binary_Input_Out_Of_Service_Set(uint32_t object_instance, bool value) { - bool status = false; - unsigned index; + struct object_data *pObject; - index = Binary_Input_Instance_To_Index(object_instance); - if (index < MAX_BINARY_INPUTS) { - status = Change_Of_Value[index]; - } - - return status; -} - -void Binary_Input_Change_Of_Value_Clear(uint32_t object_instance) -{ - unsigned index; - - index = Binary_Input_Instance_To_Index(object_instance); - if (index < MAX_BINARY_INPUTS) { - Change_Of_Value[index] = false; + pObject = Binary_Input_Object(object_instance); + if (pObject) { + if (pObject->Out_Of_Service != value) { + pObject->Out_Of_Service = value; + pObject->Change_Of_Value = true; + } } return; } /** - * For a given object instance-number, loads the value_list with the COV data. - * + * @brief For a given object, gets the Fault status flag + * @param object_instance - object-instance number of the object + * @return true the status flag is in Fault + */ +static bool Binary_Input_Object_Fault(struct object_data *pObject) +{ + bool fault = false; + + if (pObject) { + if (pObject->Reliability != RELIABILITY_NO_FAULT_DETECTED) { + fault = true; + } + } + + return fault; +} + +/** + * @brief For a given object instance-number, gets the Fault status flag + * @param object_instance - object-instance number of the object + * @return true the status flag is in Fault + */ +static bool Binary_Input_Fault(uint32_t object_instance) +{ + struct object_data *pObject; + + pObject = Binary_Input_Object(object_instance); + + return Binary_Input_Object_Fault(pObject); +} + +/** + * @brief For a given object instance-number, determines if the COV flag + * has been triggered. + * @param object_instance - object-instance number of the object + * @return true if the COV flag is set + */ +bool Binary_Input_Change_Of_Value(uint32_t object_instance) +{ + bool status = false; + struct object_data *pObject; + + pObject = Binary_Input_Object(object_instance); + if (pObject) { + status = pObject->Change_Of_Value; + } + + return status; +} + +/** + * @brief For a given object instance-number, clears the COV flag + * @param object_instance - object-instance number of the object + */ +void Binary_Input_Change_Of_Value_Clear(uint32_t object_instance) +{ + struct object_data *pObject; + + pObject = Binary_Input_Object(object_instance); + if (pObject) { + pObject->Change_Of_Value = false; + } + + return; +} + +/** + * @brief For a given object instance-number, loads the value_list with the COV data. * @param object_instance - object-instance number of the object * @param value_list - list of COV data - * * @return true if the value list is encoded */ bool Binary_Input_Encode_Value_List( uint32_t object_instance, BACNET_PROPERTY_VALUE *value_list) { bool status = false; + const bool in_alarm = false; + bool out_of_service = false; + bool fault = false; + const bool overridden = false; + BACNET_BINARY_PV present_value = BINARY_INACTIVE; + struct object_data *pObject; - if (value_list) { - value_list->propertyIdentifier = PROP_PRESENT_VALUE; - value_list->propertyArrayIndex = BACNET_ARRAY_ALL; - value_list->value.context_specific = false; - value_list->value.tag = BACNET_APPLICATION_TAG_ENUMERATED; - value_list->value.next = NULL; - value_list->value.type.Enumerated = - Binary_Input_Present_Value(object_instance); - value_list->priority = BACNET_NO_PRIORITY; - value_list = value_list->next; - } - if (value_list) { - value_list->propertyIdentifier = PROP_STATUS_FLAGS; - value_list->propertyArrayIndex = BACNET_ARRAY_ALL; - value_list->value.context_specific = false; - value_list->value.tag = BACNET_APPLICATION_TAG_BIT_STRING; - value_list->value.next = NULL; - bitstring_init(&value_list->value.type.Bit_String); - bitstring_set_bit( - &value_list->value.type.Bit_String, STATUS_FLAG_IN_ALARM, false); - bitstring_set_bit( - &value_list->value.type.Bit_String, STATUS_FLAG_FAULT, false); - bitstring_set_bit( - &value_list->value.type.Bit_String, STATUS_FLAG_OVERRIDDEN, false); - if (Binary_Input_Out_Of_Service(object_instance)) { - bitstring_set_bit(&value_list->value.type.Bit_String, - STATUS_FLAG_OUT_OF_SERVICE, true); - } else { - bitstring_set_bit(&value_list->value.type.Bit_String, - STATUS_FLAG_OUT_OF_SERVICE, false); + pObject = Binary_Input_Object(object_instance); + if (pObject) { + if (pObject->Reliability != RELIABILITY_NO_FAULT_DETECTED) { + fault = true; } - value_list->priority = BACNET_NO_PRIORITY; - value_list->next = NULL; - status = true; + out_of_service = pObject->Out_Of_Service; + if (pObject->Present_Value) { + present_value = BINARY_ACTIVE; + } + status = cov_value_list_encode_enumerated(value_list, present_value, + in_alarm, fault, overridden, out_of_service); } return status; } +/** + * @brief For a given object instance-number, sets the present-value + * @param object_instance - object-instance number of the object + * @param value - enumerated binary present-value + * @return true if values are within range and present-value is set. + */ bool Binary_Input_Present_Value_Set( uint32_t object_instance, BACNET_BINARY_PV value) { - unsigned index = 0; bool status = false; + struct object_data *pObject; - index = Binary_Input_Instance_To_Index(object_instance); - if (index < MAX_BINARY_INPUTS) { - if (Polarity[index] != POLARITY_NORMAL) { - if (value == BINARY_INACTIVE) { - value = BINARY_ACTIVE; - } else { - value = BINARY_INACTIVE; + pObject = Binary_Input_Object(object_instance); + if (pObject) { + if (value <= MAX_BINARY_PV) { + if (pObject->Polarity != POLARITY_NORMAL) { + if (value == BINARY_INACTIVE) { + value = BINARY_ACTIVE; + } else { + value = BINARY_INACTIVE; + } } - } - if (Present_Value[index] != value) { - Change_Of_Value[index] = true; - } - Present_Value[index] = value; - status = true; - } - - return status; -} - -void Binary_Input_Out_Of_Service_Set(uint32_t object_instance, bool value) -{ - unsigned index = 0; - - index = Binary_Input_Instance_To_Index(object_instance); - if (index < MAX_BINARY_INPUTS) { - if (Out_Of_Service[index] != value) { - Change_Of_Value[index] = true; - } - Out_Of_Service[index] = value; - } - - return; -} - -bool Binary_Input_Object_Name( - uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) -{ - static char text_string[32] = ""; /* okay for single thread */ - bool status = false; - unsigned index = 0; - - index = Binary_Input_Instance_To_Index(object_instance); - if (index < MAX_BINARY_INPUTS) { - if (Object_Name[index] == NULL) { - sprintf( - text_string, "BINARY INPUT %lu", (unsigned long)object_instance); - status = characterstring_init_ansi(object_name, text_string); - } else { - status = characterstring_init_ansi(object_name, Object_Name[index]); + Binary_Input_Present_Value_COV_Detect(pObject, value); + pObject->Present_Value = true; + status = true; } } @@ -311,46 +353,140 @@ bool Binary_Input_Object_Name( } /** - * For a given object instance-number, sets the object-name + * For a given object instance-number, sets the present-value * * @param object_instance - object-instance number of the object - * @param new_name - holds the object-name to be set + * @param value - floating point analog value + * @param error_class - the BACnet error class + * @param error_code - BACnet Error code * - * @return true if object-name was set + * @return true if values are within range and present-value is set. */ -bool Binary_Input_Name_Set(uint32_t object_instance, char *new_name) +static bool Binary_Input_Present_Value_Write( + uint32_t object_instance, BACNET_BINARY_PV value, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code) { bool status = false; - if (object_instance < MAX_BINARY_INPUTS && new_name) { - status = true; - Object_Name[object_instance] = new_name; + struct object_data *pObject; + BACNET_BINARY_PV old_value = BINARY_INACTIVE; + + pObject = Binary_Input_Object(object_instance); + if (pObject) { + if (value <= MAX_BINARY_PV) { + if (pObject->Write_Enabled) { + old_value = pObject->Present_Value; + Binary_Input_Present_Value_COV_Detect(pObject, value); + pObject->Present_Value = value; + if (pObject->Out_Of_Service) { + /* The physical point that the object represents + is not in service. This means that changes to the + Present_Value property are decoupled from the + physical point when the value of Out_Of_Service + is true. */ + } else if (Binary_Input_Write_Present_Value_Callback) { + Binary_Input_Write_Present_Value_Callback( + object_instance, old_value, value); + } + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; } return status; } +/** + * @brief Get the object name + * @param object_instance - object-instance number of the object + * @param object_name - holds the object-name to be retrieved + * @return true if object-name was retrieved + */ +bool Binary_Input_Object_Name( + uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + struct object_data *pObject; + + pObject = Binary_Input_Object(object_instance); + if (pObject) { + if (pObject->Object_Name == NULL) { + sprintf( + text_string, "BINARY INPUT %lu", (unsigned long)object_instance); + status = characterstring_init_ansi(object_name, text_string); + } else { + status = characterstring_init_ansi(object_name, pObject->Object_Name); + } + } + + return status; +} + +/** + * @brief For a given object instance-number, sets the object-name + * @param object_instance - object-instance number of the object + * @param new_name - holds the object-name to be set + * @return true if object-name was set + */ +bool Binary_Input_Name_Set(uint32_t object_instance, char *new_name) +{ + bool status = false; + struct object_data *pObject; + + pObject = Binary_Input_Object(object_instance); + if (pObject) { + if (new_name) { + status = true; + pObject->Object_Name = new_name; + } + } + + return status; +} + +/** + * @brief For a given object instance-number, returns the polarity property. + * @param object_instance - object-instance number of the object + * @return the polarity property of the object. + */ BACNET_POLARITY Binary_Input_Polarity(uint32_t object_instance) { BACNET_POLARITY polarity = POLARITY_NORMAL; - unsigned index = 0; + struct object_data *pObject; - index = Binary_Input_Instance_To_Index(object_instance); - if (index < MAX_BINARY_INPUTS) { - polarity = Polarity[index]; + pObject = Binary_Input_Object(object_instance); + if (pObject) { + polarity = pObject->Polarity; } return polarity; } +/** + * @brief For a given object instance-number, sets the polarity property + * @param object_instance - object-instance number of the object + * @param polarity - polarity property value + * @return true if polarity was set + */ bool Binary_Input_Polarity_Set( uint32_t object_instance, BACNET_POLARITY polarity) { bool status = false; - unsigned index = 0; + struct object_data *pObject; - index = Binary_Input_Instance_To_Index(object_instance); - if (index < MAX_BINARY_INPUTS) { - Polarity[index] = polarity; + pObject = Binary_Input_Object(object_instance); + if (pObject) { + pObject->Polarity = polarity; } return status; @@ -364,12 +500,14 @@ bool Binary_Input_Polarity_Set( char *Binary_Input_Description(uint32_t object_instance) { char *name = NULL; + struct object_data *pObject; - if (object_instance < MAX_BINARY_INPUTS) { - if (Description[object_instance] == NULL) { + pObject = Binary_Input_Object(object_instance); + if (pObject) { + if (pObject->Description == NULL) { name = ""; } else { - name = Description[object_instance]; + name = (char *)pObject->Description; } } @@ -385,17 +523,27 @@ char *Binary_Input_Description(uint32_t object_instance) bool Binary_Input_Description_Set(uint32_t object_instance, char *new_name) { bool status = false; /* return value */ + struct object_data *pObject; - if (object_instance < MAX_BINARY_INPUTS && new_name) { + pObject = Binary_Input_Object(object_instance); + if (pObject) { status = true; - Description[object_instance] = new_name; + pObject->Description = new_name; } return status; } -/* return apdu length, or BACNET_STATUS_ERROR on error */ -/* assumption - object already exists, and has been bounds checked */ +/** + * 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 Binary_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) { int apdu_len = 0; /* return value */ @@ -412,7 +560,7 @@ int Binary_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) switch (rpdata->object_property) { case PROP_OBJECT_IDENTIFIER: apdu_len = encode_application_object_id( - &apdu[0], OBJECT_BINARY_INPUT, rpdata->object_instance); + &apdu[0], Object_Type, rpdata->object_instance); break; case PROP_OBJECT_NAME: /* note: object name must be unique in our device */ @@ -420,18 +568,11 @@ int Binary_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) apdu_len = encode_application_character_string(&apdu[0], &char_string); break; - case PROP_DESCRIPTION: - characterstring_init_ansi(&char_string, - Binary_Input_Description(rpdata->object_instance)); - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; case PROP_OBJECT_TYPE: apdu_len = - encode_application_enumerated(&apdu[0], OBJECT_BINARY_INPUT); + encode_application_enumerated(&apdu[0], Object_Type); break; case PROP_PRESENT_VALUE: - /* note: you need to look up the actual value */ apdu_len = encode_application_enumerated( &apdu[0], Binary_Input_Present_Value(rpdata->object_instance)); break; @@ -439,14 +580,14 @@ int Binary_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) /* note: see the details in the standard on how to use these */ bitstring_init(&bit_string); bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); - bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + state = Binary_Input_Fault(rpdata->object_instance); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, state); bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); state = Binary_Input_Out_Of_Service(rpdata->object_instance); bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state); apdu_len = encode_application_bitstring(&apdu[0], &bit_string); break; case PROP_EVENT_STATE: - /* note: see the details in the standard on how to use this */ apdu_len = encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); break; @@ -458,6 +599,12 @@ int Binary_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) apdu_len = encode_application_enumerated( &apdu[0], Binary_Input_Polarity(rpdata->object_instance)); break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Binary_Input_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; @@ -474,7 +621,15 @@ int Binary_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) return apdu_len; } -/* returns true if successful */ +/** + * WriteProperty handler for this object. For the given WriteProperty + * data, the application_data is loaded or the error flags are set. + * + * @param wp_data - BACNET_WRITE_PROPERTY_DATA data, including + * requested data and space for the reply, or error response. + * + * @return false if an error is loaded, true if no errors + */ bool Binary_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) { bool status = false; /* return value */ @@ -502,14 +657,10 @@ bool Binary_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED); if (status) { - if (value.type.Enumerated <= MAX_BINARY_PV) { - Binary_Input_Present_Value_Set(wp_data->object_instance, - (BACNET_BINARY_PV)value.type.Enumerated); - } else { - status = false; - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } + status = + Binary_Input_Present_Value_Write(wp_data->object_instance, + value.type.Enumerated, + &wp_data->error_class, &wp_data->error_code); } break; case PROP_OUT_OF_SERVICE: @@ -534,20 +685,161 @@ bool Binary_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) } } break; - case PROP_OBJECT_IDENTIFIER: - case PROP_OBJECT_NAME: - case PROP_DESCRIPTION: - case PROP_OBJECT_TYPE: - case PROP_STATUS_FLAGS: - case PROP_EVENT_STATE: - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; - break; default: - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + if (property_lists_member( + Binary_Input_Properties_Required, + Binary_Input_Properties_Optional, + Binary_Input_Properties_Proprietary, + wp_data->object_property)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + } break; } return status; } + +/** + * @brief Sets a callback used when present-value is written from BACnet + * @param cb - callback used to provide indications + */ +void Binary_Input_Write_Present_Value_Callback_Set( + binary_input_write_present_value_callback cb) +{ + Binary_Input_Write_Present_Value_Callback = cb; +} + +/** + * @brief Determines a object write-enabled flag state + * @param object_instance - object-instance number of the object + * @return write-enabled status flag + */ +bool Binary_Input_Write_Enabled(uint32_t object_instance) +{ + bool value = false; + struct object_data *pObject; + + pObject = Binary_Input_Object(object_instance); + if (pObject) { + value = pObject->Write_Enabled; + } + + return value; +} + +/** + * @brief For a given object instance-number, sets the write-enabled flag + * @param object_instance - object-instance number of the object + */ +void Binary_Input_Write_Enable(uint32_t object_instance) +{ + struct object_data *pObject; + + pObject = Binary_Input_Object(object_instance); + if (pObject) { + pObject->Write_Enabled = true; + } +} + +/** + * @brief For a given object instance-number, clears the write-enabled flag + * @param object_instance - object-instance number of the object + */ +void Binary_Input_Write_Disable(uint32_t object_instance) +{ + struct object_data *pObject; + + pObject = Binary_Input_Object(object_instance); + if (pObject) { + pObject->Write_Enabled = false; + } +} + +/** + * Creates a Binary Input object + * @param object_instance - object-instance number of the object + */ +uint32_t Binary_Input_Create(uint32_t object_instance) +{ + struct object_data *pObject = NULL; + int index = 0; + + pObject = Binary_Input_Object(object_instance); + if (!pObject) { + pObject = calloc(1, sizeof(struct object_data)); + if (pObject) { + pObject->Object_Name = NULL; + pObject->Description = NULL; + pObject->Reliability = RELIABILITY_NO_FAULT_DETECTED; + pObject->Present_Value = false; + pObject->Out_Of_Service = false; + pObject->Active_Text = Default_Active_Text; + pObject->Inactive_Text = Default_Inactive_Text; + pObject->Change_Of_Value = false; + pObject->Write_Enabled = false; + pObject->Polarity = 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; +} + +/** + * Initializes the Binary Input object data + */ +void Binary_Input_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; + } +} + +/** + * Delete a specific Binary Input object + * @param object_instance - object-instance number of the object + * @return true if the object is deleted + */ +bool Binary_Input_Delete(uint32_t object_instance) +{ + bool status = false; + struct object_data *pObject; + + pObject = Keylist_Data_Delete(Object_List, object_instance); + if (pObject) { + free(pObject); + status = true; + } + + return status; +} + +/** + * Initializes the Binary Input object data + */ +void Binary_Input_Init(void) +{ + if (!Object_List) { + Object_List = Keylist_Create(); + } +} diff --git a/src/bacnet/basic/object/bi.h b/src/bacnet/basic/object/bi.h index 9061a912..12b2e7c1 100644 --- a/src/bacnet/basic/object/bi.h +++ b/src/bacnet/basic/object/bi.h @@ -1,29 +1,15 @@ -/************************************************************************** -* -* Copyright (C) 2006 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -* -*********************************************************************/ -#ifndef BI_H -#define BI_H +/** + * @file + * @author Steve Karg + * @date 2006 + * @brief Binary Input object is an input object with a present-value that + * uses an enumerated two state active/inactive data type. + * @section LICENSE + * Copyright (C) 2006 Steve Karg + * SPDX-License-Identifier: MIT + */ +#ifndef BACNET_BINARY_INPUT_OBJECT_H +#define BACNET_BINARY_INPUT_OBJECT_H #include #include @@ -34,6 +20,16 @@ #include "bacnet/rp.h" #include "bacnet/wp.h" +/** + * @brief Callback for gateway write present value request + * @param object_instance - object-instance number of the object + * @param old_value - binary preset-value prior to write + * @param value - binary preset-value of the write + */ +typedef void (*binary_input_write_present_value_callback)( + uint32_t object_instance, BACNET_BINARY_PV old_value, + BACNET_BINARY_PV value); + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ @@ -100,6 +96,7 @@ extern "C" { bool Binary_Input_Inactive_Text_Set( uint32_t instance, char *new_name); + BACNET_STACK_EXPORT char *Binary_Input_Active_Text( uint32_t instance); @@ -138,11 +135,13 @@ extern "C" { BACNET_STACK_EXPORT int Binary_Input_Read_Property( BACNET_READ_PROPERTY_DATA * rpdata); - BACNET_STACK_EXPORT bool Binary_Input_Write_Property( BACNET_WRITE_PROPERTY_DATA * wp_data); - + BACNET_STACK_EXPORT + void Binary_Input_Write_Present_Value_Callback_Set( + binary_input_write_present_value_callback cb); + BACNET_STACK_EXPORT uint32_t Binary_Input_Create( uint32_t object_instance); diff --git a/src/bacnet/basic/object/bo.c b/src/bacnet/basic/object/bo.c index 86d26610..373276da 100644 --- a/src/bacnet/basic/object/bo.c +++ b/src/bacnet/basic/object/bo.c @@ -79,7 +79,7 @@ static binary_output_write_present_value_callback Binary_Output_Write_Present_Value_Callback; /* These three arrays are used by the ReadPropertyMultiple handler */ -static const int Binary_Output_Properties_Required[] = { PROP_OBJECT_IDENTIFIER, +static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS, PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, PROP_POLARITY, PROP_PRIORITY_ARRAY, PROP_RELINQUISH_DEFAULT, @@ -88,10 +88,10 @@ static const int Binary_Output_Properties_Required[] = { PROP_OBJECT_IDENTIFIER, #endif -1 }; -static const int Binary_Output_Properties_Optional[] = { PROP_RELIABILITY, +static const int Properties_Optional[] = { PROP_RELIABILITY, PROP_DESCRIPTION, PROP_ACTIVE_TEXT, PROP_INACTIVE_TEXT, -1 }; -static const int Binary_Output_Properties_Proprietary[] = { -1 }; +static const int Properties_Proprietary[] = { -1 }; /** * Returns the list of required, optional, and proprietary properties. @@ -108,13 +108,13 @@ void Binary_Output_Property_Lists( const int **pRequired, const int **pOptional, const int **pProprietary) { if (pRequired) { - *pRequired = Binary_Output_Properties_Required; + *pRequired = Properties_Required; } if (pOptional) { - *pOptional = Binary_Output_Properties_Optional; + *pOptional = Properties_Optional; } if (pProprietary) { - *pProprietary = Binary_Output_Properties_Proprietary; + *pProprietary = Properties_Proprietary; } return; @@ -340,7 +340,7 @@ bool Binary_Output_Present_Value_Relinquish( * @brief For a given object instance-number, writes the present-value to the * remote node * @param object_instance - object-instance number of the object - * @param value - floating point analog value + * @param value - present-value * @param priority - priority-array index value 1..16 * @param error_class - the BACnet error class * @param error_code - BACnet Error code @@ -911,11 +911,12 @@ bool Binary_Output_Encode_Value_List( bool status = false; struct object_data *pObject; const bool in_alarm = false; - const bool fault = false; + bool fault = false; const bool overridden = false; pObject = Keylist_Data(Object_List, object_instance); if (pObject) { + fault = Binary_Output_Object_Fault(pObject); status = cov_value_list_encode_enumerated(value_list, pObject->Present_Value, in_alarm, fault, overridden, pObject->Out_Of_Service); @@ -1117,25 +1118,15 @@ bool Binary_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) wp_data->object_instance, value.type.Boolean); } break; - case PROP_OBJECT_IDENTIFIER: - case PROP_OBJECT_TYPE: - case PROP_OBJECT_NAME: - case PROP_STATUS_FLAGS: - case PROP_EVENT_STATE: - case PROP_DESCRIPTION: - case PROP_POLARITY: - case PROP_RELIABILITY: - case PROP_ACTIVE_TEXT: - case PROP_INACTIVE_TEXT: -#if (BACNET_PROTOCOL_REVISION >= 17) - case PROP_CURRENT_COMMAND_PRIORITY: -#endif - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; - break; default: - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + if (property_lists_member(Properties_Required, Properties_Optional, + Properties_Proprietary, wp_data->object_property)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + } break; } /* not using len at this time */ diff --git a/src/bacnet/basic/object/bv.c b/src/bacnet/basic/object/bv.c index 0d1445f7..7384ed9c 100644 --- a/src/bacnet/basic/object/bv.c +++ b/src/bacnet/basic/object/bv.c @@ -1,54 +1,50 @@ -/************************************************************************** - * +/** + * @file + * @author Steve Karg + * @date 2006 + * @brief Binary Value object is an input object with a present-value that + * uses an enumerated two state active/inactive data type. + * @section LICENSE * Copyright (C) 2006 Steve Karg - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - *********************************************************************/ - -/* Binary Output Objects - customize for your use */ - + * SPDX-License-Identifier: MIT + */ #include #include #include +#include +/* BACnet Stack defines - first */ #include "bacnet/bacdef.h" +/* BACnet Stack API */ #include "bacnet/bacdcode.h" #include "bacnet/bacapp.h" #include "bacnet/wp.h" #include "bacnet/rp.h" -#include "bacnet/basic/object/bv.h" #include "bacnet/basic/services.h" +#include "bacnet/basic/sys/keylist.h" +/* me! */ +#include "bacnet/basic/object/bv.h" -#ifndef MAX_BINARY_VALUES -#define MAX_BINARY_VALUES 10 -#endif - -/* When all the priorities are level null, the present value returns */ -/* the Relinquish Default value */ -#define RELINQUISH_DEFAULT BINARY_INACTIVE -/* Here is our Priority Array.*/ -static BACNET_BINARY_PV Binary_Value_Level[MAX_BINARY_VALUES] - [BACNET_MAX_PRIORITY]; -/* Writable out-of-service allows others to play with our Present Value */ -/* without changing the physical output */ -static bool Out_Of_Service[MAX_BINARY_VALUES]; +static const char *Default_Active_Text = "Active"; +static const char *Default_Inactive_Text = "Inactive"; +struct object_data { + bool Out_Of_Service : 1; + bool Change_Of_Value : 1; + bool Present_Value : 1; + bool Write_Enabled : 1; + bool Polarity : 1; + uint8_t Reliability; + const char *Object_Name; + const char *Active_Text; + const char *Inactive_Text; + const char *Description; +}; +/* 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_BINARY_VALUE; +/* callback for present value writes */ +static binary_value_write_present_value_callback + Binary_Value_Write_Present_Value_Callback; /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Binary_Value_Properties_Required[] = { PROP_OBJECT_IDENTIFIER, @@ -56,7 +52,7 @@ static const int Binary_Value_Properties_Required[] = { PROP_OBJECT_IDENTIFIER, PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, -1 }; static const int Binary_Value_Properties_Optional[] = { PROP_DESCRIPTION, - PROP_PRIORITY_ARRAY, PROP_RELINQUISH_DEFAULT, -1 }; + PROP_RELIABILITY, PROP_ACTIVE_TEXT, PROP_INACTIVE_TEXT, -1 }; static const int Binary_Value_Properties_Proprietary[] = { -1 }; @@ -85,39 +81,26 @@ void Binary_Value_Property_Lists( } /** - * Initialize the binary values. + * @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 */ -void Binary_Value_Init(void) +static struct object_data *Binary_Value_Object(uint32_t object_instance) { - unsigned i, j; - static bool initialized = false; - - if (!initialized) { - initialized = true; - - /* initialize all the analog output priority arrays to NULL */ - for (i = 0; i < MAX_BINARY_VALUES; i++) { - for (j = 0; j < BACNET_MAX_PRIORITY; j++) { - Binary_Value_Level[i][j] = BINARY_NULL; - } - } - } - - return; + return Keylist_Data(Object_List, object_instance); } /** - * We simply have 0-n object instances. Yours might be - * more complex, and then you need validate that the - * given instance exists. - * - * @param object_instance Object instance - * - * @return true/false + * @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 Binary_Value_Valid_Instance(uint32_t object_instance) { - if (object_instance < MAX_BINARY_VALUES) { + struct object_data *pObject; + + pObject = Binary_Value_Object(object_instance); + if (pObject) { return true; } @@ -125,47 +108,34 @@ bool Binary_Value_Valid_Instance(uint32_t object_instance) } /** - * Return the count of analog values. - * - * @return Count of binary values. + * @brief Determines the number of objects + * @return Number of objects */ unsigned Binary_Value_Count(void) { - return MAX_BINARY_VALUES; + 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. - * - * @param index Index - * - * @return Object instance + * @brief Determines the object instance-number for a given 0..N index + * of objects where N is the count. + * @param index - 0..count value + * @return object instance-number for the given index */ uint32_t Binary_Value_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 - * - * @param object_instance Object instance - * - * @return Index in the object table. + * @brief For a given object instance-number, determines a 0..N index + * of objects where N is the count. + * @param object_instance - object-instance number of the object + * @return index for the given instance-number, or count if not valid. */ unsigned Binary_Value_Instance_To_Index(uint32_t object_instance) { - unsigned index = MAX_BINARY_VALUES; - - if (object_instance < MAX_BINARY_VALUES) { - index = object_instance; - } - - return index; + return Keylist_Index(Object_List, object_instance); } /** @@ -177,16 +147,17 @@ unsigned Binary_Value_Instance_To_Index(uint32_t object_instance) */ BACNET_BINARY_PV Binary_Value_Present_Value(uint32_t object_instance) { - BACNET_BINARY_PV value = RELINQUISH_DEFAULT; - unsigned index = 0; - unsigned i = 0; + BACNET_BINARY_PV value = BINARY_INACTIVE; + struct object_data *pObject; - index = Binary_Value_Instance_To_Index(object_instance); - if (index < MAX_BINARY_VALUES) { - for (i = 0; i < BACNET_MAX_PRIORITY; i++) { - if (Binary_Value_Level[index][i] != BINARY_NULL) { - value = Binary_Value_Level[index][i]; - break; + pObject = Binary_Value_Object(object_instance); + if (pObject) { + value = pObject->Present_Value; + if (pObject->Polarity != POLARITY_NORMAL) { + if (value == BINARY_INACTIVE) { + value = BINARY_ACTIVE; + } else { + value = BINARY_INACTIVE; } } } @@ -195,168 +166,513 @@ BACNET_BINARY_PV Binary_Value_Present_Value(uint32_t object_instance) } /** - * @brief Encode a BACnetARRAY property element - * @param object_instance [in] BACnet network port object instance number - * @param priority [in] array index requested: - * 0 to N for individual array members - * @param apdu [out] Buffer in which the APDU contents are built, or NULL to - * return the length of buffer if it had been built - * @return The length of the apdu encoded or - * BACNET_STATUS_ERROR for ERROR_CODE_INVALID_ARRAY_INDEX + * @brief For a given object instance-number, checks the present-value for COV + * @param pObject - specific object with valid data + * @param value - floating point analog value */ -static int Binary_Value_Priority_Array_Encode( - uint32_t object_instance, BACNET_ARRAY_INDEX priority, uint8_t *apdu) +static void Binary_Value_Present_Value_COV_Detect( + struct object_data *pObject, BACNET_BINARY_PV value) { - int apdu_len = BACNET_STATUS_ERROR; - BACNET_BINARY_PV value = RELINQUISH_DEFAULT; - unsigned index = 0; - - index = Binary_Value_Instance_To_Index(object_instance); - if ((index < MAX_BINARY_VALUES) && (priority < BACNET_MAX_PRIORITY)) { - if (Binary_Value_Level[index][priority] != BINARY_NULL) { - apdu_len = encode_application_null(apdu); - } else { - value = Binary_Value_Level[index][priority]; - apdu_len = encode_application_enumerated(apdu, value); + if (pObject) { + if (pObject->Present_Value != value) { + pObject->Change_Of_Value = true; } } - - return apdu_len; } /** - * For a given object instance-number, return the name. - * - * Note: the object name must be unique within this device - * - * @param object_instance - object-instance number of the object - * @param object_name - object name/string pointer - * - * @return true/false + * @brief 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 Binary_Value_Object_Name( - uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) +bool Binary_Value_Out_Of_Service(uint32_t object_instance) { - static char text_string[32] = ""; /* okay for single thread */ - bool status = false; + bool value = false; + struct object_data *pObject; - if (object_instance < MAX_BINARY_VALUES) { - sprintf( - text_string, "BINARY VALUE %lu", (unsigned long)object_instance); - status = characterstring_init_ansi(object_name, text_string); + pObject = Binary_Value_Object(object_instance); + if (pObject) { + value = pObject->Out_Of_Service; + } + + return value; +} + +/** + * @brief For a given object instance-number, sets the out-of-service + * property value + * @param object_instance - object-instance number of the object + * @param value - boolean out-of-service value + * @return true if the out-of-service property value was set + */ +void Binary_Value_Out_Of_Service_Set(uint32_t object_instance, bool value) +{ + struct object_data *pObject; + + pObject = Binary_Value_Object(object_instance); + if (pObject) { + if (pObject->Out_Of_Service != value) { + pObject->Out_Of_Service = value; + pObject->Change_Of_Value = true; + } + } + + return; +} + +/** + * @brief For a given object, gets the Fault status flag + * @param object_instance - object-instance number of the object + * @return true the status flag is in Fault + */ +static bool Binary_Value_Object_Fault(struct object_data *pObject) +{ + bool fault = false; + + if (pObject) { + if (pObject->Reliability != RELIABILITY_NO_FAULT_DETECTED) { + fault = true; + } + } + + return fault; +} + +/** + * @brief For a given object instance-number, gets the Fault status flag + * @param object_instance - object-instance number of the object + * @return true the status flag is in Fault + */ +static bool Binary_Value_Fault(uint32_t object_instance) +{ + struct object_data *pObject; + + pObject = Binary_Value_Object(object_instance); + + return Binary_Value_Object_Fault(pObject); +} + +/** + * @brief For a given object instance-number, determines if the COV flag + * has been triggered. + * @param object_instance - object-instance number of the object + * @return true if the COV flag is set + */ +bool Binary_Value_Change_Of_Value(uint32_t object_instance) +{ + bool status = false; + struct object_data *pObject; + + pObject = Binary_Value_Object(object_instance); + if (pObject) { + status = pObject->Change_Of_Value; } return status; } /** - * Return the OOO value, if any. - * - * @param instance Object instance. - * - * @return true/false + * @brief For a given object instance-number, clears the COV flag + * @param object_instance - object-instance number of the object */ -bool Binary_Value_Out_Of_Service(uint32_t instance) +void Binary_Value_Change_Of_Value_Clear(uint32_t object_instance) { - unsigned index = 0; - bool oos_flag = false; + struct object_data *pObject; - index = Binary_Value_Instance_To_Index(instance); - if (index < MAX_BINARY_VALUES) { - oos_flag = Out_Of_Service[index]; + pObject = Binary_Value_Object(object_instance); + if (pObject) { + pObject->Change_Of_Value = false; } - return oos_flag; + return; } /** - * Set the OOO value, if any. - * - * @param instance Object instance. - * @param oos_flag New OOO value. + * @brief For a given object instance-number, loads the value_list with the COV + * data. + * @param object_instance - object-instance number of the object + * @param value_list - list of COV data + * @return true if the value list is encoded */ -void Binary_Value_Out_Of_Service_Set(uint32_t instance, bool oos_flag) +bool Binary_Value_Encode_Value_List( + uint32_t object_instance, BACNET_PROPERTY_VALUE *value_list) { - unsigned index = 0; + bool status = false; + const bool in_alarm = false; + bool out_of_service = false; + bool fault = false; + const bool overridden = false; + BACNET_BINARY_PV present_value = BINARY_INACTIVE; + struct object_data *pObject; - index = Binary_Value_Instance_To_Index(instance); - if (index < MAX_BINARY_VALUES) { - Out_Of_Service[index] = oos_flag; + pObject = Binary_Value_Object(object_instance); + if (pObject) { + if (pObject->Reliability != RELIABILITY_NO_FAULT_DETECTED) { + fault = true; + } + out_of_service = pObject->Out_Of_Service; + if (pObject->Present_Value) { + present_value = BINARY_ACTIVE; + } + status = cov_value_list_encode_enumerated(value_list, present_value, + in_alarm, fault, overridden, out_of_service); } + + return status; } /** - * Return the requested property of the binary value. + * @brief For a given object instance-number, sets the present-value + * @param object_instance - object-instance number of the object + * @param value - enumerated binary present-value + * @return true if values are within range and present-value is set. + */ +bool Binary_Value_Present_Value_Set( + uint32_t object_instance, BACNET_BINARY_PV value) +{ + bool status = false; + struct object_data *pObject; + + pObject = Binary_Value_Object(object_instance); + if (pObject) { + if (value <= MAX_BINARY_PV) { + if (pObject->Polarity != POLARITY_NORMAL) { + if (value == BINARY_INACTIVE) { + value = BINARY_ACTIVE; + } else { + value = BINARY_INACTIVE; + } + } + Binary_Value_Present_Value_COV_Detect(pObject, value); + pObject->Present_Value = true; + status = true; + } + } + + return status; +} + +/** + * For a given object instance-number, sets the present-value * - * @param rpdata Property requested, see for BACNET_READ_PROPERTY_DATA details. + * @param object_instance - object-instance number of the object + * @param value - floating point analog value + * @param error_class - the BACnet error class + * @param error_code - BACnet Error code * - * @return apdu len, or BACNET_STATUS_ERROR on error + * @return true if values are within range and present-value is set. + */ +static bool Binary_Value_Present_Value_Write(uint32_t object_instance, + BACNET_BINARY_PV value, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code) +{ + bool status = false; + struct object_data *pObject; + BACNET_BINARY_PV old_value = BINARY_INACTIVE; + + pObject = Binary_Value_Object(object_instance); + if (pObject) { + if (value <= MAX_BINARY_PV) { + if (pObject->Write_Enabled) { + old_value = pObject->Present_Value; + Binary_Value_Present_Value_COV_Detect(pObject, value); + pObject->Present_Value = value; + if (pObject->Out_Of_Service) { + /* The physical point that the object represents + is not in service. This means that changes to the + Present_Value property are decoupled from the + physical point when the value of Out_Of_Service + is true. */ + } else if (Binary_Value_Write_Present_Value_Callback) { + Binary_Value_Write_Present_Value_Callback( + object_instance, old_value, value); + } + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + + return status; +} + +/** + * @brief Get the object name + * @param object_instance - object-instance number of the object + * @param object_name - holds the object-name to be retrieved + * @return true if object-name was retrieved + */ +bool Binary_Value_Object_Name( + uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + struct object_data *pObject; + + pObject = Binary_Value_Object(object_instance); + if (pObject) { + if (pObject->Object_Name == NULL) { + sprintf(text_string, "BINARY INPUT %lu", + (unsigned long)object_instance); + status = characterstring_init_ansi(object_name, text_string); + } else { + status = + characterstring_init_ansi(object_name, pObject->Object_Name); + } + } + + return status; +} + +/** + * @brief For a given object instance-number, sets the object-name + * @param object_instance - object-instance number of the object + * @param new_name - holds the object-name to be set + * @return true if object-name was set + */ +bool Binary_Value_Name_Set(uint32_t object_instance, char *new_name) +{ + bool status = false; + struct object_data *pObject; + + pObject = Binary_Value_Object(object_instance); + if (pObject) { + if (new_name) { + status = true; + pObject->Object_Name = new_name; + } + } + + return status; +} + +/** + * @brief For a given object instance-number, returns the polarity property. + * @param object_instance - object-instance number of the object + * @return the polarity property of the object. + */ +BACNET_POLARITY Binary_Value_Polarity(uint32_t object_instance) +{ + BACNET_POLARITY polarity = POLARITY_NORMAL; + struct object_data *pObject; + + pObject = Binary_Value_Object(object_instance); + if (pObject) { + polarity = pObject->Polarity; + } + + return polarity; +} + +/** + * @brief For a given object instance-number, sets the polarity property + * @param object_instance - object-instance number of the object + * @param polarity - polarity property value + * @return true if polarity was set + */ +bool Binary_Value_Polarity_Set( + uint32_t object_instance, BACNET_POLARITY polarity) +{ + bool status = false; + struct object_data *pObject; + + pObject = Binary_Value_Object(object_instance); + if (pObject) { + pObject->Polarity = polarity; + } + + return status; +} + +/** + * @brief 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 + */ +char *Binary_Value_Description(uint32_t object_instance) +{ + char *name = NULL; + struct object_data *pObject; + + pObject = Binary_Value_Object(object_instance); + if (pObject) { + if (pObject->Description == NULL) { + name = ""; + } else { + name = (char *)pObject->Description; + } + } + + return name; +} + +/** + * @brief 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 + * @return true if object-name was set + */ +bool Binary_Value_Description_Set(uint32_t object_instance, char *new_name) +{ + bool status = false; /* return value */ + struct object_data *pObject; + + pObject = Binary_Value_Object(object_instance); + if (pObject) { + status = true; + pObject->Description = new_name; + } + + return status; +} + +/** + * For a given object instance-number, returns the active text value + * + * @param object_instance - object-instance number of the object + * + * @return active text or NULL if not found + */ +char *Binary_Value_Active_Text(uint32_t object_instance) +{ + char *name = NULL; + struct object_data *pObject; + + pObject = Binary_Value_Object(object_instance); + if (pObject) { + name = (char *)pObject->Active_Text; + } + + 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 + * + * @return true if object-name was set + */ +bool Binary_Value_Active_Text_Set(uint32_t object_instance, char *new_name) +{ + bool status = false; /* return value */ + struct object_data *pObject; + + pObject = Binary_Value_Object(object_instance); + if (pObject && new_name) { + status = true; + pObject->Active_Text = new_name; + } + + return status; +} + +/** + * For a given object instance-number, returns the active text value + * + * @param object_instance - object-instance number of the object + * + * @return active text or NULL if not found + */ +char *Binary_Value_Inactive_Text(uint32_t object_instance) +{ + char *name = NULL; + struct object_data *pObject; + + pObject = Binary_Value_Object(object_instance); + if (pObject) { + name = (char *)pObject->Inactive_Text; + } + + 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 + * + * @return true if object-name was set + */ +bool Binary_Value_Inactive_Text_Set(uint32_t object_instance, char *new_name) +{ + bool status = false; /* return value */ + struct object_data *pObject; + + pObject = Binary_Value_Object(object_instance); + if (pObject && new_name) { + status = true; + pObject->Inactive_Text = new_name; + } + + return status; +} + +/** + * 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 Binary_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) { int apdu_len = 0; /* return value */ - int apdu_size = 0; BACNET_BIT_STRING bit_string; BACNET_CHARACTER_STRING char_string; - BACNET_BINARY_PV present_value = BINARY_INACTIVE; - unsigned object_index = 0; - bool state = false; uint8_t *apdu = NULL; + bool state = false; - /* Valid data? */ - if (rpdata == NULL) { - return 0; - } - if ((rpdata->application_data == NULL) || + if ((rpdata == NULL) || (rpdata->application_data == NULL) || (rpdata->application_data_len == 0)) { return 0; } - - /* Valid object index? */ - object_index = Binary_Value_Instance_To_Index(rpdata->object_instance); - if (object_index >= MAX_BINARY_VALUES) { - rpdata->error_class = ERROR_CLASS_OBJECT; - rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; - return BACNET_STATUS_ERROR; - } - apdu = rpdata->application_data; - apdu_size = rpdata->application_data_len; switch (rpdata->object_property) { case PROP_OBJECT_IDENTIFIER: apdu_len = encode_application_object_id( - &apdu[0], OBJECT_BINARY_VALUE, rpdata->object_instance); + &apdu[0], Object_Type, rpdata->object_instance); break; - /* note: Name and Description don't have to be the same. - You could make Description writable and different */ case PROP_OBJECT_NAME: - case PROP_DESCRIPTION: - if (Binary_Value_Object_Name( - rpdata->object_instance, &char_string)) { - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - } + /* note: object name must be unique in our device */ + Binary_Value_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_BINARY_VALUE); + apdu_len = encode_application_enumerated(&apdu[0], Object_Type); break; case PROP_PRESENT_VALUE: - present_value = Binary_Value_Present_Value(rpdata->object_instance); - apdu_len = encode_application_enumerated(&apdu[0], present_value); + apdu_len = encode_application_enumerated( + &apdu[0], Binary_Value_Present_Value(rpdata->object_instance)); break; case PROP_STATUS_FLAGS: /* note: see the details in the standard on how to use these */ bitstring_init(&bit_string); bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); - bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + state = Binary_Value_Fault(rpdata->object_instance); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, state); bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); state = Binary_Value_Out_Of_Service(rpdata->object_instance); bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state); apdu_len = encode_application_bitstring(&apdu[0], &bit_string); break; case PROP_EVENT_STATE: - /* note: see the details in the standard on how to use this */ apdu_len = encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); break; @@ -364,21 +680,27 @@ int Binary_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) state = Binary_Value_Out_Of_Service(rpdata->object_instance); apdu_len = encode_application_boolean(&apdu[0], state); break; - case PROP_PRIORITY_ARRAY: - apdu_len = bacnet_array_encode(rpdata->object_instance, - rpdata->array_index, Binary_Value_Priority_Array_Encode, - BACNET_MAX_PRIORITY, apdu, apdu_size); - if (apdu_len == BACNET_STATUS_ABORT) { - rpdata->error_code = - ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; - } else if (apdu_len == BACNET_STATUS_ERROR) { - rpdata->error_class = ERROR_CLASS_PROPERTY; - rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; - } + case PROP_POLARITY: + apdu_len = encode_application_enumerated( + &apdu[0], Binary_Value_Polarity(rpdata->object_instance)); break; - case PROP_RELINQUISH_DEFAULT: - present_value = RELINQUISH_DEFAULT; - apdu_len = encode_application_enumerated(&apdu[0], present_value); + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Binary_Value_Description(rpdata->object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_ACTIVE_TEXT: + characterstring_init_ansi(&char_string, + Binary_Value_Active_Text(rpdata->object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_INACTIVE_TEXT: + characterstring_init_ansi(&char_string, + Binary_Value_Inactive_Text(rpdata->object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); break; default: rpdata->error_class = ERROR_CLASS_PROPERTY; @@ -386,7 +708,6 @@ int Binary_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) apdu_len = BACNET_STATUS_ERROR; break; } - /* Only array properties can have array options. */ if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) && (rpdata->array_index != BACNET_ARRAY_ALL)) { @@ -409,9 +730,6 @@ int Binary_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) { bool status = false; /* return value */ - unsigned int object_index = 0; - unsigned int priority = 0; - BACNET_BINARY_PV level = BINARY_NULL; int len = 0; BACNET_APPLICATION_DATA_VALUE value; @@ -440,69 +758,15 @@ bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; return false; } - - /* Valid object index? */ - object_index = Binary_Value_Instance_To_Index(wp_data->object_instance); - if (object_index >= MAX_BINARY_VALUES) { - wp_data->error_class = ERROR_CLASS_OBJECT; - wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; - return false; - } - switch (wp_data->object_property) { case PROP_PRESENT_VALUE: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED); if (status) { - priority = wp_data->priority; - /* Command priority 6 is reserved for use by Minimum On/Off - algorithm and may not be used for other purposes in any - object. */ - if (priority && (priority <= BACNET_MAX_PRIORITY) && - (priority != 6 /* reserved */) && - (value.type.Enumerated <= MAX_BINARY_PV)) { - level = (BACNET_BINARY_PV)value.type.Enumerated; - priority--; - Binary_Value_Level[object_index][priority] = level; - /* Note: you could set the physical output here if we - are the highest priority. - However, if Out of Service is TRUE, then don't set the - physical output. This comment may apply to the - main loop (i.e. check out of service before changing - output) */ - status = true; - } else if (priority == 6) { - /* Command priority 6 is reserved for use by Minimum On/Off - algorithm and may not be used for other purposes in any - object. */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; - } else { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } - } else { - status = write_property_type_valid( - wp_data, &value, BACNET_APPLICATION_TAG_NULL); - if (status) { - level = BINARY_NULL; - priority = wp_data->priority; - if (priority && (priority <= BACNET_MAX_PRIORITY)) { - priority--; - Binary_Value_Level[object_index][priority] = level; - /* Note: you could set the physical output here to the - next highest priority, or to the relinquish default - if no priorities are set. However, if Out of Service - is TRUE, then don't set the physical output. This - comment may apply to the - main loop (i.e. check out of service before changing - output) */ - } else { - status = false; - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } - } + status = + Binary_Value_Present_Value_Write(wp_data->object_instance, + value.type.Enumerated, + &wp_data->error_class, &wp_data->error_code); } break; case PROP_OUT_OF_SERVICE: @@ -513,22 +777,181 @@ bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) wp_data->object_instance, value.type.Boolean); } break; - case PROP_OBJECT_IDENTIFIER: - case PROP_OBJECT_NAME: - case PROP_DESCRIPTION: - case PROP_OBJECT_TYPE: - case PROP_STATUS_FLAGS: - case PROP_EVENT_STATE: - case PROP_PRIORITY_ARRAY: - case PROP_RELINQUISH_DEFAULT: - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + case PROP_POLARITY: + status = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED); + if (status) { + if (value.type.Enumerated < MAX_POLARITY) { + Binary_Value_Polarity_Set(wp_data->object_instance, + (BACNET_POLARITY)value.type.Enumerated); + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } break; default: - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + if (property_lists_member(Binary_Value_Properties_Required, + Binary_Value_Properties_Optional, + Binary_Value_Properties_Proprietary, + wp_data->object_property)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + } break; } return status; } + +/** + * @brief Sets a callback used when present-value is written from BACnet + * @param cb - callback used to provide indications + */ +void Binary_Value_Write_Present_Value_Callback_Set( + binary_value_write_present_value_callback cb) +{ + Binary_Value_Write_Present_Value_Callback = cb; +} + +/** + * @brief Determines a object write-enabled flag state + * @param object_instance - object-instance number of the object + * @return write-enabled status flag + */ +bool Binary_Value_Write_Enabled(uint32_t object_instance) +{ + bool value = false; + struct object_data *pObject; + + pObject = Binary_Value_Object(object_instance); + if (pObject) { + value = pObject->Write_Enabled; + } + + return value; +} + +/** + * @brief For a given object instance-number, sets the write-enabled flag + * @param object_instance - object-instance number of the object + */ +void Binary_Value_Write_Enable(uint32_t object_instance) +{ + struct object_data *pObject; + + pObject = Binary_Value_Object(object_instance); + if (pObject) { + pObject->Write_Enabled = true; + } +} + +/** + * @brief For a given object instance-number, clears the write-enabled flag + * @param object_instance - object-instance number of the object + */ +void Binary_Value_Write_Disable(uint32_t object_instance) +{ + struct object_data *pObject; + + pObject = Binary_Value_Object(object_instance); + if (pObject) { + pObject->Write_Enabled = false; + } +} + +/** + * @brief Creates a Binary Output object + * @param object_instance - object-instance number of the object + * @return the object-instance that was created, or BACNET_MAX_INSTANCE + */ +uint32_t Binary_Value_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->Description = NULL; + pObject->Reliability = RELIABILITY_NO_FAULT_DETECTED; + pObject->Present_Value = false; + pObject->Out_Of_Service = false; + pObject->Active_Text = Default_Active_Text; + pObject->Inactive_Text = Default_Inactive_Text; + pObject->Change_Of_Value = false; + pObject->Write_Enabled = false; + pObject->Polarity = 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; +} + +/** + * Initializes the Binary Input object data + */ +void Binary_Value_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; + } +} + +/** + * Creates a Binary Input object + */ +bool Binary_Value_Delete(uint32_t object_instance) +{ + bool status = false; + struct object_data *pObject; + + pObject = Keylist_Data_Delete(Object_List, object_instance); + if (pObject) { + free(pObject); + status = true; + } + + return status; +} + +/** + * Initializes the Binary Input object data + */ +void Binary_Value_Init(void) +{ + Object_List = Keylist_Create(); +} diff --git a/src/bacnet/basic/object/bv.h b/src/bacnet/basic/object/bv.h index 51ef4049..119e0ddb 100644 --- a/src/bacnet/basic/object/bv.h +++ b/src/bacnet/basic/object/bv.h @@ -1,30 +1,15 @@ -/************************************************************************** -* -* Copyright (C) 2006 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -* -*********************************************************************/ -#ifndef BV_H -#define BV_H - +/** + * @file + * @author Steve Karg + * @date 2006 + * @brief Binary Value object is an input object with a present-value that + * uses an enumerated two state active/inactive data type. + * @section LICENSE + * Copyright (C) 2006 Steve Karg + * SPDX-License-Identifier: MIT + */ +#ifndef BACNET_BINARY_VALUE_OBJECT_H +#define BACNET_BINARY_VALUE_OBJECT_H #include #include /* BACnet Stack defines - first */ @@ -34,6 +19,16 @@ #include "bacnet/rp.h" #include "bacnet/wp.h" +/** + * @brief Callback for gateway write present value request + * @param object_instance - object-instance number of the object + * @param old_value - binary preset-value prior to write + * @param value - binary preset-value of the write + */ +typedef void (*binary_value_write_present_value_callback)( + uint32_t object_instance, BACNET_BINARY_PV old_value, + BACNET_BINARY_PV value); + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ diff --git a/src/bacnet/basic/object/device.c b/src/bacnet/basic/object/device.c index 1779c543..8369fbef 100644 --- a/src/bacnet/basic/object/device.c +++ b/src/bacnet/basic/object/device.c @@ -1,31 +1,13 @@ -/************************************************************************** - * - * Copyright (C) 2005,2006,2009 Steve Karg - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - *********************************************************************/ - -/** @file device.c Base "class" for handling all BACnet objects belonging - * to a BACnet device, as well as Device-specific properties. */ - +/** + * @file + * @author Steve Karg + * @date 2005 + * @brief Base "class" for handling all BACnet objects belonging + * to a BACnet device, as well as Device-specific properties. + * @section LICENSE + * Copyright (C) 2005 Steve Karg + * SPDX-License-Identifier: MIT + */ #include #include #include @@ -124,13 +106,14 @@ static object_functions_t My_Object_Table[] = { Analog_Input_Encode_Value_List, Analog_Input_Change_Of_Value, Analog_Input_Change_Of_Value_Clear, Analog_Input_Intrinsic_Reporting, NULL /* Add_List_Element */, NULL /* Remove_List_Element */, - NULL /* Create */, NULL /* Delete */, NULL /* Timer */ }, + Analog_Input_Create, Analog_Input_Delete, NULL /* Timer */ }, { OBJECT_ANALOG_OUTPUT, Analog_Output_Init, Analog_Output_Count, Analog_Output_Index_To_Instance, Analog_Output_Valid_Instance, Analog_Output_Object_Name, Analog_Output_Read_Property, Analog_Output_Write_Property, Analog_Output_Property_Lists, - NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* ReadRangeInfo */, NULL /* Iterator */, + Analog_Output_Encode_Value_List, Analog_Output_Change_Of_Value, + Analog_Output_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */, NULL /* Remove_List_Element */, Analog_Output_Create, Analog_Output_Delete, NULL /* Timer */ }, { OBJECT_ANALOG_VALUE, Analog_Value_Init, Analog_Value_Count, @@ -141,7 +124,7 @@ static object_functions_t My_Object_Table[] = { Analog_Value_Encode_Value_List, Analog_Value_Change_Of_Value, Analog_Value_Change_Of_Value_Clear, Analog_Value_Intrinsic_Reporting, NULL /* Add_List_Element */, NULL /* Remove_List_Element */, - NULL /* Create */, NULL /* Delete */, NULL /* Timer */ }, + Analog_Value_Create, Analog_Value_Delete, NULL /* Timer */ }, { OBJECT_BINARY_INPUT, Binary_Input_Init, Binary_Input_Count, Binary_Input_Index_To_Instance, Binary_Input_Valid_Instance, Binary_Input_Object_Name, Binary_Input_Read_Property, @@ -150,23 +133,25 @@ static object_functions_t My_Object_Table[] = { Binary_Input_Encode_Value_List, Binary_Input_Change_Of_Value, Binary_Input_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */, NULL /* Remove_List_Element */, - NULL /* Create */, NULL /* Delete */, NULL /* Timer */ }, + Binary_Input_Create, Binary_Input_Delete, NULL /* Timer */ }, { OBJECT_BINARY_OUTPUT, Binary_Output_Init, Binary_Output_Count, Binary_Output_Index_To_Instance, Binary_Output_Valid_Instance, Binary_Output_Object_Name, Binary_Output_Read_Property, Binary_Output_Write_Property, Binary_Output_Property_Lists, - NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* ReadRangeInfo */, NULL /* Iterator */, + Binary_Output_Encode_Value_List, Binary_Output_Change_Of_Value, + Binary_Output_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */, NULL /* Remove_List_Element */, Binary_Output_Create, Binary_Output_Delete, NULL /* Timer */ }, { OBJECT_BINARY_VALUE, Binary_Value_Init, Binary_Value_Count, Binary_Value_Index_To_Instance, Binary_Value_Valid_Instance, Binary_Value_Object_Name, Binary_Value_Read_Property, Binary_Value_Write_Property, Binary_Value_Property_Lists, - NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* ReadRangeInfo */, NULL /* Iterator */, + Binary_Value_Encode_Value_List, Binary_Value_Change_Of_Value, + Binary_Value_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */, NULL /* Remove_List_Element */, - NULL /* Create */, NULL /* Delete */, NULL /* Timer */ }, + Binary_Value_Create, Binary_Value_Delete, NULL /* Timer */ }, { OBJECT_CALENDAR, Calendar_Init, Calendar_Count, Calendar_Index_To_Instance, Calendar_Valid_Instance, Calendar_Object_Name, Calendar_Read_Property, @@ -242,17 +227,19 @@ static object_functions_t My_Object_Table[] = { Multistate_Input_Index_To_Instance, Multistate_Input_Valid_Instance, Multistate_Input_Object_Name, Multistate_Input_Read_Property, Multistate_Input_Write_Property, Multistate_Input_Property_Lists, - NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* ReadRangeInfo */, NULL /* Iterator */, + Multistate_Input_Encode_Value_List, Multistate_Input_Change_Of_Value, + Multistate_Input_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */, NULL /* Remove_List_Element */, - NULL /* Create */, NULL /* Delete */, NULL /* Timer */ }, + Multistate_Input_Create, Multistate_Input_Delete, NULL /* Timer */ }, { OBJECT_MULTI_STATE_OUTPUT, Multistate_Output_Init, Multistate_Output_Count, Multistate_Output_Index_To_Instance, Multistate_Output_Valid_Instance, Multistate_Output_Object_Name, Multistate_Output_Read_Property, Multistate_Output_Write_Property, Multistate_Output_Property_Lists, NULL /* ReadRangeInfo */, - NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */, - NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Iterator */, + Multistate_Output_Encode_Value_List, Multistate_Output_Change_Of_Value, + Multistate_Output_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */, NULL /* Remove_List_Element */, Multistate_Output_Create, Multistate_Output_Delete, NULL /* Timer */ }, { OBJECT_MULTI_STATE_VALUE, Multistate_Value_Init, Multistate_Value_Count, @@ -263,7 +250,7 @@ static object_functions_t My_Object_Table[] = { Multistate_Value_Encode_Value_List, Multistate_Value_Change_Of_Value, Multistate_Value_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */, NULL /* Remove_List_Element */, - NULL /* Create */, NULL /* Delete */, NULL /* Timer */ }, + Multistate_Value_Create, Multistate_Value_Delete, NULL /* Timer */ }, { OBJECT_TRENDLOG, Trend_Log_Init, Trend_Log_Count, Trend_Log_Index_To_Instance, Trend_Log_Valid_Instance, Trend_Log_Object_Name, Trend_Log_Read_Property, diff --git a/src/bacnet/basic/object/ms-input.c b/src/bacnet/basic/object/ms-input.c index 2328e253..a5f75b02 100644 --- a/src/bacnet/basic/object/ms-input.c +++ b/src/bacnet/basic/object/ms-input.c @@ -1,33 +1,18 @@ -/************************************************************************** - * +/** + * @file + * @author Steve Karg + * @date 2009 + * @brief Multi-State object is an input object with a present-value that + * uses an integer data type with a sequence of 1 to N values. + * @section LICENSE * Copyright (C) 2009 Steve Karg - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - *********************************************************************/ - -/* Multi-state Input Objects */ - + * SPDX-License-Identifier: MIT + */ #include #include #include +#include +#include /* BACnet Stack defines - first */ #include "bacnet/bacdef.h" /* BACnet Stack API */ @@ -35,27 +20,31 @@ #include "bacnet/bacapp.h" #include "bacnet/rp.h" #include "bacnet/wp.h" -#include "bacnet/basic/object/device.h" -#include "bacnet/basic/object/ms-input.h" +#include "bacnet/basic/sys/keylist.h" #include "bacnet/basic/services.h" +/* me! */ +#include "bacnet/basic/object/ms-input.h" -/* number of demo objects */ -#ifndef MAX_MULTISTATE_INPUTS -#define MAX_MULTISTATE_INPUTS 4 -#endif - -/* how many states? 1 to 254 states - 0 is not allowed. */ -#ifndef MULTISTATE_NUMBER_OF_STATES -#define MULTISTATE_NUMBER_OF_STATES (254) -#endif - -/* Here is our Present Value */ -static uint8_t Present_Value[MAX_MULTISTATE_INPUTS]; -/* Writable out-of-service allows others to manipulate our Present Value */ -static bool Out_Of_Service[MAX_MULTISTATE_INPUTS]; -static char Object_Name[MAX_MULTISTATE_INPUTS][64]; -static char Object_Description[MAX_MULTISTATE_INPUTS][64]; -static char State_Text[MAX_MULTISTATE_INPUTS][MULTISTATE_NUMBER_OF_STATES][64]; +struct object_data { + bool Out_Of_Service : 1; + bool Change_Of_Value : 1; + bool Write_Enabled : 1; + uint8_t Present_Value; + uint8_t Reliability; + const char *Object_Name; + /* The state text functions expect a list of C strings separated by '\0' */ + const char *State_Text; + const char *Description; +}; +/* 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_MULTI_STATE_INPUT; +/* callback for present value writes */ +static multistate_input_write_present_value_callback + Multistate_Input_Write_Present_Value_Callback; +/* default state text when none is specified */ +static const char *Default_State_Text = "State 1\0" "State 2\0" "State 3\0" ; /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER, @@ -67,6 +56,14 @@ static const int Properties_Optional[] = { PROP_DESCRIPTION, PROP_STATE_TEXT, static const int Properties_Proprietary[] = { -1 }; +/** + * Initialize the pointers for the required, the optional and the properitary + * value properties. + * + * @param pRequired - Pointer to the pointer of required values. + * @param pOptional - Pointer to the pointer of optional values. + * @param pProprietary - Pointer to the pointer of properitary values. + */ void Multistate_Input_Property_Lists( const int **pRequired, const int **pOptional, const int **pProprietary) { @@ -83,90 +80,271 @@ void Multistate_Input_Property_Lists( return; } -void Multistate_Input_Init(void) +/** + * @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 *Multistate_Input_Object(uint32_t object_instance) { - unsigned i; - - /* initialize all the analog output priority arrays to NULL */ - for (i = 0; i < MAX_MULTISTATE_INPUTS; i++) { - Present_Value[i] = 1; - sprintf(&Object_Name[i][0], "MULTISTATE INPUT %u", i); - sprintf(&Object_Description[i][0], "MULTISTATE INPUT %u", i); - } - - return; + return Keylist_Data(Object_List, object_instance); } -/* 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 Multistate objects where N is count. + * @param object_instance - object-instance number of the object + * @return index for the given instance-number, or count (object not found) + */ unsigned Multistate_Input_Instance_To_Index(uint32_t object_instance) { - unsigned index = MAX_MULTISTATE_INPUTS; - - if (object_instance < MAX_MULTISTATE_INPUTS) { - index = object_instance; - } - - return index; + return Keylist_Index(Object_List, object_instance); } -/* 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 the object instance-number for a given 0..N index + * of Multistate Input objects where N is Multistate_Input_Count(). + * @param index - 0..Multistate_Input_Count() value + * @return object instance-number for the given index + */ uint32_t Multistate_Input_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 count how many you have */ +/** + * @brief Determines the number of Multistate Input objects + * @return Number of Multistate Input objects + */ unsigned Multistate_Input_Count(void) { - return MAX_MULTISTATE_INPUTS; + return Keylist_Count(Object_List); } +/** + * @brief Determines if a given Multistate Input instance is valid + * @param object_instance - object-instance number of the object + * @return true if the instance is valid, and false if not + */ bool Multistate_Input_Valid_Instance(uint32_t object_instance) { - unsigned index = 0; /* offset from instance lookup */ + struct object_data *pObject; - index = Multistate_Input_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_INPUTS) { + pObject = Multistate_Input_Object(object_instance); + if (pObject) { return true; } return false; } -static uint32_t Multistate_Input_Max_States(uint32_t instance) +/** + * @brief Count the number of states + * @param state_names - string of null-terminated state names + * @return number of states + */ +static unsigned state_name_count(const char *state_names) { - (void)instance; - return MULTISTATE_NUMBER_OF_STATES; + unsigned count = 0; + int len = 0; + + if (state_names) { + do { + len = strlen(state_names); + if (len > 0) { + count++; + state_names = state_names + len + 1; + } + } while (len > 0); + } + + return count; } +/** + * @brief Get the specific state name at index 0..N + * @param state_names - string of null-terminated state names + * @param state_index - state index number 1..N of the state names + * @return state name, or NULL + */ +static const char *state_name_by_index(const char *state_names, unsigned index) +{ + unsigned count = 0; + int len = 0; + + if (state_names) { + do { + len = strlen(state_names); + if (len > 0) { + count++; + if (index == count) { + return state_names; + } + state_names = state_names + len + 1; + } + } while (len > 0); + } + + return NULL; +} + +/** + * @brief For a given object instance-number, determines number of states + * @param object_instance - object-instance number of the object + * @return number of states 1..N + */ +static uint32_t Multistate_Input_Max_States(uint32_t object_instance) +{ + uint32_t count = 0; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + count = state_name_count(pObject->State_Text); + } + + return count; +} + +/** + * @brief For a given object instance-number, returns the state-text in + * a C string. + * @param object_instance - object-instance number of the object + * @param state_index - state index number 1..N of the text requested + * @return C string retrieved + */ +char *Multistate_Input_State_Text( + uint32_t object_instance, uint32_t state_index) +{ + char *pName = NULL; /* return value */ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (state_index > 0) { + pName = (char *)state_name_by_index(pObject->State_Text, + state_index); + } + } + + return pName; +} + +/** + * @brief Encode a BACnetARRAY property element + * @param object_instance [in] BACnet network port object instance number + * @param index [in] array index requested: + * 0 to N for individual array members + * @param apdu [out] Buffer in which the APDU contents are built, or NULL to + * return the length of buffer if it had been built + * @return The length of the apdu encoded or + * BACNET_STATUS_ERROR for ERROR_CODE_INVALID_ARRAY_INDEX + */ +static int Multistate_Input_State_Text_Encode( + uint32_t object_instance, BACNET_ARRAY_INDEX index, uint8_t *apdu) +{ + int apdu_len = BACNET_STATUS_ERROR; + char *pName = NULL; /* return value */ + BACNET_CHARACTER_STRING char_string = { 0 }; + uint32_t state_index = 1; + + state_index += index; + pName = Multistate_Input_State_Text(object_instance, state_index); + if (pName) { + characterstring_init_ansi(&char_string, pName); + apdu_len = encode_application_character_string( + apdu, &char_string); + } + + return apdu_len; +} + +/** + * @brief For a given object instance-number, sets the list of state-text from + * a C string array. The state_text_list consists of C strings separated + * by '\0'. For example: + * {@code + * static const char *baud_rate_names = { + * "9600\0" + * "19200\0" + * "38400\0" + * "57600\0" + * "76800\0" + * "115200\0" + * }; + * + * @param object_instance - object-instance number of the object + * @param state_text_list - array of state names to use in this object + * @return true if the state text was set + */ +bool Multistate_Input_State_Text_List_Set( + uint32_t object_instance, + const char *state_text_list) +{ + bool status = false; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + pObject->State_Text = state_text_list; + status = true; + } + + return status; +} + +/** + * @brief For a given object instance-number, determines the present-value + * @param object_instance - object-instance number of the object + * @return present-value 1..N of the object + */ uint32_t Multistate_Input_Present_Value(uint32_t object_instance) { uint32_t value = 1; - unsigned index = 0; /* offset from instance lookup */ + struct object_data *pObject; - index = Multistate_Input_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_INPUTS) { - value = Present_Value[index]; + pObject = Multistate_Input_Object(object_instance); + if (pObject) { + value = pObject->Present_Value; } return value; } +/** + * @brief For a given object instance-number, checks the present-value for COV + * @param pObject - specific object with valid data + * @param value - floating point analog value + */ +static void Multistate_Input_Present_Value_COV_Detect( + struct object_data *pObject, uint32_t value) +{ + if (pObject) { + if (pObject->Present_Value != value) { + pObject->Change_Of_Value = true; + } + } +} + +/** + * @brief For a given object instance-number, sets the present-value + * @param object_instance - object-instance number of the object + * @param value - integer multi-state value 1..N + * @return true if values are within range and present-value is set. + */ bool Multistate_Input_Present_Value_Set( uint32_t object_instance, uint32_t value) { bool status = false; - unsigned index = 0; /* offset from instance lookup */ + struct object_data *pObject; + unsigned max_states = 0; - index = Multistate_Input_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_INPUTS) { - if ((value > 0) && (value <= MULTISTATE_NUMBER_OF_STATES)) { - Present_Value[index] = (uint8_t)value; + pObject = Multistate_Input_Object(object_instance); + if (pObject) { + max_states = state_name_count(pObject->State_Text); + if ((value >= 1) && (value <= max_states)) { + Multistate_Input_Present_Value_COV_Detect(pObject, value); + pObject->Present_Value = value; status = true; } } @@ -174,280 +352,337 @@ bool Multistate_Input_Present_Value_Set( return status; } +/** + * For a given object instance-number, sets the present-value + * + * @param object_instance - object-instance number of the object + * @param value - floating point analog value + * @param error_class - the BACnet error class + * @param error_code - BACnet Error code + * + * @return true if values are within range and present-value is set. + */ +static bool Multistate_Input_Present_Value_Write( + uint32_t object_instance, uint32_t value, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code) +{ + bool status = false; + struct object_data *pObject; + uint32_t old_value = 1; + + pObject = Multistate_Input_Object(object_instance); + if (pObject) { + if (value <= UINT32_MAX) { + if (pObject->Write_Enabled) { + old_value = pObject->Present_Value; + Multistate_Input_Present_Value_COV_Detect(pObject, value); + pObject->Present_Value = value; + if (pObject->Out_Of_Service) { + /* The physical point that the object represents + is not in service. This means that changes to the + Present_Value property are decoupled from the + physical point when the value of Out_Of_Service + is true. */ + } else if (Multistate_Input_Write_Present_Value_Callback) { + Multistate_Input_Write_Present_Value_Callback( + object_instance, old_value, value); + } + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + + return status; +} + +/** + * @brief For a given object instance-number, determines the + * out-of-service state + * @param object_instance - object-instance number of the object + * @return out-of-service state of the object + */ bool Multistate_Input_Out_Of_Service(uint32_t object_instance) { bool value = false; - unsigned index = 0; + struct object_data *pObject; - index = Multistate_Input_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_INPUTS) { - value = Out_Of_Service[index]; + pObject = Multistate_Input_Object(object_instance); + if (pObject) { + value = pObject->Out_Of_Service; } return value; } +/** + * @brief For a given object instance-number, sets the out-of-service state + * @param object_instance - object-instance number of the object + * @param value - out-of-service state + */ void Multistate_Input_Out_Of_Service_Set(uint32_t object_instance, bool value) { - unsigned index = 0; + struct object_data *pObject; - index = Multistate_Input_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_INPUTS) { - Out_Of_Service[index] = value; + pObject = Multistate_Input_Object(object_instance); + if (pObject) { + pObject->Out_Of_Service = value; + pObject->Change_Of_Value = true; } return; } -char *Multistate_Input_Description(uint32_t object_instance) -{ - unsigned index = 0; /* offset from instance lookup */ - char *pName = NULL; /* return value */ - - index = Multistate_Input_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_INPUTS) { - pName = Object_Description[index]; - } - - return pName; -} - -bool Multistate_Input_Description_Set(uint32_t object_instance, char *new_name) -{ - unsigned index = 0; /* offset from instance lookup */ - size_t i = 0; /* loop counter */ - bool status = false; /* return value */ - - index = Multistate_Input_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_INPUTS) { - status = true; - if (new_name) { - for (i = 0; i < sizeof(Object_Description[index]); i++) { - Object_Description[index][i] = new_name[i]; - if (new_name[i] == 0) { - break; - } - } - } else { - for (i = 0; i < sizeof(Object_Description[index]); i++) { - Object_Description[index][i] = 0; - } - } - } - - return status; -} - -static bool Multistate_Input_Description_Write(uint32_t object_instance, - BACNET_CHARACTER_STRING *char_string, - BACNET_ERROR_CLASS *error_class, - BACNET_ERROR_CODE *error_code) -{ - unsigned index = 0; /* offset from instance lookup */ - size_t length = 0; - uint8_t encoding = 0; - bool status = false; /* return value */ - - index = Multistate_Input_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_INPUTS) { - length = characterstring_length(char_string); - if (length <= sizeof(Object_Description[index])) { - encoding = characterstring_encoding(char_string); - if (encoding == CHARACTER_UTF8) { - status = characterstring_ansi_copy(Object_Description[index], - sizeof(Object_Description[index]), char_string); - if (!status) { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; - } - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; - } - } - - return status; -} - +/** + * @brief 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 Multistate_Input_Object_Name( uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) { - unsigned index = 0; /* offset from instance lookup */ bool status = false; + struct object_data *pObject; + char name_text[32]; - index = Multistate_Input_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_INPUTS) { - status = characterstring_init_ansi(object_name, Object_Name[index]); + pObject = Multistate_Input_Object(object_instance); + if (pObject) { + if (pObject->Object_Name) { + status = characterstring_init_ansi(object_name, + pObject->Object_Name); + } else { + snprintf(name_text, sizeof(name_text), "MULTI-STATE INPUT %u", + object_instance); + status = characterstring_init_ansi(object_name, name_text); + } } return status; } -/* note: the object name must be unique within this device */ +/** + * @brief For a given object instance-number, sets the object-name + * Note that the object name must be unique within this device. + * @param object_instance - object-instance number of the object + * @param new_name - holds the object-name to be set + * @return true if object-name was set + */ bool Multistate_Input_Name_Set(uint32_t object_instance, char *new_name) { - unsigned index = 0; /* offset from instance lookup */ - size_t i = 0; /* loop counter */ bool status = false; /* return value */ + struct object_data *pObject; - index = Multistate_Input_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_INPUTS) { + pObject = Multistate_Input_Object(object_instance); + if (pObject && new_name) { status = true; - /* FIXME: check to see if there is a matching name */ - if (new_name) { - for (i = 0; i < sizeof(Object_Name[index]); i++) { - Object_Name[index][i] = new_name[i]; - if (new_name[i] == 0) { - break; - } - } - } else { - for (i = 0; i < sizeof(Object_Name[index]); i++) { - Object_Name[index][i] = 0; + pObject->Object_Name = new_name; + } + + return status; +} + +/** + * @brief For a given object instance-number, gets the reliability. + * @param object_instance - object-instance number of the object + * @return reliability value + */ +BACNET_RELIABILITY Multistate_Input_Reliability(uint32_t object_instance) +{ + BACNET_RELIABILITY reliability = RELIABILITY_NO_FAULT_DETECTED; + struct object_data *pObject; + + pObject = Multistate_Input_Object(object_instance); + if (pObject) { + reliability = (BACNET_RELIABILITY)pObject->Reliability; + } + + return reliability; +} + +/** + * @brief For a given object instance-number, gets the Fault status flag + * @param object_instance - object-instance number of the object + * @return true the status flag is in Fault + */ +static bool Multistate_Input_Object_Fault(struct object_data *pObject) +{ + bool fault = false; + + if (pObject) { + if (pObject->Reliability != RELIABILITY_NO_FAULT_DETECTED) { + fault = true; + } + } + + return fault; +} + +/** + * @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 Multistate_Input_Reliability_Set( + uint32_t object_instance, BACNET_RELIABILITY value) +{ + struct object_data *pObject; + bool status = false; + bool fault = false; + + pObject = Multistate_Input_Object(object_instance); + if (pObject) { + if (value <= RELIABILITY_PROPRIETARY_MAX) { + fault = Multistate_Input_Object_Fault(pObject); + pObject->Reliability = value; + if (fault != Multistate_Input_Object_Fault(pObject)) { + pObject->Change_Of_Value = true; } + status = true; } } return status; } -static bool Multistate_Input_Object_Name_Write(uint32_t object_instance, - BACNET_CHARACTER_STRING *char_string, - BACNET_ERROR_CLASS *error_class, - BACNET_ERROR_CODE *error_code) +/** + * @brief For a given object instance-number, gets the Fault status flag + * @param object_instance - object-instance number of the object + * @return true the status flag is in Fault + */ +static bool Multistate_Input_Fault(uint32_t object_instance) { - unsigned index = 0; /* offset from instance lookup */ - size_t length = 0; - uint8_t encoding = 0; - bool status = false; /* return value */ + struct object_data *pObject; - index = Multistate_Input_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_INPUTS) { - length = characterstring_length(char_string); - if (length <= sizeof(Object_Name[index])) { - encoding = characterstring_encoding(char_string); - if (encoding == CHARACTER_UTF8) { - status = characterstring_ansi_copy(Object_Name[index], - sizeof(Object_Name[index]), char_string); - if (!status) { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; - } - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; - } - } + pObject = Multistate_Input_Object(object_instance); - return status; + return Multistate_Input_Object_Fault(pObject); } -char *Multistate_Input_State_Text( - uint32_t object_instance, uint32_t state_index) +/** + * @brief 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 + */ +char *Multistate_Input_Description(uint32_t object_instance) { - unsigned index = 0; /* offset from instance lookup */ - char *pName = NULL; /* return value */ + char *name = NULL; + struct object_data *pObject; - index = Multistate_Input_Instance_To_Index(object_instance); - if ((index < MAX_MULTISTATE_INPUTS) && (state_index > 0) && - (state_index <= MULTISTATE_NUMBER_OF_STATES)) { - state_index--; - pName = State_Text[index][state_index]; + pObject = Multistate_Input_Object(object_instance); + if (pObject) { + name = (char *)pObject->Description; } - return pName; + return name; } -/* note: the object name must be unique within this device */ -bool Multistate_Input_State_Text_Set( - uint32_t object_instance, uint32_t state_index, char *new_name) +/** + * @brief 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 + * @return true if object-name was set + */ +bool Multistate_Input_Description_Set(uint32_t object_instance, char *new_name) { - unsigned index = 0; /* offset from instance lookup */ - size_t i = 0; /* loop counter */ bool status = false; /* return value */ + struct object_data *pObject; - index = Multistate_Input_Instance_To_Index(object_instance); - if ((index < MAX_MULTISTATE_INPUTS) && (state_index > 0) && - (state_index <= MULTISTATE_NUMBER_OF_STATES)) { - state_index--; + pObject = Multistate_Input_Object(object_instance); + if (pObject && new_name) { status = true; - if (new_name) { - for (i = 0; i < sizeof(State_Text[index][state_index]); i++) { - State_Text[index][state_index][i] = new_name[i]; - if (new_name[i] == 0) { - break; - } - } - } else { - for (i = 0; i < sizeof(State_Text[index][state_index]); i++) { - State_Text[index][state_index][i] = 0; - } - } + pObject->Description = new_name; } return status; - ; } -static bool Multistate_Input_State_Text_Write(uint32_t object_instance, - uint32_t state_index, - BACNET_CHARACTER_STRING *char_string, - BACNET_ERROR_CLASS *error_class, - BACNET_ERROR_CODE *error_code) +/** + * @brief Get the COV change flag status + * @param object_instance - object-instance number of the object + * @return the COV change flag status + */ +bool Multistate_Input_Change_Of_Value(uint32_t object_instance) { - unsigned index = 0; /* offset from instance lookup */ - size_t length = 0; - uint8_t encoding = 0; - bool status = false; /* return value */ + bool changed = false; - index = Multistate_Input_Instance_To_Index(object_instance); - if ((index < MAX_MULTISTATE_INPUTS) && (state_index > 0) && - (state_index <= Multistate_Input_Max_States(object_instance))) { - state_index--; - length = characterstring_length(char_string); - if (length <= sizeof(State_Text[index][state_index])) { - encoding = characterstring_encoding(char_string); - if (encoding == CHARACTER_UTF8) { - status = - characterstring_ansi_copy(State_Text[index][state_index], - sizeof(State_Text[index][state_index]), char_string); - if (!status) { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; - } - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; - } - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + struct object_data *pObject; + + pObject = Multistate_Input_Object(object_instance); + if (pObject) { + changed = pObject->Change_Of_Value; } + return changed; +} + +/** + * @brief Clear the COV change flag + * @param object_instance - object-instance number of the object + */ +void Multistate_Input_Change_Of_Value_Clear(uint32_t object_instance) +{ + struct object_data *pObject; + + pObject = Multistate_Input_Object(object_instance); + if (pObject) { + pObject->Change_Of_Value = false; + } +} + +/** + * @brief Encode the Value List for Present-Value and Status-Flags + * @param object_instance - object-instance number of the object + * @param value_list - #BACNET_PROPERTY_VALUE with at least 2 entries + * @return true if values were encoded + */ +bool Multistate_Input_Encode_Value_List( + uint32_t object_instance, BACNET_PROPERTY_VALUE *value_list) +{ + bool status = false; + struct object_data *pObject; + const bool in_alarm = false; + bool fault = false; + const bool overridden = false; + + pObject = Multistate_Input_Object(object_instance); + if (pObject) { + fault = Multistate_Input_Object_Fault(pObject); + status = + cov_value_list_encode_unsigned(value_list, pObject->Present_Value, + in_alarm, fault, overridden, pObject->Out_Of_Service); + } return status; } -/* return apdu len, or BACNET_STATUS_ERROR on error */ +/** + * @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 Multistate_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) { - int len = 0; int apdu_len = 0; /* return value */ + int apdu_size = 0; BACNET_BIT_STRING bit_string; BACNET_CHARACTER_STRING char_string; uint32_t present_value = 0; - unsigned i = 0; uint32_t max_states = 0; bool state = false; uint8_t *apdu = NULL; @@ -457,27 +692,20 @@ int Multistate_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) return 0; } apdu = rpdata->application_data; + apdu_size = rpdata->application_data_len; switch (rpdata->object_property) { case PROP_OBJECT_IDENTIFIER: apdu_len = encode_application_object_id( - &apdu[0], OBJECT_MULTI_STATE_INPUT, rpdata->object_instance); + &apdu[0], Object_Type, rpdata->object_instance); break; - /* note: Name and Description don't have to be the same. - You could make Description writable and different */ case PROP_OBJECT_NAME: Multistate_Input_Object_Name(rpdata->object_instance, &char_string); apdu_len = encode_application_character_string(&apdu[0], &char_string); break; - case PROP_DESCRIPTION: - characterstring_init_ansi(&char_string, - Multistate_Input_Description(rpdata->object_instance)); - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; case PROP_OBJECT_TYPE: apdu_len = encode_application_enumerated( - &apdu[0], OBJECT_MULTI_STATE_INPUT); + &apdu[0], Object_Type); break; case PROP_PRESENT_VALUE: present_value = @@ -488,17 +716,17 @@ int Multistate_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) /* note: see the details in the standard on how to use these */ bitstring_init(&bit_string); bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); - bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + state = Multistate_Input_Fault(rpdata->object_instance); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, state); bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); - if (Multistate_Input_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); - } + state = Multistate_Input_Out_Of_Service(rpdata->object_instance); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state); apdu_len = encode_application_bitstring(&apdu[0], &bit_string); break; + case PROP_RELIABILITY: + apdu_len = encode_application_enumerated(&apdu[0], + Multistate_Input_Reliability(rpdata->object_instance)); + break; case PROP_EVENT_STATE: /* note: see the details in the standard on how to use this */ apdu_len = @@ -513,49 +741,24 @@ int Multistate_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) Multistate_Input_Max_States(rpdata->object_instance)); break; case PROP_STATE_TEXT: - if (rpdata->array_index == 0) { - /* Array element zero is the number of elements in the array */ - apdu_len = encode_application_unsigned(&apdu[0], - Multistate_Input_Max_States(rpdata->object_instance)); - } else if (rpdata->array_index == BACNET_ARRAY_ALL) { - /* if no index was specified, then try to encode the entire list - */ - /* into one packet. */ - max_states = - Multistate_Input_Max_States(rpdata->object_instance); - for (i = 1; i <= max_states; i++) { - characterstring_init_ansi(&char_string, - Multistate_Input_State_Text( - rpdata->object_instance, i)); - /* FIXME: this might go beyond MAX_APDU length! */ - len = encode_application_character_string( - &apdu[apdu_len], &char_string); - /* add it if we have room */ - if ((apdu_len + len) < MAX_APDU) { - apdu_len += len; - } else { - rpdata->error_code = - ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; - apdu_len = BACNET_STATUS_ABORT; - break; - } - } - } else { - max_states = - Multistate_Input_Max_States(rpdata->object_instance); - if (rpdata->array_index <= max_states) { - characterstring_init_ansi(&char_string, - Multistate_Input_State_Text( - rpdata->object_instance, rpdata->array_index)); - apdu_len = encode_application_character_string( - &apdu[0], &char_string); - } else { - rpdata->error_class = ERROR_CLASS_PROPERTY; - rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; - apdu_len = BACNET_STATUS_ERROR; - } + max_states = Multistate_Input_Max_States(rpdata->object_instance); + apdu_len = bacnet_array_encode(rpdata->object_instance, + rpdata->array_index, Multistate_Input_State_Text_Encode, + max_states, apdu, apdu_size); + if (apdu_len == BACNET_STATUS_ABORT) { + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + } else if (apdu_len == BACNET_STATUS_ERROR) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; } break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Multistate_Input_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; @@ -573,17 +776,18 @@ int Multistate_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) return apdu_len; } -/* returns true if successful */ +/** + * @brief WriteProperty handler for this object. For the given WriteProperty + * data, the application_data is loaded or the error flags are set. + * @param wp_data - BACNET_WRITE_PROPERTY_DATA data, including + * requested data and space for the reply, or error response. + * @return false if an error is loaded, true if no errors + */ bool Multistate_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) { bool status = false; /* return value */ int len = 0; - int element_len = 0; BACNET_APPLICATION_DATA_VALUE value; - uint32_t max_states = 0; - uint32_t array_index = 0; - BACNET_OBJECT_TYPE object_type = OBJECT_NONE; - uint32_t object_instance = 0; /* decode the first chunk of the request */ len = bacapp_decode_application_data( @@ -603,48 +807,14 @@ bool Multistate_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) return false; } switch (wp_data->object_property) { - case PROP_OBJECT_NAME: - status = write_property_type_valid( - wp_data, &value, BACNET_APPLICATION_TAG_CHARACTER_STRING); - if (status) { - /* All the object names in a device must be unique */ - if (Device_Valid_Object_Name(&value.type.Character_String, - &object_type, &object_instance)) { - if ((object_type == wp_data->object_type) && - (object_instance == wp_data->object_instance)) { - /* writing same name to same object */ - status = true; - } else { - status = false; - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_DUPLICATE_NAME; - } - } else { - status = Multistate_Input_Object_Name_Write( - wp_data->object_instance, &value.type.Character_String, - &wp_data->error_class, &wp_data->error_code); - } - } - break; - case PROP_DESCRIPTION: - status = write_property_type_valid( - wp_data, &value, BACNET_APPLICATION_TAG_CHARACTER_STRING); - if (status) { - status = Multistate_Input_Description_Write( - wp_data->object_instance, &value.type.Character_String, - &wp_data->error_class, &wp_data->error_code); - } - break; case PROP_PRESENT_VALUE: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT); if (status) { - status = Multistate_Input_Present_Value_Set( - wp_data->object_instance, value.type.Unsigned_Int); - if (!status) { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } + status = + Multistate_Input_Present_Value_Write(wp_data->object_instance, + value.type.Enumerated, + &wp_data->error_class, &wp_data->error_code); } break; case PROP_OUT_OF_SERVICE: @@ -655,75 +825,120 @@ bool Multistate_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) wp_data->object_instance, value.type.Boolean); } break; - case PROP_STATE_TEXT: - if (wp_data->array_index == 0) { - /* Array element zero is the number of - elements in the array. We have a fixed - size array, so we are read-only. */ + default: + if (property_lists_member( + Properties_Required, + Properties_Optional, + Properties_Proprietary, + wp_data->object_property)) { wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; - } else if (wp_data->array_index == BACNET_ARRAY_ALL) { - max_states = - Multistate_Input_Max_States(wp_data->object_instance); - array_index = 1; - element_len = len; - do { - status = write_property_type_valid(wp_data, &value, - BACNET_APPLICATION_TAG_CHARACTER_STRING); - if (!status) { - break; - } - if (element_len) { - status = Multistate_Input_State_Text_Write( - wp_data->object_instance, array_index, - &value.type.Character_String, &wp_data->error_class, - &wp_data->error_code); - } - max_states--; - array_index++; - if (max_states) { - element_len = bacapp_decode_application_data( - &wp_data->application_data[len], - wp_data->application_data_len - len, &value); - if (element_len < 0) { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - break; - } - len += element_len; - } - } while (max_states); } else { - max_states = - Multistate_Input_Max_States(wp_data->object_instance); - if (wp_data->array_index <= max_states) { - status = write_property_type_valid(wp_data, &value, - BACNET_APPLICATION_TAG_CHARACTER_STRING); - if (status) { - status = Multistate_Input_State_Text_Write( - wp_data->object_instance, wp_data->array_index, - &value.type.Character_String, &wp_data->error_class, - &wp_data->error_code); - } - } else { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; } break; - case PROP_OBJECT_IDENTIFIER: - case PROP_OBJECT_TYPE: - case PROP_STATUS_FLAGS: - case PROP_EVENT_STATE: - case PROP_NUMBER_OF_STATES: - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; - break; - default: - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; - break; } return status; } + +/** + * @brief Sets a callback used when present-value is written from BACnet + * @param cb - callback used to provide indications + */ +void Multistate_Input_Write_Present_Value_Callback_Set( + multistate_input_write_present_value_callback cb) +{ + Multistate_Input_Write_Present_Value_Callback = cb; +} + +/** + * @brief Creates a new object and adds it to the object list + * @param object_instance - object-instance number of the object + * @return the object-instance that was created, or BACNET_MAX_INSTANCE + */ +uint32_t Multistate_Input_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->State_Text = Default_State_Text; + pObject->Out_Of_Service = false; + pObject->Reliability = RELIABILITY_NO_FAULT_DETECTED; + pObject->Change_Of_Value = false; + pObject->Present_Value = 1; + /* 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 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 Multistate_Input_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 Cleans up the object list and its data + */ +void Multistate_Input_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 list + */ +void Multistate_Input_Init(void) +{ + Object_List = Keylist_Create(); +} diff --git a/src/bacnet/basic/object/ms-input.h b/src/bacnet/basic/object/ms-input.h index 370e4129..57b3302a 100644 --- a/src/bacnet/basic/object/ms-input.h +++ b/src/bacnet/basic/object/ms-input.h @@ -1,29 +1,15 @@ -/************************************************************************** -* -* Copyright (C) 2005 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -* -*********************************************************************/ -#ifndef MS_INPUT_H -#define MS_INPUT_H +/** + * @file + * @author Steve Karg + * @date 2009 + * @brief Multi-State object is an input object with a present-value that + * uses an integer data type with a sequence of 1 to N values. + * @section LICENSE + * Copyright (C) 2009 Steve Karg + * SPDX-License-Identifier: MIT + */ +#ifndef BACNET_MULTI_STATE_INPUT_H +#define BACNET_MULTI_STATE_INPUT_H #include #include @@ -34,6 +20,16 @@ #include "bacnet/rp.h" #include "bacnet/wp.h" +/** + * @brief Callback for gateway write present value request + * @param object_instance - object-instance number of the object + * @param old_value - multistate preset-value prior to write + * @param value - multistate preset-value of the write + */ +typedef void (*multistate_input_write_present_value_callback)( + uint32_t object_instance, uint32_t old_value, + uint32_t value); + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ diff --git a/src/bacnet/basic/object/mso.c b/src/bacnet/basic/object/mso.c index 9afac9c5..b23bfc41 100644 --- a/src/bacnet/basic/object/mso.c +++ b/src/bacnet/basic/object/mso.c @@ -1100,24 +1100,18 @@ bool Multistate_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) wp_data->object_instance, value.type.Boolean); } break; - case PROP_OBJECT_NAME: - case PROP_OBJECT_IDENTIFIER: - case PROP_OBJECT_TYPE: - case PROP_STATE_TEXT: - case PROP_STATUS_FLAGS: - case PROP_EVENT_STATE: - case PROP_NUMBER_OF_STATES: - case PROP_RELIABILITY: - case PROP_DESCRIPTION: -#if (BACNET_PROTOCOL_REVISION >= 17) - case PROP_CURRENT_COMMAND_PRIORITY: -#endif - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; - break; default: - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + if (property_lists_member( + Properties_Required, + Properties_Optional, + Properties_Proprietary, + wp_data->object_property)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + } break; } diff --git a/src/bacnet/basic/object/msv.c b/src/bacnet/basic/object/msv.c index 53a59bc5..2bd35369 100644 --- a/src/bacnet/basic/object/msv.c +++ b/src/bacnet/basic/object/msv.c @@ -1,33 +1,18 @@ -/************************************************************************** - * - * Copyright (C) 2012 Steve Karg - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - *********************************************************************/ - -/* Multi-state Value Objects */ - +/** + * @file + * @author Steve Karg + * @date 2009 + * @brief Multi-State object is an input object with a present-value that + * uses an integer data type with a sequence of 1 to N values. + * @section LICENSE + * Copyright (C) 2009 Steve Karg + * SPDX-License-Identifier: MIT + */ #include #include #include +#include +#include /* BACnet Stack defines - first */ #include "bacnet/bacdef.h" /* BACnet Stack API */ @@ -35,31 +20,33 @@ #include "bacnet/bacapp.h" #include "bacnet/rp.h" #include "bacnet/wp.h" -#include "bacnet/basic/object/msv.h" +#include "bacnet/basic/sys/keylist.h" #include "bacnet/basic/services.h" +/* me! */ +#include "bacnet/basic/object/msv.h" -/* number of demo objects */ -#ifndef MAX_MULTISTATE_VALUES -#define MAX_MULTISTATE_VALUES 4 -#endif - -/* how many states? 1 to 254 states - 0 is not allowed. */ -#ifndef MULTISTATE_NUMBER_OF_STATES -#define MULTISTATE_NUMBER_OF_STATES (254) -#endif - -/* Here is our Present Value */ -static uint8_t Present_Value[MAX_MULTISTATE_VALUES]; -/* Writable out-of-service allows others to manipulate our Present Value */ -static bool Out_Of_Service[MAX_MULTISTATE_VALUES]; -/* Change of Value flag */ -static bool Change_Of_Value[MAX_MULTISTATE_VALUES]; -/* object name storage */ -static char Object_Name[MAX_MULTISTATE_VALUES][64]; -/* object description storage */ -static char Object_Description[MAX_MULTISTATE_VALUES][64]; -/* object state text storage */ -static char State_Text[MAX_MULTISTATE_VALUES][MULTISTATE_NUMBER_OF_STATES][64]; +struct object_data { + bool Out_Of_Service : 1; + bool Change_Of_Value : 1; + bool Write_Enabled : 1; + uint8_t Present_Value; + uint8_t Reliability; + const char *Object_Name; + /* The state text functions expect a list of C strings separated by '\0' */ + const char *State_Text; + const char *Description; +}; +/* 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_MULTI_STATE_VALUE; +/* callback for present value writes */ +static multistate_value_write_present_value_callback + Multistate_Value_Write_Present_Value_Callback; +/* default state text when none is specified */ +static const char *Default_State_Text = "State 1\0" + "State 2\0" + "State 3\0"; /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER, @@ -71,6 +58,14 @@ static const int Properties_Optional[] = { PROP_DESCRIPTION, PROP_STATE_TEXT, static const int Properties_Proprietary[] = { -1 }; +/** + * Initialize the pointers for the required, the optional and the properitary + * value properties. + * + * @param pRequired - Pointer to the pointer of required values. + * @param pOptional - Pointer to the pointer of optional values. + * @param pProprietary - Pointer to the pointer of properitary values. + */ void Multistate_Value_Property_Lists( const int **pRequired, const int **pOptional, const int **pProprietary) { @@ -87,87 +82,269 @@ void Multistate_Value_Property_Lists( return; } -void Multistate_Value_Init(void) +/** + * @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 *Multistate_Value_Object(uint32_t object_instance) { - unsigned int i; - - /* initialize all the analog output priority arrays to NULL */ - for (i = 0; i < MAX_MULTISTATE_VALUES; i++) { - Present_Value[i] = 1; - sprintf(&Object_Name[i][0], "MULTISTATE VALUE %u", i); - sprintf(&Object_Description[i][0], "MULTISTATE VALUE %u", i); - } - - return; + return Keylist_Data(Object_List, object_instance); } -/* 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 Multistate objects where N is count. + * @param object_instance - object-instance number of the object + * @return index for the given instance-number, or count (object not found) + */ unsigned Multistate_Value_Instance_To_Index(uint32_t object_instance) { - unsigned index = MAX_MULTISTATE_VALUES; - - if (object_instance < MAX_MULTISTATE_VALUES) { - index = object_instance; - } - - return index; + return Keylist_Index(Object_List, object_instance); } -/* 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 the object instance-number for a given 0..N index + * of Multistate Input objects where N is Multistate_Value_Count(). + * @param index - 0..Multistate_Value_Count() value + * @return object instance-number for the given index + */ uint32_t Multistate_Value_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 count how many you have */ +/** + * @brief Determines the number of Multistate Input objects + * @return Number of Multistate Input objects + */ unsigned Multistate_Value_Count(void) { - return MAX_MULTISTATE_VALUES; + return Keylist_Count(Object_List); } +/** + * @brief Determines if a given Multistate Input instance is valid + * @param object_instance - object-instance number of the object + * @return true if the instance is valid, and false if not + */ bool Multistate_Value_Valid_Instance(uint32_t object_instance) { - unsigned index = 0; /* offset from instance lookup */ + struct object_data *pObject; - index = Multistate_Value_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_VALUES) { + pObject = Multistate_Value_Object(object_instance); + if (pObject) { return true; } return false; } +/** + * @brief Count the number of states + * @param state_names - string of null-terminated state names + * @return number of states + */ +static unsigned state_name_count(const char *state_names) +{ + unsigned count = 0; + int len = 0; + + if (state_names) { + do { + len = strlen(state_names); + if (len > 0) { + count++; + state_names = state_names + len + 1; + } + } while (len > 0); + } + + return count; +} + +/** + * @brief Get the specific state name at index 0..N + * @param state_names - string of null-terminated state names + * @param state_index - state index number 1..N of the state names + * @return state name, or NULL + */ +static const char *state_name_by_index(const char *state_names, unsigned index) +{ + unsigned count = 0; + int len = 0; + + if (state_names) { + do { + len = strlen(state_names); + if (len > 0) { + count++; + if (index == count) { + return state_names; + } + state_names = state_names + len + 1; + } + } while (len > 0); + } + + return NULL; +} + +/** + * @brief For a given object instance-number, determines number of states + * @param object_instance - object-instance number of the object + * @return number of states 1..N + */ +static uint32_t Multistate_Value_Max_States(uint32_t object_instance) +{ + uint32_t count = 0; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + count = state_name_count(pObject->State_Text); + } + + return count; +} + +/** + * @brief For a given object instance-number, returns the state-text in + * a C string. + * @param object_instance - object-instance number of the object + * @param state_index - state index number 1..N of the text requested + * @return C string retrieved + */ +char *Multistate_Value_State_Text( + uint32_t object_instance, uint32_t state_index) +{ + char *pName = NULL; /* return value */ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (state_index > 0) { + pName = + (char *)state_name_by_index(pObject->State_Text, state_index); + } + } + + return pName; +} + +/** + * @brief Encode a BACnetARRAY property element + * @param object_instance [in] BACnet network port object instance number + * @param index [in] array index requested: + * 0 to N for individual array members + * @param apdu [out] Buffer in which the APDU contents are built, or NULL to + * return the length of buffer if it had been built + * @return The length of the apdu encoded or + * BACNET_STATUS_ERROR for ERROR_CODE_INVALID_ARRAY_INDEX + */ +static int Multistate_Value_State_Text_Encode( + uint32_t object_instance, BACNET_ARRAY_INDEX index, uint8_t *apdu) +{ + int apdu_len = BACNET_STATUS_ERROR; + char *pName = NULL; /* return value */ + BACNET_CHARACTER_STRING char_string = { 0 }; + uint32_t state_index = 1; + + state_index += index; + pName = Multistate_Value_State_Text(object_instance, state_index); + if (pName) { + characterstring_init_ansi(&char_string, pName); + apdu_len = encode_application_character_string(apdu, &char_string); + } + + return apdu_len; +} + +/** + * @brief For a given object instance-number, sets the list of state-text from + * a C string array. The state_text_list consists of C strings separated + * by '\0'. For example: + * {@code + * static const char *baud_rate_names = { + * "9600\0" + * "19200\0" + * "38400\0" + * "57600\0" + * "76800\0" + * "115200\0" + * }; + * + * @param object_instance - object-instance number of the object + * @param state_text_list - array of state names to use in this object + * @return true if the state text was set + */ +bool Multistate_Value_State_Text_List_Set( + uint32_t object_instance, const char *state_text_list) +{ + bool status = false; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + pObject->State_Text = state_text_list; + status = true; + } + + return status; +} + +/** + * @brief For a given object instance-number, determines the present-value + * @param object_instance - object-instance number of the object + * @return present-value 1..N of the object + */ uint32_t Multistate_Value_Present_Value(uint32_t object_instance) { uint32_t value = 1; - unsigned index = 0; /* offset from instance lookup */ + struct object_data *pObject; - index = Multistate_Value_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_VALUES) { - value = Present_Value[index]; + pObject = Multistate_Value_Object(object_instance); + if (pObject) { + value = pObject->Present_Value; } return value; } +/** + * @brief For a given object instance-number, checks the present-value for COV + * @param pObject - specific object with valid data + * @param value - floating point analog value + */ +static void Multistate_Value_Present_Value_COV_Detect( + struct object_data *pObject, uint32_t value) +{ + if (pObject) { + if (pObject->Present_Value != value) { + pObject->Change_Of_Value = true; + } + } +} + +/** + * @brief For a given object instance-number, sets the present-value + * @param object_instance - object-instance number of the object + * @param value - integer multi-state value 1..N + * @return true if values are within range and present-value is set. + */ bool Multistate_Value_Present_Value_Set( uint32_t object_instance, uint32_t value) { bool status = false; - unsigned index = 0; /* offset from instance lookup */ + struct object_data *pObject; + unsigned max_states = 0; - index = Multistate_Value_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_VALUES) { - if ((value > 0) && (value <= MULTISTATE_NUMBER_OF_STATES)) { - if (Present_Value[index] != (uint8_t)value) { - Change_Of_Value[index] = true; - } - Present_Value[index] = (uint8_t)value; + pObject = Multistate_Value_Object(object_instance); + if (pObject) { + max_states = state_name_count(pObject->State_Text); + if ((value >= 1) && (value <= max_states)) { + Multistate_Value_Present_Value_COV_Detect(pObject, value); + pObject->Present_Value = value; status = true; } } @@ -175,226 +352,338 @@ bool Multistate_Value_Present_Value_Set( return status; } +/** + * For a given object instance-number, sets the present-value + * + * @param object_instance - object-instance number of the object + * @param value - floating point analog value + * @param error_class - the BACnet error class + * @param error_code - BACnet Error code + * + * @return true if values are within range and present-value is set. + */ +static bool Multistate_Value_Present_Value_Write(uint32_t object_instance, + uint32_t value, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code) +{ + bool status = false; + struct object_data *pObject; + uint32_t old_value = 1; + + pObject = Multistate_Value_Object(object_instance); + if (pObject) { + if (value <= UINT32_MAX) { + if (pObject->Write_Enabled) { + old_value = pObject->Present_Value; + Multistate_Value_Present_Value_COV_Detect(pObject, value); + pObject->Present_Value = value; + if (pObject->Out_Of_Service) { + /* The physical point that the object represents + is not in service. This means that changes to the + Present_Value property are decoupled from the + physical point when the value of Out_Of_Service + is true. */ + } else if (Multistate_Value_Write_Present_Value_Callback) { + Multistate_Value_Write_Present_Value_Callback( + object_instance, old_value, value); + } + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + + return status; +} + +/** + * @brief For a given object instance-number, determines the + * out-of-service state + * @param object_instance - object-instance number of the object + * @return out-of-service state of the object + */ bool Multistate_Value_Out_Of_Service(uint32_t object_instance) { bool value = false; - unsigned index = 0; + struct object_data *pObject; - index = Multistate_Value_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_VALUES) { - value = Out_Of_Service[index]; + pObject = Multistate_Value_Object(object_instance); + if (pObject) { + value = pObject->Out_Of_Service; } return value; } +/** + * @brief For a given object instance-number, sets the out-of-service state + * @param object_instance - object-instance number of the object + * @param value - out-of-service state + */ void Multistate_Value_Out_Of_Service_Set(uint32_t object_instance, bool value) { - unsigned index = 0; + struct object_data *pObject; - index = Multistate_Value_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_VALUES) { - if (Out_Of_Service[index] != value) { - Change_Of_Value[index] = true; - } - Out_Of_Service[index] = value; - } - - return; -} - -char *Multistate_Value_Description(uint32_t object_instance) -{ - unsigned index = 0; /* offset from instance lookup */ - char *pName = NULL; /* return value */ - - index = Multistate_Value_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_VALUES) { - pName = Object_Description[index]; - } - - return pName; -} - -bool Multistate_Value_Description_Set(uint32_t object_instance, char *new_name) -{ - unsigned index = 0; /* offset from instance lookup */ - size_t i = 0; /* loop counter */ - bool status = false; /* return value */ - - index = Multistate_Value_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_VALUES) { - status = true; - if (new_name) { - for (i = 0; i < sizeof(Object_Description[index]); i++) { - Object_Description[index][i] = new_name[i]; - if (new_name[i] == 0) { - break; - } - } - } else { - for (i = 0; i < sizeof(Object_Description[index]); i++) { - Object_Description[index][i] = 0; - } - } - } - - return status; -} - -bool Multistate_Value_Object_Name( - uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) -{ - unsigned index = 0; /* offset from instance lookup */ - bool status = false; - - index = Multistate_Value_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_VALUES) { - status = characterstring_init_ansi(object_name, Object_Name[index]); - } - - return status; -} - -/* note: the object name must be unique within this device */ -bool Multistate_Value_Name_Set(uint32_t object_instance, char *new_name) -{ - unsigned index = 0; /* offset from instance lookup */ - size_t i = 0; /* loop counter */ - bool status = false; /* return value */ - - index = Multistate_Value_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_VALUES) { - status = true; - /* FIXME: check to see if there is a matching name */ - if (new_name) { - for (i = 0; i < sizeof(Object_Name[index]); i++) { - Object_Name[index][i] = new_name[i]; - if (new_name[i] == 0) { - break; - } - } - } else { - for (i = 0; i < sizeof(Object_Name[index]); i++) { - Object_Name[index][i] = 0; - } - } - } - - return status; -} - -char *Multistate_Value_State_Text( - uint32_t object_instance, uint32_t state_index) -{ - unsigned index = 0; /* offset from instance lookup */ - char *pName = NULL; /* return value */ - - index = Multistate_Value_Instance_To_Index(object_instance); - if ((index < MAX_MULTISTATE_VALUES) && (state_index > 0) && - (state_index <= MULTISTATE_NUMBER_OF_STATES)) { - state_index--; - pName = State_Text[index][state_index]; - } - - return pName; -} - -/* note: the object name must be unique within this device */ -bool Multistate_Value_State_Text_Set( - uint32_t object_instance, uint32_t state_index, char *new_name) -{ - unsigned index = 0; /* offset from instance lookup */ - size_t i = 0; /* loop counter */ - bool status = false; /* return value */ - - index = Multistate_Value_Instance_To_Index(object_instance); - if ((index < MAX_MULTISTATE_VALUES) && (state_index > 0) && - (state_index <= MULTISTATE_NUMBER_OF_STATES)) { - state_index--; - status = true; - if (new_name) { - for (i = 0; i < sizeof(State_Text[index][state_index]); i++) { - State_Text[index][state_index][i] = new_name[i]; - if (new_name[i] == 0) { - break; - } - } - } else { - for (i = 0; i < sizeof(State_Text[index][state_index]); i++) { - State_Text[index][state_index][i] = 0; - } - } - } - - return status; - ; -} - -bool Multistate_Value_Change_Of_Value(uint32_t object_instance) -{ - bool status = false; - unsigned index; - - index = Multistate_Value_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_VALUES) { - status = Change_Of_Value[index]; - } - - return status; -} - -void Multistate_Value_Change_Of_Value_Clear(uint32_t object_instance) -{ - unsigned index; - - index = Multistate_Value_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_VALUES) { - Change_Of_Value[index] = false; + pObject = Multistate_Value_Object(object_instance); + if (pObject) { + pObject->Out_Of_Service = value; + pObject->Change_Of_Value = true; } return; } /** - * For a given object instance-number, loads the value_list with the COV data. - * + * @brief 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 value_list - list of COV data + * @param object_name - holds the object-name retrieved * - * @return true if the value list is encoded + * @return true if object-name was retrieved */ -bool Multistate_Value_Encode_Value_List( - uint32_t object_instance, BACNET_PROPERTY_VALUE *value_list) +bool Multistate_Value_Object_Name( + uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) { bool status = false; - const bool in_alarm = false; - const bool fault = false; - const bool overridden = false; - bool out_of_service = false; - uint32_t present_value = 0; - unsigned index = 0; + struct object_data *pObject; + char name_text[32]; - index = Multistate_Value_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_VALUES) { - present_value = Present_Value[index]; - out_of_service = Out_Of_Service[index]; - status = cov_value_list_encode_enumerated(value_list, present_value, - in_alarm, fault, overridden, out_of_service); + pObject = Multistate_Value_Object(object_instance); + if (pObject) { + if (pObject->Object_Name) { + status = + characterstring_init_ansi(object_name, pObject->Object_Name); + } else { + snprintf(name_text, sizeof(name_text), "MULTI-STATE INPUT %u", + object_instance); + status = characterstring_init_ansi(object_name, name_text); + } } return status; } -/* return apdu len, or BACNET_STATUS_ERROR on error */ +/** + * @brief For a given object instance-number, sets the object-name + * Note that the object name must be unique within this device. + * @param object_instance - object-instance number of the object + * @param new_name - holds the object-name to be set + * @return true if object-name was set + */ +bool Multistate_Value_Name_Set(uint32_t object_instance, char *new_name) +{ + bool status = false; /* return value */ + struct object_data *pObject; + + pObject = Multistate_Value_Object(object_instance); + if (pObject && new_name) { + status = true; + pObject->Object_Name = new_name; + } + + return status; +} + +/** + * @brief For a given object instance-number, gets the reliability. + * @param object_instance - object-instance number of the object + * @return reliability value + */ +BACNET_RELIABILITY Multistate_Value_Reliability(uint32_t object_instance) +{ + BACNET_RELIABILITY reliability = RELIABILITY_NO_FAULT_DETECTED; + struct object_data *pObject; + + pObject = Multistate_Value_Object(object_instance); + if (pObject) { + reliability = (BACNET_RELIABILITY)pObject->Reliability; + } + + return reliability; +} + +/** + * @brief For a given object instance-number, gets the Fault status flag + * @param object_instance - object-instance number of the object + * @return true the status flag is in Fault + */ +static bool Multistate_Value_Object_Fault(struct object_data *pObject) +{ + bool fault = false; + + if (pObject) { + if (pObject->Reliability != RELIABILITY_NO_FAULT_DETECTED) { + fault = true; + } + } + + return fault; +} + +/** + * @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 Multistate_Value_Reliability_Set( + uint32_t object_instance, BACNET_RELIABILITY value) +{ + struct object_data *pObject; + bool status = false; + bool fault = false; + + pObject = Multistate_Value_Object(object_instance); + if (pObject) { + if (value <= RELIABILITY_PROPRIETARY_MAX) { + fault = Multistate_Value_Object_Fault(pObject); + pObject->Reliability = value; + if (fault != Multistate_Value_Object_Fault(pObject)) { + pObject->Change_Of_Value = true; + } + status = true; + } + } + + return status; +} + +/** + * @brief For a given object instance-number, gets the Fault status flag + * @param object_instance - object-instance number of the object + * @return true the status flag is in Fault + */ +static bool Multistate_Value_Fault(uint32_t object_instance) +{ + struct object_data *pObject; + + pObject = Multistate_Value_Object(object_instance); + + return Multistate_Value_Object_Fault(pObject); +} + +/** + * @brief 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 + */ +char *Multistate_Value_Description(uint32_t object_instance) +{ + char *name = NULL; + struct object_data *pObject; + + pObject = Multistate_Value_Object(object_instance); + if (pObject) { + name = (char *)pObject->Description; + } + + return name; +} + +/** + * @brief 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 + * @return true if object-name was set + */ +bool Multistate_Value_Description_Set(uint32_t object_instance, char *new_name) +{ + bool status = false; /* return value */ + struct object_data *pObject; + + pObject = Multistate_Value_Object(object_instance); + if (pObject && new_name) { + status = true; + pObject->Description = new_name; + } + + return status; +} + +/** + * @brief Get the COV change flag status + * @param object_instance - object-instance number of the object + * @return the COV change flag status + */ +bool Multistate_Value_Change_Of_Value(uint32_t object_instance) +{ + bool changed = false; + + struct object_data *pObject; + + pObject = Multistate_Value_Object(object_instance); + if (pObject) { + changed = pObject->Change_Of_Value; + } + + return changed; +} + +/** + * @brief Clear the COV change flag + * @param object_instance - object-instance number of the object + */ +void Multistate_Value_Change_Of_Value_Clear(uint32_t object_instance) +{ + struct object_data *pObject; + + pObject = Multistate_Value_Object(object_instance); + if (pObject) { + pObject->Change_Of_Value = false; + } +} + +/** + * @brief Encode the Value List for Present-Value and Status-Flags + * @param object_instance - object-instance number of the object + * @param value_list - #BACNET_PROPERTY_VALUE with at least 2 entries + * @return true if values were encoded + */ +bool Multistate_Value_Encode_Value_List( + uint32_t object_instance, BACNET_PROPERTY_VALUE *value_list) +{ + bool status = false; + struct object_data *pObject; + const bool in_alarm = false; + bool fault = false; + const bool overridden = false; + + pObject = Multistate_Value_Object(object_instance); + if (pObject) { + fault = Multistate_Value_Object_Fault(pObject); + status = + cov_value_list_encode_unsigned(value_list, pObject->Present_Value, + in_alarm, fault, overridden, pObject->Out_Of_Service); + } + 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 Multistate_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) { - int len = 0; int apdu_len = 0; /* return value */ + int apdu_size = 0; BACNET_BIT_STRING bit_string; BACNET_CHARACTER_STRING char_string; uint32_t present_value = 0; - unsigned i = 0; + uint32_t max_states = 0; bool state = false; uint8_t *apdu = NULL; @@ -403,10 +692,11 @@ int Multistate_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) return 0; } apdu = rpdata->application_data; + apdu_size = rpdata->application_data_len; switch (rpdata->object_property) { case PROP_OBJECT_IDENTIFIER: apdu_len = encode_application_object_id( - &apdu[0], OBJECT_MULTI_STATE_VALUE, rpdata->object_instance); + &apdu[0], Object_Type, rpdata->object_instance); break; /* note: Name and Description don't have to be the same. You could make Description writable and different */ @@ -415,15 +705,8 @@ int Multistate_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) apdu_len = encode_application_character_string(&apdu[0], &char_string); break; - case PROP_DESCRIPTION: - characterstring_init_ansi(&char_string, - Multistate_Value_Description(rpdata->object_instance)); - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; case PROP_OBJECT_TYPE: - apdu_len = encode_application_enumerated( - &apdu[0], OBJECT_MULTI_STATE_VALUE); + apdu_len = encode_application_enumerated(&apdu[0], Object_Type); break; case PROP_PRESENT_VALUE: present_value = @@ -434,12 +717,17 @@ int Multistate_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) /* note: see the details in the standard on how to use these */ bitstring_init(&bit_string); bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); - bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + state = Multistate_Value_Fault(rpdata->object_instance); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, state); bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); state = Multistate_Value_Out_Of_Service(rpdata->object_instance); bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state); apdu_len = encode_application_bitstring(&apdu[0], &bit_string); break; + case PROP_RELIABILITY: + apdu_len = encode_application_enumerated(&apdu[0], + Multistate_Value_Reliability(rpdata->object_instance)); + break; case PROP_EVENT_STATE: /* note: see the details in the standard on how to use this */ apdu_len = @@ -450,49 +738,28 @@ int Multistate_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) apdu_len = encode_application_boolean(&apdu[0], state); break; case PROP_NUMBER_OF_STATES: - apdu_len = encode_application_unsigned( - &apdu[apdu_len], MULTISTATE_NUMBER_OF_STATES); + apdu_len = encode_application_unsigned(&apdu[apdu_len], + Multistate_Value_Max_States(rpdata->object_instance)); break; case PROP_STATE_TEXT: - if (rpdata->array_index == 0) { - /* Array element zero is the number of elements in the array */ - apdu_len = encode_application_unsigned( - &apdu[0], MULTISTATE_NUMBER_OF_STATES); - } else if (rpdata->array_index == BACNET_ARRAY_ALL) { - /* if no index was specified, then try to encode the entire list - */ - /* into one packet. */ - for (i = 1; i <= MULTISTATE_NUMBER_OF_STATES; i++) { - characterstring_init_ansi(&char_string, - Multistate_Value_State_Text( - rpdata->object_instance, i)); - /* FIXME: this might go beyond MAX_APDU length! */ - len = encode_application_character_string( - &apdu[apdu_len], &char_string); - /* add it if we have room */ - if ((apdu_len + len) < MAX_APDU) { - apdu_len += len; - } else { - rpdata->error_code = - ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; - apdu_len = BACNET_STATUS_ABORT; - break; - } - } - } else { - if (rpdata->array_index <= MULTISTATE_NUMBER_OF_STATES) { - characterstring_init_ansi(&char_string, - Multistate_Value_State_Text( - rpdata->object_instance, rpdata->array_index)); - apdu_len = encode_application_character_string( - &apdu[0], &char_string); - } else { - rpdata->error_class = ERROR_CLASS_PROPERTY; - rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; - apdu_len = BACNET_STATUS_ERROR; - } + max_states = Multistate_Value_Max_States(rpdata->object_instance); + apdu_len = bacnet_array_encode(rpdata->object_instance, + rpdata->array_index, Multistate_Value_State_Text_Encode, + max_states, apdu, apdu_size); + if (apdu_len == BACNET_STATUS_ABORT) { + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + } else if (apdu_len == BACNET_STATUS_ERROR) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; } break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Multistate_Value_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; @@ -511,7 +778,13 @@ int Multistate_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) return apdu_len; } -/* returns true if successful */ +/** + * @brief WriteProperty handler for this object. For the given WriteProperty + * data, the application_data is loaded or the error flags are set. + * @param wp_data - BACNET_WRITE_PROPERTY_DATA data, including + * requested data and space for the reply, or error response. + * @return false if an error is loaded, true if no errors + */ bool Multistate_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) { bool status = false; /* return value */ @@ -521,7 +794,7 @@ bool Multistate_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) /* decode the some of the request */ len = bacapp_decode_application_data( wp_data->application_data, wp_data->application_data_len, &value); - /* FIXME: len < application_data_len: more data? */ + /* len < application_data_len: extra data for arrays only */ if (len < 0) { /* error while decoding - a value larger than we can handle */ wp_data->error_class = ERROR_CLASS_PROPERTY; @@ -541,12 +814,9 @@ bool Multistate_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT); if (status) { - status = Multistate_Value_Present_Value_Set( - wp_data->object_instance, value.type.Unsigned_Int); - if (!status) { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } + status = Multistate_Value_Present_Value_Write( + wp_data->object_instance, value.type.Enumerated, + &wp_data->error_class, &wp_data->error_code); } break; case PROP_OUT_OF_SERVICE: @@ -557,22 +827,117 @@ bool Multistate_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) wp_data->object_instance, value.type.Boolean); } break; - case PROP_OBJECT_IDENTIFIER: - case PROP_OBJECT_NAME: - case PROP_OBJECT_TYPE: - case PROP_STATUS_FLAGS: - case PROP_EVENT_STATE: - case PROP_NUMBER_OF_STATES: - case PROP_DESCRIPTION: - case PROP_STATE_TEXT: - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; - break; default: - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + if (property_lists_member(Properties_Required, Properties_Optional, + Properties_Proprietary, wp_data->object_property)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + } break; } return status; } + +/** + * @brief Sets a callback used when present-value is written from BACnet + * @param cb - callback used to provide indications + */ +void Multistate_Value_Write_Present_Value_Callback_Set( + multistate_value_write_present_value_callback cb) +{ + Multistate_Value_Write_Present_Value_Callback = cb; +} + +/** + * @brief Creates a new object and adds it to the object list + * @param object_instance - object-instance number of the object + * @return the object-instance that was created, or BACNET_MAX_INSTANCE + */ +uint32_t Multistate_Value_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->State_Text = Default_State_Text; + pObject->Out_Of_Service = false; + pObject->Reliability = RELIABILITY_NO_FAULT_DETECTED; + pObject->Change_Of_Value = false; + pObject->Present_Value = 1; + /* 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 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 Multistate_Value_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 Cleans up the object list and its data + */ +void Multistate_Value_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 list + */ +void Multistate_Value_Init(void) +{ + Object_List = Keylist_Create(); +} diff --git a/src/bacnet/basic/object/msv.h b/src/bacnet/basic/object/msv.h index 36de32fa..25fa63ae 100644 --- a/src/bacnet/basic/object/msv.h +++ b/src/bacnet/basic/object/msv.h @@ -1,29 +1,15 @@ -/************************************************************************** -* -* Copyright (C) 2012 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -* -*********************************************************************/ -#ifndef MULTISTATE_VALUE_H -#define MULTISTATE_VALUE_H +/** + * @file + * @author Steve Karg + * @date 2009 + * @brief Multi-State object is an input object with a present-value that + * uses an integer data type with a sequence of 1 to N values. + * @section LICENSE + * Copyright (C) 2009 Steve Karg + * SPDX-License-Identifier: MIT + */ +#ifndef BACNET_MULTI_STATE_VALUE_H +#define BACNET_MULTI_STATE_VALUE_H #include #include @@ -34,6 +20,16 @@ #include "bacnet/rp.h" #include "bacnet/wp.h" +/** + * @brief Callback for gateway write present value request + * @param object_instance - object-instance number of the object + * @param old_value - multistate preset-value prior to write + * @param value - multistate preset-value of the write + */ +typedef void (*multistate_value_write_present_value_callback)( + uint32_t object_instance, uint32_t old_value, + uint32_t value); + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ diff --git a/test/bacnet/basic/object/ai/CMakeLists.txt b/test/bacnet/basic/object/ai/CMakeLists.txt index 3cde278d..aea0782b 100644 --- a/test/bacnet/basic/object/ai/CMakeLists.txt +++ b/test/bacnet/basic/object/ai/CMakeLists.txt @@ -23,10 +23,12 @@ set(ZTST_DIR "${TST_DIR}/ztest/src") add_compile_definitions( BIG_ENDIAN=0 CONFIG_ZTEST=1 + INTRINSIC_REPORTING=1 ) include_directories( ${SRC_DIR} + ${TST_DIR}/bacnet/basic/object ${TST_DIR}/ztest/include ) @@ -43,10 +45,8 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/bactext.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdevobjpropref.c - ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/cov.c ${SRC_DIR}/bacnet/datetime.c - ${SRC_DIR}/bacnet/basic/sys/days.c ${SRC_DIR}/bacnet/indtext.c ${SRC_DIR}/bacnet/hostnport.c ${SRC_DIR}/bacnet/lighting.c @@ -59,8 +59,14 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/dailyschedule.c ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.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 # Test and test library files ./src/main.c + ./stubs.c + ${TST_DIR}/bacnet/basic/object/property_test.c ${ZTST_DIR}/ztest_mock.c ${ZTST_DIR}/ztest.c ) diff --git a/test/bacnet/basic/object/ai/src/main.c b/test/bacnet/basic/object/ai/src/main.c index ff4441d6..b22b41b0 100644 --- a/test/bacnet/basic/object/ai/src/main.c +++ b/test/bacnet/basic/object/ai/src/main.c @@ -1,16 +1,15 @@ -/* - * Copyright (c) 2020 Legrand North America, LLC. +/** + * @file + * @brief Unit test for object + * @author Steve Karg + * @date April 2024 + * @section LICENSE * * SPDX-License-Identifier: MIT */ - -/* @file - * @brief test BACnet integer encode/decode APIs - */ - #include #include -#include +#include /** * @addtogroup bacnet_tests @@ -26,36 +25,26 @@ ZTEST(ai_tests, testAnalogInput) static void testAnalogInput(void) #endif { - uint8_t apdu[MAX_APDU] = { 0 }; - int len = 0, test_len = 0; - BACNET_READ_PROPERTY_DATA rpdata = { 0 }; - BACNET_APPLICATION_DATA_VALUE value = {0}; - const int *required_property = NULL; - const uint32_t instance = 1; + bool status = false; + unsigned count = 0; + uint32_t object_instance = BACNET_MAX_INSTANCE, test_object_instance = 0; + const int skip_fail_property_list[] = { -1 }; Analog_Input_Init(); - rpdata.application_data = &apdu[0]; - rpdata.application_data_len = sizeof(apdu); - rpdata.object_type = OBJECT_ANALOG_INPUT; - rpdata.object_instance = instance; - rpdata.array_index = BACNET_ARRAY_ALL; - - Analog_Input_Property_Lists(&required_property, NULL, NULL); - while ((*required_property) >= 0) { - rpdata.object_property = *required_property; - len = Analog_Input_Read_Property(&rpdata); - zassert_true(len >= 0, NULL); - 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)); - } - zassert_equal(len, test_len, NULL); - } - required_property++; - } + object_instance = Analog_Input_Create(object_instance); + count = Analog_Input_Count(); + zassert_true(count == 1, NULL); + test_object_instance = Analog_Input_Index_To_Instance(0); + zassert_equal(object_instance, test_object_instance, NULL); + bacnet_object_properties_read_write_test( + OBJECT_ANALOG_INPUT, + object_instance, + Analog_Input_Property_Lists, + Analog_Input_Read_Property, + Analog_Input_Write_Property, + skip_fail_property_list); + status = Analog_Input_Delete(object_instance); + zassert_true(status, NULL); } /** * @} diff --git a/test/bacnet/basic/object/ai/stubs.c b/test/bacnet/basic/object/ai/stubs.c new file mode 100644 index 00000000..5bb693d2 --- /dev/null +++ b/test/bacnet/basic/object/ai/stubs.c @@ -0,0 +1,66 @@ +/** + * @file + * @brief Stub functions for unit test of a BACnet object + * @author Steve Karg + * @date December 2022 + * + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include "bacnet/bacdef.h" +#include "bacnet/alarm_ack.h" +#include "bacnet/datetime.h" +#include "bacnet/event.h" +#include "bacnet/getevent.h" +#include "bacnet/get_alarm_sum.h" +#include "bacnet/npdu.h" + +bool datetime_local(BACNET_DATE *bdate, + BACNET_TIME *btime, + int16_t *utc_offset_minutes, + bool *dst_active) +{ + (void)bdate; + (void)btime; + (void)utc_offset_minutes; + (void)dst_active; + + return false; +} + +void Notification_Class_common_reporting_function( + BACNET_EVENT_NOTIFICATION_DATA *event_data) +{ + (void)event_data; +} + +void Notification_Class_Get_Priorities( + uint32_t Object_Instance, uint32_t *pPriorityArray) +{ + (void)Object_Instance; + (void)pPriorityArray; +} + +void handler_get_event_information_set( + BACNET_OBJECT_TYPE object_type, get_event_info_function pFunction) +{ + (void)object_type; + (void)pFunction; +} + +void handler_alarm_ack_set( + BACNET_OBJECT_TYPE object_type, alarm_ack_function pFunction) +{ + (void)object_type; + (void)pFunction; +} + +void handler_get_alarm_summary_set( + BACNET_OBJECT_TYPE object_type, get_alarm_summary_function pFunction) +{ + (void)object_type; + (void)pFunction; +} diff --git a/test/bacnet/basic/object/ao/CMakeLists.txt b/test/bacnet/basic/object/ao/CMakeLists.txt index 501052b5..45647c9a 100644 --- a/test/bacnet/basic/object/ao/CMakeLists.txt +++ b/test/bacnet/basic/object/ao/CMakeLists.txt @@ -7,7 +7,6 @@ project(test_${basename} VERSION 1.0.0 LANGUAGES C) - string(REGEX REPLACE "/test/bacnet/[a-zA-Z_/-]*$" "/src" @@ -27,6 +26,7 @@ add_compile_definitions( include_directories( ${SRC_DIR} + ${TST_DIR}/bacnet/basic/object ${TST_DIR}/ztest/include ) @@ -43,14 +43,12 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/bacreal.c ${SRC_DIR}/bacnet/bacstr.c ${SRC_DIR}/bacnet/bactext.c - ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/cov.c ${SRC_DIR}/bacnet/datetime.c - ${SRC_DIR}/bacnet/basic/sys/days.c - ${SRC_DIR}/bacnet/basic/sys/keylist.c ${SRC_DIR}/bacnet/indtext.c ${SRC_DIR}/bacnet/hostnport.c ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/proplist.c ${SRC_DIR}/bacnet/memcopy.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c @@ -59,9 +57,12 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/dailyschedule.c ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.c + ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/basic/sys/keylist.c # Test and test library files ./src/main.c - ../mock/device_mock.c + ${TST_DIR}/bacnet/basic/object/property_test.c ${ZTST_DIR}/ztest_mock.c ${ZTST_DIR}/ztest.c ) diff --git a/test/bacnet/basic/object/ao/src/main.c b/test/bacnet/basic/object/ao/src/main.c index 0fa4382b..fa53f982 100644 --- a/test/bacnet/basic/object/ao/src/main.c +++ b/test/bacnet/basic/object/ao/src/main.c @@ -1,15 +1,15 @@ -/* - * Copyright (c) 2020 Legrand North America, LLC. +/** + * @file + * @brief Unit test for object + * @author Steve Karg + * @date April 2024 + * @section LICENSE * * SPDX-License-Identifier: MIT */ - -/* @file - * @brief test BACnet integer encode/decode APIs - */ - #include #include +#include /** * @addtogroup bacnet_tests @@ -25,47 +25,26 @@ ZTEST(ao_tests, testAnalogOutput) static void testAnalogOutput(void) #endif { - uint8_t apdu[MAX_APDU] = { 0 }; - int len = 0, test_len = 0; - BACNET_READ_PROPERTY_DATA rpdata = { 0 }; - BACNET_APPLICATION_DATA_VALUE value = {0}; - const int *required_property = NULL; - const uint32_t instance = 1; + bool status = false; + unsigned count = 0; + uint32_t object_instance = BACNET_MAX_INSTANCE, test_object_instance = 0; + const int skip_fail_property_list[] = { PROP_PRIORITY_ARRAY, -1 }; Analog_Output_Init(); - Analog_Output_Create(1); - rpdata.application_data = &apdu[0]; - rpdata.application_data_len = sizeof(apdu); - rpdata.object_type = OBJECT_ANALOG_OUTPUT; - rpdata.object_instance = instance; - rpdata.array_index = BACNET_ARRAY_ALL; - - Analog_Output_Property_Lists(&required_property, NULL, NULL); - while ((*required_property) >= 0) { - rpdata.object_property = *required_property; - len = Analog_Output_Read_Property(&rpdata); - if (len < 0) { - printf("property %u: failed to read!\n", - (unsigned)rpdata.object_property); - } - zassert_true(len >= 0, NULL); - 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 %u: failed to decode!\n", - (unsigned)rpdata.object_property); - } - if (rpdata.object_property == PROP_PRIORITY_ARRAY) { - /* FIXME: known fail to decode */ - len = test_len; - } - zassert_equal(len, test_len, NULL); - } - required_property++; - } - - return; + object_instance = Analog_Output_Create(object_instance); + count = Analog_Output_Count(); + zassert_true(count == 1, NULL); + test_object_instance = Analog_Output_Index_To_Instance(0); + zassert_equal(object_instance, test_object_instance, NULL); + bacnet_object_properties_read_write_test( + OBJECT_ANALOG_OUTPUT, + object_instance, + Analog_Output_Property_Lists, + Analog_Output_Read_Property, + Analog_Output_Write_Property, + skip_fail_property_list); + status = Analog_Output_Delete(object_instance); + zassert_true(status, NULL); } /** * @} diff --git a/test/bacnet/basic/object/av/CMakeLists.txt b/test/bacnet/basic/object/av/CMakeLists.txt index 89c9f294..a06960df 100644 --- a/test/bacnet/basic/object/av/CMakeLists.txt +++ b/test/bacnet/basic/object/av/CMakeLists.txt @@ -23,10 +23,12 @@ set(ZTST_DIR "${TST_DIR}/ztest/src") add_compile_definitions( BIG_ENDIAN=0 CONFIG_ZTEST=1 + INTRINSIC_REPORTING=1 ) include_directories( ${SRC_DIR} + ${TST_DIR}/bacnet/basic/object ${TST_DIR}/ztest/include ) @@ -43,10 +45,8 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/bacreal.c ${SRC_DIR}/bacnet/bacstr.c ${SRC_DIR}/bacnet/bactext.c - ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/cov.c ${SRC_DIR}/bacnet/datetime.c - ${SRC_DIR}/bacnet/basic/sys/days.c ${SRC_DIR}/bacnet/indtext.c ${SRC_DIR}/bacnet/hostnport.c ${SRC_DIR}/bacnet/lighting.c @@ -59,8 +59,14 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/dailyschedule.c ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.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 # Test and test library files ./src/main.c + ./stubs.c + ${TST_DIR}/bacnet/basic/object/property_test.c ${ZTST_DIR}/ztest_mock.c ${ZTST_DIR}/ztest.c ) diff --git a/test/bacnet/basic/object/av/src/main.c b/test/bacnet/basic/object/av/src/main.c index 76bbbf56..97e10ab8 100644 --- a/test/bacnet/basic/object/av/src/main.c +++ b/test/bacnet/basic/object/av/src/main.c @@ -1,15 +1,15 @@ -/* - * Copyright (c) 2020 Legrand North America, LLC. +/** + * @file + * @brief Unit test for object + * @author Steve Karg + * @date April 2024 + * @section LICENSE * * SPDX-License-Identifier: MIT */ - -/* @file - * @brief test BACnet integer encode/decode APIs - */ - #include #include +#include /** * @addtogroup bacnet_tests @@ -25,40 +25,26 @@ ZTEST(av_tests, testAnalog_Value) static void testAnalog_Value(void) #endif { - uint8_t apdu[MAX_APDU] = { 0 }; - int len = 0, test_len = 0; - BACNET_READ_PROPERTY_DATA rpdata = { 0 }; - BACNET_APPLICATION_DATA_VALUE value = {0}; - const int *required_property = NULL; - const uint32_t instance = 1; + bool status = false; + unsigned count = 0; + uint32_t object_instance = BACNET_MAX_INSTANCE, test_object_instance = 0; + const int skip_fail_property_list[] = { -1 }; Analog_Value_Init(); - rpdata.application_data = &apdu[0]; - rpdata.application_data_len = sizeof(apdu); - rpdata.object_type = OBJECT_ANALOG_VALUE; - rpdata.object_instance = instance; - rpdata.array_index = BACNET_ARRAY_ALL; - - Analog_Value_Property_Lists(&required_property, NULL, NULL); - while ((*required_property) >= 0) { - rpdata.object_property = *required_property; - len = Analog_Value_Read_Property(&rpdata); - zassert_true(len >= 0, NULL); - 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)); - } - if (rpdata.object_property == PROP_PRIORITY_ARRAY) { - /* FIXME: known fail to decode */ - len = test_len; - } - zassert_equal(len, test_len, NULL); - } - required_property++; - } + object_instance = Analog_Value_Create(object_instance); + count = Analog_Value_Count(); + zassert_true(count == 1, NULL); + test_object_instance = Analog_Value_Index_To_Instance(0); + zassert_equal(object_instance, test_object_instance, NULL); + bacnet_object_properties_read_write_test( + OBJECT_ANALOG_VALUE, + object_instance, + Analog_Value_Property_Lists, + Analog_Value_Read_Property, + Analog_Value_Write_Property, + skip_fail_property_list); + status = Analog_Value_Delete(object_instance); + zassert_true(status, NULL); } /** * @} diff --git a/test/bacnet/basic/object/av/stubs.c b/test/bacnet/basic/object/av/stubs.c new file mode 100644 index 00000000..5bb693d2 --- /dev/null +++ b/test/bacnet/basic/object/av/stubs.c @@ -0,0 +1,66 @@ +/** + * @file + * @brief Stub functions for unit test of a BACnet object + * @author Steve Karg + * @date December 2022 + * + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include "bacnet/bacdef.h" +#include "bacnet/alarm_ack.h" +#include "bacnet/datetime.h" +#include "bacnet/event.h" +#include "bacnet/getevent.h" +#include "bacnet/get_alarm_sum.h" +#include "bacnet/npdu.h" + +bool datetime_local(BACNET_DATE *bdate, + BACNET_TIME *btime, + int16_t *utc_offset_minutes, + bool *dst_active) +{ + (void)bdate; + (void)btime; + (void)utc_offset_minutes; + (void)dst_active; + + return false; +} + +void Notification_Class_common_reporting_function( + BACNET_EVENT_NOTIFICATION_DATA *event_data) +{ + (void)event_data; +} + +void Notification_Class_Get_Priorities( + uint32_t Object_Instance, uint32_t *pPriorityArray) +{ + (void)Object_Instance; + (void)pPriorityArray; +} + +void handler_get_event_information_set( + BACNET_OBJECT_TYPE object_type, get_event_info_function pFunction) +{ + (void)object_type; + (void)pFunction; +} + +void handler_alarm_ack_set( + BACNET_OBJECT_TYPE object_type, alarm_ack_function pFunction) +{ + (void)object_type; + (void)pFunction; +} + +void handler_get_alarm_summary_set( + BACNET_OBJECT_TYPE object_type, get_alarm_summary_function pFunction) +{ + (void)object_type; + (void)pFunction; +} diff --git a/test/bacnet/basic/object/bi/CMakeLists.txt b/test/bacnet/basic/object/bi/CMakeLists.txt index 51e5a491..7306bd29 100644 --- a/test/bacnet/basic/object/bi/CMakeLists.txt +++ b/test/bacnet/basic/object/bi/CMakeLists.txt @@ -27,6 +27,7 @@ add_compile_definitions( include_directories( ${SRC_DIR} + ${TST_DIR}/bacnet/basic/object ${TST_DIR}/ztest/include ) @@ -43,13 +44,12 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/bacreal.c ${SRC_DIR}/bacnet/bacstr.c ${SRC_DIR}/bacnet/bactext.c - ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/cov.c ${SRC_DIR}/bacnet/datetime.c - ${SRC_DIR}/bacnet/basic/sys/days.c ${SRC_DIR}/bacnet/indtext.c ${SRC_DIR}/bacnet/hostnport.c ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/proplist.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/memcopy.c ${SRC_DIR}/bacnet/wp.c @@ -58,8 +58,13 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/dailyschedule.c ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.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 # Test and test library files ./src/main.c + ${TST_DIR}/bacnet/basic/object/property_test.c ${ZTST_DIR}/ztest_mock.c ${ZTST_DIR}/ztest.c ) diff --git a/test/bacnet/basic/object/bi/src/main.c b/test/bacnet/basic/object/bi/src/main.c index 0ac06bab..abd0bc23 100644 --- a/test/bacnet/basic/object/bi/src/main.c +++ b/test/bacnet/basic/object/bi/src/main.c @@ -1,16 +1,16 @@ -/* - * Copyright (c) 2020 Legrand North America, LLC. +/** + * @file + * @brief Unit test for object + * @author Steve Karg + * @date April 2024 + * @section LICENSE * * SPDX-License-Identifier: MIT */ - -/* @file - * @brief test BACnet integer encode/decode APIs - */ - #include -#include #include +#include +#include /** * @addtogroup bacnet_tests @@ -26,36 +26,26 @@ ZTEST(bi_tests, testBinaryInput) static void testBinaryInput(void) #endif { - uint8_t apdu[MAX_APDU] = { 0 }; - int len = 0, test_len = 0; - BACNET_READ_PROPERTY_DATA rpdata = { 0 }; - BACNET_APPLICATION_DATA_VALUE value = {0}; - const int *required_property = NULL; - const uint32_t instance = 1; + bool status = false; + unsigned count = 0; + uint32_t object_instance = BACNET_MAX_INSTANCE, test_object_instance = 0; + const int skip_fail_property_list[] = { -1 }; Binary_Input_Init(); - rpdata.application_data = &apdu[0]; - rpdata.application_data_len = sizeof(apdu); - rpdata.object_type = OBJECT_BINARY_INPUT; - rpdata.object_instance = instance; - rpdata.array_index = BACNET_ARRAY_ALL; - - Binary_Input_Property_Lists(&required_property, NULL, NULL); - while ((*required_property) >= 0) { - rpdata.object_property = *required_property; - len = Binary_Input_Read_Property(&rpdata); - zassert_true(len >= 0, NULL); - 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)); - } - zassert_equal(len, test_len, NULL); - } - required_property++; - } + object_instance = Binary_Input_Create(object_instance); + count = Binary_Input_Count(); + zassert_true(count == 1, NULL); + test_object_instance = Binary_Input_Index_To_Instance(0); + zassert_equal(object_instance, test_object_instance, NULL); + bacnet_object_properties_read_write_test( + OBJECT_BINARY_INPUT, + object_instance, + Binary_Input_Property_Lists, + Binary_Input_Read_Property, + Binary_Input_Write_Property, + skip_fail_property_list); + status = Binary_Input_Delete(object_instance); + zassert_true(status, NULL); } /** * @} diff --git a/test/bacnet/basic/object/bo/CMakeLists.txt b/test/bacnet/basic/object/bo/CMakeLists.txt index 8aaa6d7b..d6157c05 100644 --- a/test/bacnet/basic/object/bo/CMakeLists.txt +++ b/test/bacnet/basic/object/bo/CMakeLists.txt @@ -27,6 +27,7 @@ add_compile_definitions( include_directories( ${SRC_DIR} + ${TST_DIR}/bacnet/basic/object ${TST_DIR}/ztest/include ) @@ -43,14 +44,12 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/bacreal.c ${SRC_DIR}/bacnet/bacstr.c ${SRC_DIR}/bacnet/bactext.c - ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/cov.c ${SRC_DIR}/bacnet/datetime.c - ${SRC_DIR}/bacnet/basic/sys/days.c - ${SRC_DIR}/bacnet/basic/sys/keylist.c ${SRC_DIR}/bacnet/indtext.c ${SRC_DIR}/bacnet/hostnport.c ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/proplist.c ${SRC_DIR}/bacnet/memcopy.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c @@ -59,9 +58,13 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/dailyschedule.c ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.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 # Test and test library files ./src/main.c - ../mock/device_mock.c + ${TST_DIR}/bacnet/basic/object/property_test.c ${ZTST_DIR}/ztest_mock.c ${ZTST_DIR}/ztest.c ) diff --git a/test/bacnet/basic/object/bo/src/main.c b/test/bacnet/basic/object/bo/src/main.c index 3c1f8a0c..b353904b 100644 --- a/test/bacnet/basic/object/bo/src/main.c +++ b/test/bacnet/basic/object/bo/src/main.c @@ -1,15 +1,15 @@ -/* - * Copyright (c) 2020 Legrand North America, LLC. +/** + * @file + * @brief Unit test for object + * @author Steve Karg + * @date April 2024 + * @section LICENSE * * SPDX-License-Identifier: MIT */ - -/* @file - * @brief test BACnet integer encode/decode APIs - */ - #include #include +#include /** * @addtogroup bacnet_tests @@ -25,45 +25,26 @@ ZTEST(bo_tests, testBinaryOutput) static void testBinaryOutput(void) #endif { - uint8_t apdu[MAX_APDU] = { 0 }; - int len = 0, test_len = 0; - BACNET_READ_PROPERTY_DATA rpdata = { 0 }; - BACNET_APPLICATION_DATA_VALUE value = {0}; - const int *required_property = NULL; - const uint32_t instance = 1; + bool status = false; + unsigned count = 0; + uint32_t object_instance = BACNET_MAX_INSTANCE, test_object_instance = 0; + const int skip_fail_property_list[] = { PROP_PRIORITY_ARRAY, -1 }; Binary_Output_Init(); - Binary_Output_Create(1); - rpdata.application_data = &apdu[0]; - rpdata.application_data_len = sizeof(apdu); - rpdata.object_type = OBJECT_BINARY_OUTPUT; - rpdata.object_instance = instance; - rpdata.array_index = BACNET_ARRAY_ALL; - - Binary_Output_Property_Lists(&required_property, NULL, NULL); - while ((*required_property) >= 0) { - rpdata.object_property = *required_property; - len = Binary_Output_Read_Property(&rpdata); - if (len < 0) { - printf("property %u: failed to read!\n", - (unsigned)rpdata.object_property); - } - zassert_true(len >= 0, NULL); - 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 %u: failed to decode!\n", - (unsigned)rpdata.object_property); - } - if (rpdata.object_property == PROP_PRIORITY_ARRAY) { - /* FIXME: known fail to decode */ - len = test_len; - } - zassert_equal(len, test_len, NULL); - } - required_property++; - } + object_instance = Binary_Output_Create(object_instance); + count = Binary_Output_Count(); + zassert_true(count == 1, NULL); + test_object_instance = Binary_Output_Index_To_Instance(0); + zassert_equal(object_instance, test_object_instance, NULL); + bacnet_object_properties_read_write_test( + OBJECT_BINARY_OUTPUT, + object_instance, + Binary_Output_Property_Lists, + Binary_Output_Read_Property, + Binary_Output_Write_Property, + skip_fail_property_list); + status = Binary_Output_Delete(object_instance); + zassert_true(status, NULL); } /** * @} diff --git a/test/bacnet/basic/object/bv/CMakeLists.txt b/test/bacnet/basic/object/bv/CMakeLists.txt index 7b73229e..215070c2 100644 --- a/test/bacnet/basic/object/bv/CMakeLists.txt +++ b/test/bacnet/basic/object/bv/CMakeLists.txt @@ -27,6 +27,7 @@ add_compile_definitions( include_directories( ${SRC_DIR} + ${TST_DIR}/bacnet/basic/object ${TST_DIR}/ztest/include ) @@ -43,12 +44,12 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/bacreal.c ${SRC_DIR}/bacnet/bacstr.c ${SRC_DIR}/bacnet/bactext.c - ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/cov.c ${SRC_DIR}/bacnet/datetime.c - ${SRC_DIR}/bacnet/basic/sys/days.c ${SRC_DIR}/bacnet/indtext.c ${SRC_DIR}/bacnet/hostnport.c ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/proplist.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c ${SRC_DIR}/bacnet/weeklyschedule.c @@ -56,8 +57,13 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/dailyschedule.c ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.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 # Test and test library files ./src/main.c + ${TST_DIR}/bacnet/basic/object/property_test.c ${ZTST_DIR}/ztest_mock.c ${ZTST_DIR}/ztest.c ) diff --git a/test/bacnet/basic/object/bv/src/main.c b/test/bacnet/basic/object/bv/src/main.c index 08fea7cc..84555798 100644 --- a/test/bacnet/basic/object/bv/src/main.c +++ b/test/bacnet/basic/object/bv/src/main.c @@ -9,7 +9,9 @@ */ #include +#include #include +#include /** * @addtogroup bacnet_tests @@ -25,40 +27,26 @@ ZTEST(bv_tests, testBinary_Value) static void testBinary_Value(void) #endif { - uint8_t apdu[MAX_APDU] = { 0 }; - int len = 0, test_len = 0; - BACNET_READ_PROPERTY_DATA rpdata = { 0 }; - BACNET_APPLICATION_DATA_VALUE value = {0}; - const int *required_property = NULL; - const uint32_t instance = 1; + bool status = false; + unsigned count = 0; + uint32_t object_instance = BACNET_MAX_INSTANCE, test_object_instance = 0; + const int skip_fail_property_list[] = { -1 }; Binary_Value_Init(); - rpdata.application_data = &apdu[0]; - rpdata.application_data_len = sizeof(apdu); - rpdata.object_type = OBJECT_BINARY_VALUE; - rpdata.object_instance = instance; - rpdata.array_index = BACNET_ARRAY_ALL; - - Binary_Value_Property_Lists(&required_property, NULL, NULL); - while ((*required_property) >= 0) { - rpdata.object_property = *required_property; - len = Binary_Value_Read_Property(&rpdata); - zassert_true(len >= 0, NULL); - 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)); - } - if (rpdata.object_property == PROP_PRIORITY_ARRAY) { - /* FIXME: known fail to decode */ - len = test_len; - } - zassert_equal(len, test_len, NULL); - } - required_property++; - } + object_instance = Binary_Value_Create(object_instance); + count = Binary_Value_Count(); + zassert_true(count == 1, NULL); + test_object_instance = Binary_Value_Index_To_Instance(0); + zassert_equal(object_instance, test_object_instance, NULL); + bacnet_object_properties_read_write_test( + OBJECT_BINARY_VALUE, + object_instance, + Binary_Value_Property_Lists, + Binary_Value_Read_Property, + Binary_Value_Write_Property, + skip_fail_property_list); + status = Binary_Value_Delete(object_instance); + zassert_true(status, NULL); } /** * @} diff --git a/test/bacnet/basic/object/channel/src/main.c b/test/bacnet/basic/object/channel/src/main.c index 87304816..8243c382 100644 --- a/test/bacnet/basic/object/channel/src/main.c +++ b/test/bacnet/basic/object/channel/src/main.c @@ -3,10 +3,10 @@ * @brief Unit test for object * @author Steve Karg * @date July 2023 + * @section LICENSE * * SPDX-License-Identifier: MIT */ - #include #include #include diff --git a/test/bacnet/basic/object/ms-input/CMakeLists.txt b/test/bacnet/basic/object/ms-input/CMakeLists.txt index fabbd268..c4f032f4 100644 --- a/test/bacnet/basic/object/ms-input/CMakeLists.txt +++ b/test/bacnet/basic/object/ms-input/CMakeLists.txt @@ -27,6 +27,7 @@ add_compile_definitions( include_directories( ${SRC_DIR} + ${TST_DIR}/bacnet/basic/object ${TST_DIR}/ztest/include ) @@ -43,12 +44,12 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/bacreal.c ${SRC_DIR}/bacnet/bacstr.c ${SRC_DIR}/bacnet/bactext.c - ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/cov.c ${SRC_DIR}/bacnet/datetime.c - ${SRC_DIR}/bacnet/basic/sys/days.c ${SRC_DIR}/bacnet/indtext.c ${SRC_DIR}/bacnet/hostnport.c ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/proplist.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c ${SRC_DIR}/bacnet/weeklyschedule.c @@ -56,9 +57,13 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/dailyschedule.c ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.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 # Test and test library files ./src/main.c - ../mock/device_mock.c + ${TST_DIR}/bacnet/basic/object/property_test.c ${ZTST_DIR}/ztest_mock.c ${ZTST_DIR}/ztest.c ) diff --git a/test/bacnet/basic/object/ms-input/src/main.c b/test/bacnet/basic/object/ms-input/src/main.c index 1b48b795..161d498b 100644 --- a/test/bacnet/basic/object/ms-input/src/main.c +++ b/test/bacnet/basic/object/ms-input/src/main.c @@ -1,16 +1,16 @@ -/* - * Copyright (c) 2020 Legrand North America, LLC. +/** + * @file + * @brief Unit test for object + * @author Steve Karg + * @date April 2024 + * @section LICENSE * * SPDX-License-Identifier: MIT */ - -/* @file - * @brief test BACnet integer encode/decode APIs - */ - #include #include #include +#include /** * @addtogroup bacnet_tests @@ -26,68 +26,26 @@ ZTEST(ms_input_tests, testMultistateInput) static void testMultistateInput(void) #endif { - uint8_t apdu[MAX_APDU] = { 0 }; - int len = 0, 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; - unsigned count = 0; bool status = false; + unsigned count = 0; + uint32_t object_instance = BACNET_MAX_INSTANCE, test_object_instance = 0; + const int skip_fail_property_list[] = { -1 }; Multistate_Input_Init(); + object_instance = Multistate_Input_Create(object_instance); count = Multistate_Input_Count(); - zassert_true(count > 0, NULL); - rpdata.application_data = &apdu[0]; - rpdata.application_data_len = sizeof(apdu); - rpdata.object_type = OBJECT_MULTI_STATE_INPUT; - rpdata.object_instance = Multistate_Input_Index_To_Instance(0); - rpdata.array_index = BACNET_ARRAY_ALL; - status = Multistate_Input_Valid_Instance(rpdata.object_instance); + zassert_true(count == 1, NULL); + test_object_instance = Multistate_Input_Index_To_Instance(0); + zassert_equal(object_instance, test_object_instance, NULL); + bacnet_object_properties_read_write_test( + OBJECT_MULTI_STATE_INPUT, + object_instance, + Multistate_Input_Property_Lists, + Multistate_Input_Read_Property, + Multistate_Input_Write_Property, + skip_fail_property_list); + status = Multistate_Input_Delete(object_instance); zassert_true(status, NULL); - Multistate_Input_Property_Lists(&pRequired, &pOptional, &pProprietary); - while ((*pRequired) >= 0) { - rpdata.object_property = *pRequired; - len = Multistate_Input_Read_Property(&rpdata); - zassert_true(len >= 0, NULL); - 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)); - } - if (rpdata.object_property == PROP_PRIORITY_ARRAY) { - /* FIXME: known fail to decode */ - len = test_len; - } - zassert_equal(len, test_len, NULL); - } else { - printf("property '%s': failed to read!\n", - bactext_property_name(rpdata.object_property)); - } - pRequired++; - } - while ((*pOptional) != -1) { - rpdata.object_property = *pOptional; - rpdata.array_index = BACNET_ARRAY_ALL; - len = Multistate_Input_Read_Property(&rpdata); - zassert_not_equal(len, BACNET_STATUS_ERROR, NULL); - if (len > 0) { - test_len = bacapp_decode_application_data(rpdata.application_data, - (uint8_t)rpdata.application_data_len, &value); - if (len != test_len) { - printf("property '%s': failed to decode!\n", - bactext_property_name(rpdata.object_property)); - } - zassert_true(test_len >= 0, NULL); - } else { - printf("property '%s': failed to read!\n", - bactext_property_name(rpdata.object_property)); - } - pOptional++; - } } /** * @} diff --git a/test/bacnet/basic/object/mso/CMakeLists.txt b/test/bacnet/basic/object/mso/CMakeLists.txt index 2ca97e03..0d2af0af 100644 --- a/test/bacnet/basic/object/mso/CMakeLists.txt +++ b/test/bacnet/basic/object/mso/CMakeLists.txt @@ -27,6 +27,7 @@ add_compile_definitions( include_directories( ${SRC_DIR} + ${TST_DIR}/bacnet/basic/object ${TST_DIR}/ztest/include ) @@ -43,14 +44,12 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/bacreal.c ${SRC_DIR}/bacnet/bacstr.c ${SRC_DIR}/bacnet/bactext.c - ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/cov.c ${SRC_DIR}/bacnet/datetime.c - ${SRC_DIR}/bacnet/basic/sys/days.c - ${SRC_DIR}/bacnet/basic/sys/keylist.c ${SRC_DIR}/bacnet/indtext.c ${SRC_DIR}/bacnet/hostnport.c ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/proplist.c ${SRC_DIR}/bacnet/memcopy.c ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/wp.c @@ -59,9 +58,13 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/dailyschedule.c ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.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 # Test and test library files ./src/main.c - ../mock/device_mock.c + ${TST_DIR}/bacnet/basic/object/property_test.c ${ZTST_DIR}/ztest_mock.c ${ZTST_DIR}/ztest.c ) diff --git a/test/bacnet/basic/object/mso/src/main.c b/test/bacnet/basic/object/mso/src/main.c index 2c7beaa8..fcb83f18 100644 --- a/test/bacnet/basic/object/mso/src/main.c +++ b/test/bacnet/basic/object/mso/src/main.c @@ -1,15 +1,15 @@ -/* - * Copyright (c) 2020 Legrand North America, LLC. +/** + * @file + * @brief Unit test for object + * @author Steve Karg + * @date April 2024 + * @section LICENSE * * SPDX-License-Identifier: MIT */ - -/* @file - * @brief test BACnet integer encode/decode APIs - */ - #include #include +#include /** * @addtogroup bacnet_tests @@ -25,47 +25,26 @@ ZTEST(mso_tests, testMultistateOutput) static void testMultistateOutput(void) #endif { - uint8_t apdu[MAX_APDU] = { 0 }; - int len = 0, test_len = 0; - BACNET_READ_PROPERTY_DATA rpdata = { 0 }; - BACNET_APPLICATION_DATA_VALUE value = {0}; - const int *required_property = NULL; - const uint32_t instance = 1; + bool status = false; + unsigned count = 0; + uint32_t object_instance = BACNET_MAX_INSTANCE, test_object_instance = 0; + const int skip_fail_property_list[] = { PROP_PRIORITY_ARRAY, -1 }; Multistate_Output_Init(); - Multistate_Output_Create(1); - rpdata.application_data = &apdu[0]; - rpdata.application_data_len = sizeof(apdu); - rpdata.object_type = OBJECT_MULTI_STATE_OUTPUT; - rpdata.object_instance = instance; - rpdata.array_index = BACNET_ARRAY_ALL; - - Multistate_Output_Property_Lists(&required_property, NULL, NULL); - while ((*required_property) >= 0) { - rpdata.object_property = *required_property; - len = Multistate_Output_Read_Property(&rpdata); - if (len < 0) { - printf("property %u: failed to read!\n", - (unsigned)rpdata.object_property); - } - zassert_true(len >= 0, NULL); - 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 %u: failed to decode!\n", - (unsigned)rpdata.object_property); - } - if (rpdata.object_property == PROP_PRIORITY_ARRAY) { - /* FIXME: known fail to decode */ - len = test_len; - } - zassert_equal(len, test_len, NULL); - } - required_property++; - } - - return; + object_instance = Multistate_Output_Create(object_instance); + count = Multistate_Output_Count(); + zassert_true(count == 1, NULL); + test_object_instance = Multistate_Output_Index_To_Instance(0); + zassert_equal(object_instance, test_object_instance, NULL); + bacnet_object_properties_read_write_test( + OBJECT_MULTI_STATE_OUTPUT, + object_instance, + Multistate_Output_Property_Lists, + Multistate_Output_Read_Property, + Multistate_Output_Write_Property, + skip_fail_property_list); + status = Multistate_Output_Delete(object_instance); + zassert_true(status, NULL); } /** * @} diff --git a/test/bacnet/basic/object/msv/CMakeLists.txt b/test/bacnet/basic/object/msv/CMakeLists.txt index d7ca77c9..0c8734dc 100644 --- a/test/bacnet/basic/object/msv/CMakeLists.txt +++ b/test/bacnet/basic/object/msv/CMakeLists.txt @@ -27,6 +27,7 @@ add_compile_definitions( include_directories( ${SRC_DIR} + ${TST_DIR}/bacnet/basic/object ${TST_DIR}/ztest/include ) @@ -43,23 +44,26 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/bacreal.c ${SRC_DIR}/bacnet/bacstr.c ${SRC_DIR}/bacnet/bactext.c - ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/cov.c ${SRC_DIR}/bacnet/datetime.c - ${SRC_DIR}/bacnet/basic/sys/days.c ${SRC_DIR}/bacnet/indtext.c ${SRC_DIR}/bacnet/hostnport.c ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/proplist.c ${SRC_DIR}/bacnet/timestamp.c - ${SRC_DIR}/bacnet/memcopy.c ${SRC_DIR}/bacnet/wp.c ${SRC_DIR}/bacnet/weeklyschedule.c ${SRC_DIR}/bacnet/bactimevalue.c ${SRC_DIR}/bacnet/dailyschedule.c ${SRC_DIR}/bacnet/calendar_entry.c ${SRC_DIR}/bacnet/special_event.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 # Test and test library files ./src/main.c + ${TST_DIR}/bacnet/basic/object/property_test.c ${ZTST_DIR}/ztest_mock.c ${ZTST_DIR}/ztest.c ) diff --git a/test/bacnet/basic/object/msv/src/main.c b/test/bacnet/basic/object/msv/src/main.c index 2b560acf..af196383 100644 --- a/test/bacnet/basic/object/msv/src/main.c +++ b/test/bacnet/basic/object/msv/src/main.c @@ -1,16 +1,16 @@ -/* - * Copyright (c) 2020 Legrand North America, LLC. +/** + * @file + * @brief Unit test for object + * @author Steve Karg + * @date April 2024 + * @section LICENSE * * SPDX-License-Identifier: MIT */ - -/* @file - * @brief test BACnet integer encode/decode APIs - */ - #include #include #include +#include /** * @addtogroup bacnet_tests @@ -26,70 +26,26 @@ ZTEST(msv_tests, testMultistateValue) static void testMultistateValue(void) #endif { - uint8_t apdu[MAX_APDU] = { 0 }; - int len = 0; - int test_len = 0; - BACNET_READ_PROPERTY_DATA rpdata; - /* for decode value data */ - BACNET_APPLICATION_DATA_VALUE value; - const int *pRequired = NULL; - const int *pOptional = NULL; - const int *pProprietary = NULL; - unsigned count = 0; bool status = false; + unsigned count = 0; + uint32_t object_instance = BACNET_MAX_INSTANCE, test_object_instance = 0; + const int skip_fail_property_list[] = { -1 }; Multistate_Value_Init(); + object_instance = Multistate_Value_Create(object_instance); count = Multistate_Value_Count(); - zassert_true(count > 0, NULL); - rpdata.application_data = &apdu[0]; - rpdata.application_data_len = sizeof(apdu); - rpdata.object_type = OBJECT_MULTI_STATE_VALUE; - rpdata.object_instance = Multistate_Value_Index_To_Instance(0);; - status = Multistate_Value_Valid_Instance(rpdata.object_instance); + zassert_true(count == 1, NULL); + test_object_instance = Multistate_Value_Index_To_Instance(0); + zassert_equal(object_instance, test_object_instance, NULL); + bacnet_object_properties_read_write_test( + OBJECT_MULTI_STATE_VALUE, + object_instance, + Multistate_Value_Property_Lists, + Multistate_Value_Read_Property, + Multistate_Value_Write_Property, + skip_fail_property_list); + status = Multistate_Value_Delete(object_instance); zassert_true(status, NULL); - Multistate_Value_Property_Lists(&pRequired, &pOptional, &pProprietary); - while ((*pRequired) != -1) { - rpdata.object_property = *pRequired; - rpdata.array_index = BACNET_ARRAY_ALL; - len = Multistate_Value_Read_Property(&rpdata); - zassert_not_equal(len, BACNET_STATUS_ERROR, NULL); - if (len > 0) { - test_len = bacapp_decode_application_data(rpdata.application_data, - (uint8_t)rpdata.application_data_len, &value); - if (len != test_len) { - printf("property '%s': failed to decode!\n", - bactext_property_name(rpdata.object_property)); - } - if (rpdata.object_property == PROP_PRIORITY_ARRAY) { - /* FIXME: known fail to decode */ - len = test_len; - } - zassert_true(test_len >= 0, NULL); - } else { - printf("property '%s': failed to read!\n", - bactext_property_name(rpdata.object_property)); - } - pRequired++; - } - while ((*pOptional) != -1) { - rpdata.object_property = *pOptional; - rpdata.array_index = BACNET_ARRAY_ALL; - len = Multistate_Value_Read_Property(&rpdata); - zassert_not_equal(len, BACNET_STATUS_ERROR, NULL); - if (len > 0) { - test_len = bacapp_decode_application_data(rpdata.application_data, - (uint8_t)rpdata.application_data_len, &value); - if (len != test_len) { - printf("property '%s': failed to decode!\n", - bactext_property_name(rpdata.object_property)); - } - zassert_true(test_len >= 0, NULL); - } else { - printf("property '%s': failed to read!\n", - bactext_property_name(rpdata.object_property)); - } - pOptional++; - } } /** * @} diff --git a/zephyr/tests/bacnet/basic/object/ai/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/ai/CMakeLists.txt index 99a09f02..41ed1095 100644 --- a/zephyr/tests/bacnet/basic/object/ai/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/ai/CMakeLists.txt @@ -20,12 +20,18 @@ get_filename_component(BACNET_NAME ${BACNET_BASE} NAME) # Update include path for this module list(APPEND BACNET_INCLUDE ${BACNET_BASE}/src) +set(TEST_OBJECT_SRC ${BACNET_BASE}/test/bacnet/basic/object) +list(APPEND TEST_OBJECT_INCLUDE ${TEST_OBJECT_SRC}) + if(BOARD STREQUAL unit_testing) file(RELATIVE_PATH BACNET_INCLUDE $ENV{ZEPHYR_BASE} ${BACNET_BASE}/src) - list(APPEND INCLUDE ${BACNET_INCLUDE}) + file(RELATIVE_PATH TEST_OBJECT_INCLUDE $ENV{ZEPHYR_BASE} ${TEST_OBJECT_SRC}) + list(APPEND INCLUDE ${BACNET_INCLUDE} ${TEST_OBJECT_INCLUDE}) list(APPEND SOURCES ${BACNET_SRC_PATH}.c ${BACNET_TEST_PATH}/src/main.c + ${BACNET_TEST_PATH}/stubs.c + ${TEST_OBJECT_SRC}/property_test.c ) get_filename_component(BACNET_OBJECT_SRC ${BACNET_SRC_PATH} PATH) @@ -39,25 +45,27 @@ if(BOARD STREQUAL unit_testing) ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c + ${BACNET_SRC}/bactext.c + ${BACNET_SRC}/cov.c ${BACNET_SRC}/datetime.c ${BACNET_SRC}/timestamp.c - ${BACNET_SRC}/basic/sys/days.c ${BACNET_SRC}/bacdevobjpropref.c - ${BACNET_SRC}/bactext.c ${BACNET_SRC}/indtext.c ${BACNET_SRC}/lighting.c ${BACNET_SRC}/proplist.c ${BACNET_SRC}/wp.c - ${BACNET_SRC}/cov.c ${BACNET_SRC}/memcopy.c ${BACNET_SRC}/hostnport.c ${BACNET_SRC}/dailyschedule.c ${BACNET_SRC}/weeklyschedule.c + ${BACNET_SRC}/bactimevalue.c ${BACNET_SRC}/calendar_entry.c ${BACNET_SRC}/special_event.c ${BACNET_SRC}/basic/sys/bigend.c - ${BACNET_SRC}/bactimevalue.c - ) + ${BACNET_SRC}/basic/sys/days.c + ${BACNET_SRC}/basic/sys/debug.c + ${BACNET_SRC}/basic/sys/keylist.c + ) set(CONF_FILE "${CONF_FILE};prj.unit_testing.conf") find_package(Zephyr COMPONENTS unittest REQUIRED HINTS $ENV{ZEPHYR_BASE}) @@ -66,9 +74,13 @@ else() find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(${BACNET_NAME}) - target_include_directories(app PRIVATE ${BACNET_INCLUDE}) + target_include_directories(app PRIVATE + ${BACNET_INCLUDE} + ${TEST_OBJECT_INCLUDE}) target_sources(app PRIVATE ${BACNET_TEST_PATH}/src/main.c + ${BACNET_TEST_PATH}/stubs.c + ${TEST_OBJECT_SRC}/property_test.c ) endif() diff --git a/zephyr/tests/bacnet/basic/object/ao/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/ao/CMakeLists.txt index efa33480..bbd75f54 100644 --- a/zephyr/tests/bacnet/basic/object/ao/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/ao/CMakeLists.txt @@ -20,41 +20,50 @@ get_filename_component(BACNET_NAME ${BACNET_BASE} NAME) # Update include path for this module list(APPEND BACNET_INCLUDE ${BACNET_BASE}/src) +set(TEST_OBJECT_SRC ${BACNET_BASE}/test/bacnet/basic/object) +list(APPEND TEST_OBJECT_INCLUDE ${TEST_OBJECT_SRC}) + if(BOARD STREQUAL unit_testing) file(RELATIVE_PATH BACNET_INCLUDE $ENV{ZEPHYR_BASE} ${BACNET_BASE}/src) - list(APPEND INCLUDE ${BACNET_INCLUDE}) + file(RELATIVE_PATH TEST_OBJECT_INCLUDE $ENV{ZEPHYR_BASE} ${TEST_OBJECT_SRC}) + list(APPEND INCLUDE ${BACNET_INCLUDE} ${TEST_OBJECT_INCLUDE}) list(APPEND SOURCES ${BACNET_SRC_PATH}.c ${BACNET_TEST_PATH}/src/main.c + ${TEST_OBJECT_SRC}/property_test.c ) get_filename_component(BACNET_OBJECT_SRC ${BACNET_SRC_PATH} PATH) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES - ${BACNET_SRC}/bacaddr.c - ${BACNET_SRC}/bacapp.c - ${BACNET_SRC}/bacdcode.c - ${BACNET_SRC}/bacdest.c - ${BACNET_SRC}/bacstr.c - ${BACNET_SRC}/bacint.c - ${BACNET_SRC}/bacreal.c - ${BACNET_SRC}/datetime.c - ${BACNET_SRC}/timestamp.c - ${BACNET_SRC}/basic/sys/days.c - ${BACNET_SRC}/bacdevobjpropref.c - ${BACNET_SRC}/bactext.c - ${BACNET_SRC}/indtext.c - ${BACNET_SRC}/lighting.c - ${BACNET_SRC}/wp.c - ${BACNET_SRC}/hostnport.c - ${BACNET_SRC}/dailyschedule.c - ${BACNET_SRC}/weeklyschedule.c - ${BACNET_SRC}/calendar_entry.c - ${BACNET_SRC}/special_event.c - ${BACNET_SRC}/basic/sys/bigend.c - ${BACNET_SRC}/bactimevalue.c - ) + ${BACNET_SRC}/bacaddr.c + ${BACNET_SRC}/bacapp.c + ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c + ${BACNET_SRC}/bacstr.c + ${BACNET_SRC}/bacint.c + ${BACNET_SRC}/bacreal.c + ${BACNET_SRC}/datetime.c + ${BACNET_SRC}/bacdevobjpropref.c + ${BACNET_SRC}/bactext.c + ${BACNET_SRC}/cov.c + ${BACNET_SRC}/indtext.c + ${BACNET_SRC}/lighting.c + ${BACNET_SRC}/proplist.c + ${BACNET_SRC}/timestamp.c + ${BACNET_SRC}/wp.c + ${BACNET_SRC}/hostnport.c + ${BACNET_SRC}/dailyschedule.c + ${BACNET_SRC}/weeklyschedule.c + ${BACNET_SRC}/calendar_entry.c + ${BACNET_SRC}/special_event.c + ${BACNET_SRC}/bactimevalue.c + ${BACNET_SRC}/basic/sys/bigend.c + ${BACNET_SRC}/basic/sys/days.c + ${BACNET_SRC}/basic/sys/debug.c + ${BACNET_SRC}/basic/sys/keylist.c +) set(CONF_FILE "${CONF_FILE};prj.unit_testing.conf") find_package(Zephyr COMPONENTS unittest REQUIRED HINTS $ENV{ZEPHYR_BASE}) @@ -63,9 +72,12 @@ else() find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(${BACNET_NAME}) - target_include_directories(app PRIVATE ${BACNET_INCLUDE}) + target_include_directories(app PRIVATE + ${BACNET_INCLUDE} + ${TEST_OBJECT_INCLUDE}) target_sources(app PRIVATE ${BACNET_TEST_PATH}/src/main.c + ${TEST_OBJECT_SRC}/property_test.c ) endif() diff --git a/zephyr/tests/bacnet/basic/object/av/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/av/CMakeLists.txt index dc9f469d..5c5e76d8 100644 --- a/zephyr/tests/bacnet/basic/object/av/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/av/CMakeLists.txt @@ -20,12 +20,18 @@ get_filename_component(BACNET_NAME ${BACNET_BASE} NAME) # Update include path for this module list(APPEND BACNET_INCLUDE ${BACNET_BASE}/src) +set(TEST_OBJECT_SRC ${BACNET_BASE}/test/bacnet/basic/object) +list(APPEND TEST_OBJECT_INCLUDE ${TEST_OBJECT_SRC}) + if(BOARD STREQUAL unit_testing) file(RELATIVE_PATH BACNET_INCLUDE $ENV{ZEPHYR_BASE} ${BACNET_BASE}/src) - list(APPEND INCLUDE ${BACNET_INCLUDE}) + file(RELATIVE_PATH TEST_OBJECT_INCLUDE $ENV{ZEPHYR_BASE} ${TEST_OBJECT_SRC}) + list(APPEND INCLUDE ${BACNET_INCLUDE} ${TEST_OBJECT_INCLUDE}) list(APPEND SOURCES ${BACNET_SRC_PATH}.c ${BACNET_TEST_PATH}/src/main.c + ${BACNET_TEST_PATH}/stubs.c + ${TEST_OBJECT_SRC}/property_test.c ) get_filename_component(BACNET_OBJECT_SRC ${BACNET_SRC_PATH} PATH) @@ -40,22 +46,25 @@ if(BOARD STREQUAL unit_testing) ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c ${BACNET_SRC}/datetime.c - ${BACNET_SRC}/timestamp.c - ${BACNET_SRC}/basic/sys/days.c ${BACNET_SRC}/bacdevobjpropref.c ${BACNET_SRC}/bactext.c + ${BACNET_SRC}/cov.c ${BACNET_SRC}/indtext.c ${BACNET_SRC}/lighting.c ${BACNET_SRC}/proplist.c + ${BACNET_SRC}/timestamp.c ${BACNET_SRC}/wp.c ${BACNET_SRC}/hostnport.c ${BACNET_SRC}/dailyschedule.c ${BACNET_SRC}/weeklyschedule.c ${BACNET_SRC}/calendar_entry.c ${BACNET_SRC}/special_event.c - ${BACNET_SRC}/basic/sys/bigend.c ${BACNET_SRC}/bactimevalue.c - ) + ${BACNET_SRC}/basic/sys/bigend.c + ${BACNET_SRC}/basic/sys/days.c + ${BACNET_SRC}/basic/sys/debug.c + ${BACNET_SRC}/basic/sys/keylist.c + ) set(CONF_FILE "${CONF_FILE};prj.unit_testing.conf") find_package(Zephyr COMPONENTS unittest REQUIRED HINTS $ENV{ZEPHYR_BASE}) @@ -64,9 +73,13 @@ else() find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(${BACNET_NAME}) - target_include_directories(app PRIVATE ${BACNET_INCLUDE}) + target_include_directories(app PRIVATE + ${BACNET_INCLUDE} + ${TEST_OBJECT_INCLUDE}) target_sources(app PRIVATE ${BACNET_TEST_PATH}/src/main.c + ${BACNET_TEST_PATH}/stubs.c + ${TEST_OBJECT_SRC}/property_test.c ) endif() diff --git a/zephyr/tests/bacnet/basic/object/bi/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/bi/CMakeLists.txt index efa33480..37d7f398 100644 --- a/zephyr/tests/bacnet/basic/object/bi/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/bi/CMakeLists.txt @@ -20,12 +20,17 @@ get_filename_component(BACNET_NAME ${BACNET_BASE} NAME) # Update include path for this module list(APPEND BACNET_INCLUDE ${BACNET_BASE}/src) +set(TEST_OBJECT_SRC ${BACNET_BASE}/test/bacnet/basic/object) +list(APPEND TEST_OBJECT_INCLUDE ${TEST_OBJECT_SRC}) + if(BOARD STREQUAL unit_testing) file(RELATIVE_PATH BACNET_INCLUDE $ENV{ZEPHYR_BASE} ${BACNET_BASE}/src) - list(APPEND INCLUDE ${BACNET_INCLUDE}) + file(RELATIVE_PATH TEST_OBJECT_INCLUDE $ENV{ZEPHYR_BASE} ${TEST_OBJECT_SRC}) + list(APPEND INCLUDE ${BACNET_INCLUDE} ${TEST_OBJECT_INCLUDE}) list(APPEND SOURCES ${BACNET_SRC_PATH}.c ${BACNET_TEST_PATH}/src/main.c + ${TEST_OBJECT_SRC}/property_test.c ) get_filename_component(BACNET_OBJECT_SRC ${BACNET_SRC_PATH} PATH) @@ -41,19 +46,23 @@ if(BOARD STREQUAL unit_testing) ${BACNET_SRC}/bacreal.c ${BACNET_SRC}/datetime.c ${BACNET_SRC}/timestamp.c - ${BACNET_SRC}/basic/sys/days.c ${BACNET_SRC}/bacdevobjpropref.c ${BACNET_SRC}/bactext.c + ${BACNET_SRC}/cov.c ${BACNET_SRC}/indtext.c ${BACNET_SRC}/lighting.c + ${BACNET_SRC}/proplist.c ${BACNET_SRC}/wp.c ${BACNET_SRC}/hostnport.c ${BACNET_SRC}/dailyschedule.c ${BACNET_SRC}/weeklyschedule.c ${BACNET_SRC}/calendar_entry.c ${BACNET_SRC}/special_event.c - ${BACNET_SRC}/basic/sys/bigend.c ${BACNET_SRC}/bactimevalue.c + ${BACNET_SRC}/basic/sys/bigend.c + ${BACNET_SRC}/basic/sys/days.c + ${BACNET_SRC}/basic/sys/debug.c + ${BACNET_SRC}/basic/sys/keylist.c ) set(CONF_FILE "${CONF_FILE};prj.unit_testing.conf") @@ -63,9 +72,12 @@ else() find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(${BACNET_NAME}) - target_include_directories(app PRIVATE ${BACNET_INCLUDE}) + target_include_directories(app PRIVATE + ${BACNET_INCLUDE} + ${TEST_OBJECT_INCLUDE}) target_sources(app PRIVATE ${BACNET_TEST_PATH}/src/main.c + ${TEST_OBJECT_SRC}/property_test.c ) endif() diff --git a/zephyr/tests/bacnet/basic/object/bo/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/bo/CMakeLists.txt index 33497f88..cb63f6c6 100644 --- a/zephyr/tests/bacnet/basic/object/bo/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/bo/CMakeLists.txt @@ -20,12 +20,17 @@ get_filename_component(BACNET_NAME ${BACNET_BASE} NAME) # Update include path for this module list(APPEND BACNET_INCLUDE ${BACNET_BASE}/src) +set(TEST_OBJECT_SRC ${BACNET_BASE}/test/bacnet/basic/object) +list(APPEND TEST_OBJECT_INCLUDE ${TEST_OBJECT_SRC}) + if(BOARD STREQUAL unit_testing) file(RELATIVE_PATH BACNET_INCLUDE $ENV{ZEPHYR_BASE} ${BACNET_BASE}/src) - list(APPEND INCLUDE ${BACNET_INCLUDE}) + file(RELATIVE_PATH TEST_OBJECT_INCLUDE $ENV{ZEPHYR_BASE} ${TEST_OBJECT_SRC}) + list(APPEND INCLUDE ${BACNET_INCLUDE} ${TEST_OBJECT_INCLUDE}) list(APPEND SOURCES ${BACNET_SRC_PATH}.c ${BACNET_TEST_PATH}/src/main.c + ${TEST_OBJECT_SRC}/property_test.c ) get_filename_component(BACNET_OBJECT_SRC ${BACNET_SRC_PATH} PATH) @@ -40,32 +45,25 @@ if(BOARD STREQUAL unit_testing) ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c ${BACNET_SRC}/datetime.c - ${BACNET_SRC}/timestamp.c - ${BACNET_SRC}/basic/sys/days.c ${BACNET_SRC}/bacdevobjpropref.c ${BACNET_SRC}/bactext.c + ${BACNET_SRC}/cov.c ${BACNET_SRC}/indtext.c ${BACNET_SRC}/lighting.c + ${BACNET_SRC}/proplist.c + ${BACNET_SRC}/timestamp.c ${BACNET_SRC}/wp.c ${BACNET_SRC}/hostnport.c ${BACNET_SRC}/dailyschedule.c ${BACNET_SRC}/weeklyschedule.c ${BACNET_SRC}/calendar_entry.c ${BACNET_SRC}/special_event.c - ${BACNET_SRC}/basic/sys/bigend.c ${BACNET_SRC}/bactimevalue.c + ${BACNET_SRC}/basic/sys/bigend.c + ${BACNET_SRC}/basic/sys/days.c + ${BACNET_SRC}/basic/sys/debug.c ${BACNET_SRC}/basic/sys/keylist.c - ${BACNET_SRC}/basic/object/device.c - ${BACNET_SRC}/proplist.c - ${BACNET_SRC}/cov.c - ${BACNET_SRC}/dcc.c - ${BACNET_SRC}/basic/service/h_apdu.c - ${BACNET_SRC}/basic/binding/address.c - ${BACNET_SRC}/basic/service/h_cov.c - ${BACNET_SRC}/npdu.c - ${BACNET_SRC}/abort.c - ${BACNET_SRC}/bacerror.c - ) + ) set(CONF_FILE "${CONF_FILE};prj.unit_testing.conf") find_package(Zephyr COMPONENTS unittest REQUIRED HINTS $ENV{ZEPHYR_BASE}) @@ -74,9 +72,12 @@ else() find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(${BACNET_NAME}) - target_include_directories(app PRIVATE ${BACNET_INCLUDE}) + target_include_directories(app PRIVATE + ${BACNET_INCLUDE} + ${TEST_OBJECT_INCLUDE}) target_sources(app PRIVATE ${BACNET_TEST_PATH}/src/main.c + ${TEST_OBJECT_SRC}/property_test.c ) endif() diff --git a/zephyr/tests/bacnet/basic/object/bv/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/bv/CMakeLists.txt index efa33480..937e9416 100644 --- a/zephyr/tests/bacnet/basic/object/bv/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/bv/CMakeLists.txt @@ -20,41 +20,50 @@ get_filename_component(BACNET_NAME ${BACNET_BASE} NAME) # Update include path for this module list(APPEND BACNET_INCLUDE ${BACNET_BASE}/src) +set(TEST_OBJECT_SRC ${BACNET_BASE}/test/bacnet/basic/object) +list(APPEND TEST_OBJECT_INCLUDE ${TEST_OBJECT_SRC}) + if(BOARD STREQUAL unit_testing) file(RELATIVE_PATH BACNET_INCLUDE $ENV{ZEPHYR_BASE} ${BACNET_BASE}/src) - list(APPEND INCLUDE ${BACNET_INCLUDE}) + file(RELATIVE_PATH TEST_OBJECT_INCLUDE $ENV{ZEPHYR_BASE} ${TEST_OBJECT_SRC}) + list(APPEND INCLUDE ${BACNET_INCLUDE} ${TEST_OBJECT_INCLUDE}) list(APPEND SOURCES ${BACNET_SRC_PATH}.c ${BACNET_TEST_PATH}/src/main.c + ${TEST_OBJECT_SRC}/property_test.c ) get_filename_component(BACNET_OBJECT_SRC ${BACNET_SRC_PATH} PATH) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES - ${BACNET_SRC}/bacaddr.c - ${BACNET_SRC}/bacapp.c - ${BACNET_SRC}/bacdcode.c - ${BACNET_SRC}/bacdest.c - ${BACNET_SRC}/bacstr.c - ${BACNET_SRC}/bacint.c - ${BACNET_SRC}/bacreal.c - ${BACNET_SRC}/datetime.c - ${BACNET_SRC}/timestamp.c - ${BACNET_SRC}/basic/sys/days.c - ${BACNET_SRC}/bacdevobjpropref.c - ${BACNET_SRC}/bactext.c - ${BACNET_SRC}/indtext.c - ${BACNET_SRC}/lighting.c - ${BACNET_SRC}/wp.c - ${BACNET_SRC}/hostnport.c - ${BACNET_SRC}/dailyschedule.c - ${BACNET_SRC}/weeklyschedule.c - ${BACNET_SRC}/calendar_entry.c - ${BACNET_SRC}/special_event.c - ${BACNET_SRC}/basic/sys/bigend.c - ${BACNET_SRC}/bactimevalue.c - ) + ${BACNET_SRC}/bacaddr.c + ${BACNET_SRC}/bacapp.c + ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c + ${BACNET_SRC}/bacstr.c + ${BACNET_SRC}/bacint.c + ${BACNET_SRC}/bacreal.c + ${BACNET_SRC}/datetime.c + ${BACNET_SRC}/bacdevobjpropref.c + ${BACNET_SRC}/bactext.c + ${BACNET_SRC}/cov.c + ${BACNET_SRC}/indtext.c + ${BACNET_SRC}/lighting.c + ${BACNET_SRC}/proplist.c + ${BACNET_SRC}/timestamp.c + ${BACNET_SRC}/wp.c + ${BACNET_SRC}/hostnport.c + ${BACNET_SRC}/dailyschedule.c + ${BACNET_SRC}/weeklyschedule.c + ${BACNET_SRC}/calendar_entry.c + ${BACNET_SRC}/special_event.c + ${BACNET_SRC}/bactimevalue.c + ${BACNET_SRC}/basic/sys/bigend.c + ${BACNET_SRC}/basic/sys/days.c + ${BACNET_SRC}/basic/sys/debug.c + ${BACNET_SRC}/basic/sys/keylist.c + ) set(CONF_FILE "${CONF_FILE};prj.unit_testing.conf") find_package(Zephyr COMPONENTS unittest REQUIRED HINTS $ENV{ZEPHYR_BASE}) @@ -63,9 +72,12 @@ else() find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(${BACNET_NAME}) - target_include_directories(app PRIVATE ${BACNET_INCLUDE}) + target_include_directories(app PRIVATE + ${BACNET_INCLUDE} + ${TEST_OBJECT_INCLUDE}) target_sources(app PRIVATE ${BACNET_TEST_PATH}/src/main.c + ${TEST_OBJECT_SRC}/property_test.c ) endif() diff --git a/zephyr/tests/bacnet/basic/object/ms-input/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/ms-input/CMakeLists.txt index 882fcb8f..937e9416 100644 --- a/zephyr/tests/bacnet/basic/object/ms-input/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/ms-input/CMakeLists.txt @@ -20,42 +20,50 @@ get_filename_component(BACNET_NAME ${BACNET_BASE} NAME) # Update include path for this module list(APPEND BACNET_INCLUDE ${BACNET_BASE}/src) +set(TEST_OBJECT_SRC ${BACNET_BASE}/test/bacnet/basic/object) +list(APPEND TEST_OBJECT_INCLUDE ${TEST_OBJECT_SRC}) + if(BOARD STREQUAL unit_testing) file(RELATIVE_PATH BACNET_INCLUDE $ENV{ZEPHYR_BASE} ${BACNET_BASE}/src) - list(APPEND INCLUDE ${BACNET_INCLUDE}) + file(RELATIVE_PATH TEST_OBJECT_INCLUDE $ENV{ZEPHYR_BASE} ${TEST_OBJECT_SRC}) + list(APPEND INCLUDE ${BACNET_INCLUDE} ${TEST_OBJECT_INCLUDE}) list(APPEND SOURCES ${BACNET_SRC_PATH}.c ${BACNET_TEST_PATH}/src/main.c - ${BACNET_TEST_PATH}/../mock/device_mock.c + ${TEST_OBJECT_SRC}/property_test.c ) get_filename_component(BACNET_OBJECT_SRC ${BACNET_SRC_PATH} PATH) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES - ${BACNET_SRC}/bacaddr.c - ${BACNET_SRC}/bacapp.c - ${BACNET_SRC}/bacdcode.c - ${BACNET_SRC}/bacdest.c - ${BACNET_SRC}/bacstr.c - ${BACNET_SRC}/bacint.c - ${BACNET_SRC}/bacreal.c - ${BACNET_SRC}/datetime.c - ${BACNET_SRC}/timestamp.c - ${BACNET_SRC}/basic/sys/days.c - ${BACNET_SRC}/bacdevobjpropref.c - ${BACNET_SRC}/bactext.c - ${BACNET_SRC}/indtext.c - ${BACNET_SRC}/lighting.c - ${BACNET_SRC}/wp.c - ${BACNET_SRC}/hostnport.c - ${BACNET_SRC}/dailyschedule.c - ${BACNET_SRC}/weeklyschedule.c - ${BACNET_SRC}/calendar_entry.c - ${BACNET_SRC}/special_event.c - ${BACNET_SRC}/basic/sys/bigend.c - ${BACNET_SRC}/bactimevalue.c - ) + ${BACNET_SRC}/bacaddr.c + ${BACNET_SRC}/bacapp.c + ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c + ${BACNET_SRC}/bacstr.c + ${BACNET_SRC}/bacint.c + ${BACNET_SRC}/bacreal.c + ${BACNET_SRC}/datetime.c + ${BACNET_SRC}/bacdevobjpropref.c + ${BACNET_SRC}/bactext.c + ${BACNET_SRC}/cov.c + ${BACNET_SRC}/indtext.c + ${BACNET_SRC}/lighting.c + ${BACNET_SRC}/proplist.c + ${BACNET_SRC}/timestamp.c + ${BACNET_SRC}/wp.c + ${BACNET_SRC}/hostnport.c + ${BACNET_SRC}/dailyschedule.c + ${BACNET_SRC}/weeklyschedule.c + ${BACNET_SRC}/calendar_entry.c + ${BACNET_SRC}/special_event.c + ${BACNET_SRC}/bactimevalue.c + ${BACNET_SRC}/basic/sys/bigend.c + ${BACNET_SRC}/basic/sys/days.c + ${BACNET_SRC}/basic/sys/debug.c + ${BACNET_SRC}/basic/sys/keylist.c + ) set(CONF_FILE "${CONF_FILE};prj.unit_testing.conf") find_package(Zephyr COMPONENTS unittest REQUIRED HINTS $ENV{ZEPHYR_BASE}) @@ -64,9 +72,12 @@ else() find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(${BACNET_NAME}) - target_include_directories(app PRIVATE ${BACNET_INCLUDE}) + target_include_directories(app PRIVATE + ${BACNET_INCLUDE} + ${TEST_OBJECT_INCLUDE}) target_sources(app PRIVATE ${BACNET_TEST_PATH}/src/main.c + ${TEST_OBJECT_SRC}/property_test.c ) endif() diff --git a/zephyr/tests/bacnet/basic/object/mso/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/mso/CMakeLists.txt index efa33480..c4ff5edd 100644 --- a/zephyr/tests/bacnet/basic/object/mso/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/mso/CMakeLists.txt @@ -20,13 +20,18 @@ get_filename_component(BACNET_NAME ${BACNET_BASE} NAME) # Update include path for this module list(APPEND BACNET_INCLUDE ${BACNET_BASE}/src) +set(TEST_OBJECT_SRC ${BACNET_BASE}/test/bacnet/basic/object) +list(APPEND TEST_OBJECT_INCLUDE ${TEST_OBJECT_SRC}) + if(BOARD STREQUAL unit_testing) file(RELATIVE_PATH BACNET_INCLUDE $ENV{ZEPHYR_BASE} ${BACNET_BASE}/src) - list(APPEND INCLUDE ${BACNET_INCLUDE}) + file(RELATIVE_PATH TEST_OBJECT_INCLUDE $ENV{ZEPHYR_BASE} ${TEST_OBJECT_SRC}) + list(APPEND INCLUDE ${BACNET_INCLUDE} ${TEST_OBJECT_INCLUDE}) list(APPEND SOURCES ${BACNET_SRC_PATH}.c ${BACNET_TEST_PATH}/src/main.c - ) + ${TEST_OBJECT_SRC}/property_test.c + ) get_filename_component(BACNET_OBJECT_SRC ${BACNET_SRC_PATH} PATH) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) @@ -40,21 +45,25 @@ if(BOARD STREQUAL unit_testing) ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c ${BACNET_SRC}/datetime.c - ${BACNET_SRC}/timestamp.c - ${BACNET_SRC}/basic/sys/days.c ${BACNET_SRC}/bacdevobjpropref.c ${BACNET_SRC}/bactext.c + ${BACNET_SRC}/cov.c ${BACNET_SRC}/indtext.c ${BACNET_SRC}/lighting.c + ${BACNET_SRC}/proplist.c + ${BACNET_SRC}/timestamp.c ${BACNET_SRC}/wp.c ${BACNET_SRC}/hostnport.c ${BACNET_SRC}/dailyschedule.c ${BACNET_SRC}/weeklyschedule.c ${BACNET_SRC}/calendar_entry.c ${BACNET_SRC}/special_event.c - ${BACNET_SRC}/basic/sys/bigend.c ${BACNET_SRC}/bactimevalue.c - ) + ${BACNET_SRC}/basic/sys/bigend.c + ${BACNET_SRC}/basic/sys/days.c + ${BACNET_SRC}/basic/sys/debug.c + ${BACNET_SRC}/basic/sys/keylist.c + ) set(CONF_FILE "${CONF_FILE};prj.unit_testing.conf") find_package(Zephyr COMPONENTS unittest REQUIRED HINTS $ENV{ZEPHYR_BASE}) @@ -63,9 +72,12 @@ else() find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(${BACNET_NAME}) - target_include_directories(app PRIVATE ${BACNET_INCLUDE}) + target_include_directories(app PRIVATE + ${BACNET_INCLUDE} + ${TEST_OBJECT_INCLUDE}) target_sources(app PRIVATE ${BACNET_TEST_PATH}/src/main.c + ${TEST_OBJECT_SRC}/property_test.c ) endif() diff --git a/zephyr/tests/bacnet/basic/object/msv/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/msv/CMakeLists.txt index 6c8cc384..3ab01401 100644 --- a/zephyr/tests/bacnet/basic/object/msv/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/msv/CMakeLists.txt @@ -19,12 +19,17 @@ get_filename_component(BACNET_NAME ${BACNET_BASE} NAME) # Update include path for this module list(APPEND BACNET_INCLUDE ${BACNET_BASE}/src) +set(TEST_OBJECT_SRC ${BACNET_BASE}/test/bacnet/basic/object) +list(APPEND TEST_OBJECT_INCLUDE ${TEST_OBJECT_SRC}) + if(BOARD STREQUAL unit_testing) file(RELATIVE_PATH BACNET_INCLUDE $ENV{ZEPHYR_BASE} ${BACNET_BASE}/src) - list(APPEND INCLUDE ${BACNET_INCLUDE}) + file(RELATIVE_PATH TEST_OBJECT_INCLUDE $ENV{ZEPHYR_BASE} ${TEST_OBJECT_SRC}) + list(APPEND INCLUDE ${BACNET_INCLUDE} ${TEST_OBJECT_INCLUDE}) list(APPEND SOURCES ${BACNET_SRC_PATH}.c ${BACNET_TEST_PATH}/src/main.c + ${TEST_OBJECT_SRC}/property_test.c ) get_filename_component(BACNET_OBJECT_SRC ${BACNET_SRC_PATH} PATH) @@ -39,23 +44,25 @@ if(BOARD STREQUAL unit_testing) ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c ${BACNET_SRC}/datetime.c - ${BACNET_SRC}/timestamp.c - ${BACNET_SRC}/basic/sys/days.c ${BACNET_SRC}/bacdevobjpropref.c ${BACNET_SRC}/bactext.c + ${BACNET_SRC}/cov.c ${BACNET_SRC}/indtext.c ${BACNET_SRC}/lighting.c + ${BACNET_SRC}/proplist.c + ${BACNET_SRC}/timestamp.c ${BACNET_SRC}/wp.c - ${BACNET_SRC}/cov.c - ${BACNET_SRC}/memcopy.c ${BACNET_SRC}/hostnport.c ${BACNET_SRC}/dailyschedule.c ${BACNET_SRC}/weeklyschedule.c ${BACNET_SRC}/calendar_entry.c ${BACNET_SRC}/special_event.c - ${BACNET_SRC}/basic/sys/bigend.c ${BACNET_SRC}/bactimevalue.c - ) + ${BACNET_SRC}/basic/sys/bigend.c + ${BACNET_SRC}/basic/sys/days.c + ${BACNET_SRC}/basic/sys/debug.c + ${BACNET_SRC}/basic/sys/keylist.c + ) set(CONF_FILE "${CONF_FILE};prj.unit_testing.conf") find_package(Zephyr COMPONENTS unittest REQUIRED HINTS $ENV{ZEPHYR_BASE}) @@ -64,9 +71,12 @@ else() find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(${BACNET_NAME}) - target_include_directories(app PRIVATE ${BACNET_INCLUDE}) + target_include_directories(app PRIVATE + ${BACNET_INCLUDE} + ${TEST_OBJECT_INCLUDE}) target_sources(app PRIVATE ${BACNET_TEST_PATH}/src/main.c + ${TEST_OBJECT_SRC}/property_test.c ) endif() diff --git a/zephyr/tests/bacnet/basic/object/time_value/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/time_value/CMakeLists.txt index 0f5819fc..312342b6 100644 --- a/zephyr/tests/bacnet/basic/object/time_value/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/time_value/CMakeLists.txt @@ -25,7 +25,7 @@ list(APPEND TEST_OBJECT_INCLUDE ${TEST_OBJECT_SRC}) if(BOARD STREQUAL unit_testing) file(RELATIVE_PATH BACNET_INCLUDE $ENV{ZEPHYR_BASE} ${BACNET_BASE}/src) -file(RELATIVE_PATH TEST_OBJECT_INCLUDE $ENV{ZEPHYR_BASE} ${TEST_OBJECT_SRC}) + file(RELATIVE_PATH TEST_OBJECT_INCLUDE $ENV{ZEPHYR_BASE} ${TEST_OBJECT_SRC}) list(APPEND INCLUDE ${BACNET_INCLUDE} ${TEST_OBJECT_INCLUDE}) list(APPEND SOURCES ${BACNET_SRC_PATH}.c