diff --git a/src/bacnet/basic/object/ao.c b/src/bacnet/basic/object/ao.c index b434ae52..79b11d98 100644 --- a/src/bacnet/basic/object/ao.c +++ b/src/bacnet/basic/object/ao.c @@ -1,6 +1,16 @@ -/************************************************************************** +/** + * @file + * @author Steve Karg + * @date 2005 + * @brief Analog Output objects, customize for your use * - * Copyright (C) 2005 Steve Karg + * @section DESCRIPTION + * + * The Analog Output object is an object with a present-value that + * uses a single precision floating point data type, and includes + * a present-value derived from the priority array. + * + * @section LICENSE * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -20,141 +30,163 @@ * 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 Output Objects - customize for your use */ - + */ #include #include #include +#include +#include +#include "bacnet/config.h" #include "bacnet/bacdef.h" #include "bacnet/bacdcode.h" #include "bacnet/bacenum.h" +#include "bacnet/bacerror.h" #include "bacnet/bacapp.h" -#include "bacnet/config.h" /* the custom stuff */ +#include "bacnet/bactext.h" +#include "bacnet/cov.h" +#include "bacnet/apdu.h" +#include "bacnet/npdu.h" +#include "bacnet/abort.h" +#include "bacnet/reject.h" +#include "bacnet/rp.h" #include "bacnet/wp.h" -#include "bacnet/basic/object/ao.h" +#include "bacnet/basic/object/device.h" #include "bacnet/basic/services.h" +#include "bacnet/basic/sys/keylist.h" +/* me! */ +#include "ao.h" -#ifndef MAX_ANALOG_OUTPUTS -#define MAX_ANALOG_OUTPUTS 4 -#endif - -/* we choose to have a NULL level in our system represented by */ -/* a particular value. When the priorities are not in use, they */ -/* will be relinquished (i.e. set to the NULL level). */ -#define AO_LEVEL_NULL 255 -/* When all the priorities are level null, the present value returns */ -/* the Relinquish Default value */ -#define AO_RELINQUISH_DEFAULT 0 -/* Here is our Priority Array. They are supposed to be Real, but */ -/* we don't have that kind of memory, so we will use a single byte */ -/* and load a Real for returning the value when asked. */ -static uint8_t Analog_Output_Level[MAX_ANALOG_OUTPUTS][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_ANALOG_OUTPUTS]; - -/* we need to have our arrays initialized before answering any calls */ -static bool Analog_Output_Initialized = false; +struct object_data { + bool Out_Of_Service : 1; + bool Overridden : 1; + bool Changed : 1; + float COV_Increment; + float Prior_Value; + bool Relinquished[BACNET_MAX_PRIORITY]; + float Priority_Array[BACNET_MAX_PRIORITY]; + float Relinquish_Default; + float Min_Pres_Value; + float Max_Pres_Value; + uint16_t Units; + uint8_t Reliability; + const char *Object_Name; + const char *Description; +}; +/* Key List for storing the object data sorted by instance number */ +static OS_Keylist Object_List; +/* callback for present value writes */ +static analog_output_write_present_value_callback + Analog_Output_Write_Present_Value_Callback; /* These three arrays are used by the ReadPropertyMultiple handler */ -static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER, + +static const int Analog_Output_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_UNITS, PROP_PRIORITY_ARRAY, - PROP_RELINQUISH_DEFAULT, -1 }; + PROP_RELINQUISH_DEFAULT, +#if (BACNET_PROTOCOL_REVISION >= 17) + PROP_CURRENT_COMMAND_PRIORITY, +#endif + -1 }; -static const int Properties_Optional[] = { -1 }; +static const int Analog_Output_Properties_Optional[] = { PROP_RELIABILITY, + PROP_DESCRIPTION, PROP_COV_INCREMENT, PROP_MIN_PRES_VALUE, + PROP_MAX_PRES_VALUE, -1 }; -static const int Properties_Proprietary[] = { -1 }; +static const int Analog_Output_Properties_Proprietary[] = { -1 }; +/** + * @brief Returns the list of required, optional, and proprietary properties. + * Used by ReadPropertyMultiple service. + * @param pRequired - pointer to list of int terminated by -1, of + * BACnet required properties for this object. + * @param pOptional - pointer to list of int terminated by -1, of + * BACnet optkional properties for this object. + * @param pProprietary - pointer to list of int terminated by -1, of + * BACnet proprietary properties for this object. + */ void Analog_Output_Property_Lists( const int **pRequired, const int **pOptional, const int **pProprietary) { if (pRequired) { - *pRequired = Properties_Required; + *pRequired = Analog_Output_Properties_Required; } if (pOptional) { - *pOptional = Properties_Optional; + *pOptional = Analog_Output_Properties_Optional; } if (pProprietary) { - *pProprietary = Properties_Proprietary; + *pProprietary = Analog_Output_Properties_Proprietary; } return; } -void Analog_Output_Init(void) -{ - unsigned i, j; - - if (!Analog_Output_Initialized) { - Analog_Output_Initialized = true; - - /* initialize all the analog output priority arrays to NULL */ - for (i = 0; i < MAX_ANALOG_OUTPUTS; i++) { - for (j = 0; j < BACNET_MAX_PRIORITY; j++) { - Analog_Output_Level[i][j] = AO_LEVEL_NULL; - } - } - } - - return; -} - -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need validate that the */ -/* given instance exists */ +/** + * @brief Determines if a given 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_Output_Valid_Instance(uint32_t object_instance) { - if (object_instance < MAX_ANALOG_OUTPUTS) { + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { return true; } return false; } -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then count how many you have */ +/** + * @brief Determines the number of Analog Value objects + * @return Number of Analog Value objects + */ unsigned Analog_Output_Count(void) { - return MAX_ANALOG_OUTPUTS; + 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 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_Output_Index_To_Instance(unsigned index) { - return index; + return Keylist_Key(Object_List, index); } -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need to return the index */ -/* that correlates to the correct instance number */ +/** + * @brief For a given object instance-number, determines a 0..N index + * of 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_Output_Instance_To_Index(uint32_t object_instance) { - unsigned index = MAX_ANALOG_OUTPUTS; - - if (object_instance < MAX_ANALOG_OUTPUTS) { - 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_Output_Present_Value(uint32_t object_instance) { - float value = AO_RELINQUISH_DEFAULT; - unsigned index = 0; - unsigned i = 0; + float value = 0.0; + uint8_t priority = 0; /* loop counter */ + struct object_data *pObject; - index = Analog_Output_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_OUTPUTS) { - for (i = 0; i < BACNET_MAX_PRIORITY; i++) { - if (Analog_Output_Level[index][i] != AO_LEVEL_NULL) { - value = (float)Analog_Output_Level[index][i]; + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + value = Analog_Output_Relinquish_Default(object_instance); + for (priority = 0; priority < BACNET_MAX_PRIORITY; priority++) { + if (!pObject->Relinquished[priority]) { + value = pObject->Priority_Array[priority]; break; } } @@ -163,17 +195,23 @@ float Analog_Output_Present_Value(uint32_t object_instance) return value; } -unsigned Analog_Output_Present_Value_Priority(uint32_t object_instance) +/** + * @brief For a given object instance-number, determines the priority + * @param object_instance - object-instance number of the object + * @return active priority 1..16, or 0 if no priority is active + */ +unsigned Analog_Output_Present_Value_Priority( + uint32_t object_instance) { - unsigned index = 0; /* instance to index conversion */ - unsigned i = 0; /* loop counter */ + unsigned p = 0; /* loop counter */ unsigned priority = 0; /* return value */ + struct object_data *pObject; - index = Analog_Output_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_OUTPUTS) { - for (i = 0; i < BACNET_MAX_PRIORITY; i++) { - if (Analog_Output_Level[index][i] != AO_LEVEL_NULL) { - priority = i + 1; + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + for (p = 0; p < BACNET_MAX_PRIORITY; p++) { + if (!pObject->Relinquished[p]) { + priority = p + 1; break; } } @@ -182,24 +220,149 @@ unsigned Analog_Output_Present_Value_Priority(uint32_t object_instance) return priority; } +/** + * @brief For a given object instance-number and priority 1..16, determines the + * priority-array value + * @param object_instance - object-instance number of the object + * @param priority - priority-array index value 1..16 + * + * @return priority-array value of the object + */ +static float Analog_Output_Priority_Array( + uint32_t object_instance, unsigned priority) +{ + float value = 0.0; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if ((priority >= BACNET_MIN_PRIORITY) && + (priority <= BACNET_MAX_PRIORITY)) { + value = pObject->Priority_Array[priority - 1]; + } + } + + return value; +} + +/** + * @brief For a given object instance-number and priority 1..16, determines + * if the priority-array slot is NULL + * @param object_instance - object-instance number of the object + * @param priority - priority-array index value 1..16 + * @return true if the priority array slot is NULL + */ +static bool Analog_Output_Priority_Array_Null( + uint32_t object_instance, unsigned priority) +{ + bool null_value = false; + + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if ((priority >= BACNET_MIN_PRIORITY) && + (priority <= BACNET_MAX_PRIORITY)) { + if (pObject->Relinquished[priority - 1]) { + null_value = true; + } + } + } + + return null_value; +} + +/** + * For a given object instance-number, determines the relinquish-default value + * + * @param object_instance - object-instance number + * + * @return relinquish-default value of the object + */ +float Analog_Output_Relinquish_Default(uint32_t object_instance) +{ + float value = 0.0; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + value = pObject->Relinquish_Default; + } + + return value; +} + +/** + * For a given object instance-number, sets the relinquish-default value + * + * @param object_instance - object-instance number of the object + * @param value - floating point analog output relinquish-default value + * + * @return true if values are within range and relinquish-default value is set. + */ +bool Analog_Output_Relinquish_Default_Set(uint32_t object_instance, float value) +{ + bool status = false; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + pObject->Relinquish_Default = value; + status = true; + } + + return status; +} + +/** + * 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_Output_Present_Value_COV_Detect( + struct object_data *pObject, float value) +{ + float prior_value = 0.0; + float cov_increment = 0.0; + float cov_delta = 0.0; + + 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) { + 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 + * @param priority - priority-array index value 1..16 + * @return true if values are within range and present-value is set. + */ bool Analog_Output_Present_Value_Set( uint32_t object_instance, float value, unsigned priority) { - unsigned index = 0; bool status = false; + struct object_data *pObject; - index = Analog_Output_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_OUTPUTS) { - if (priority && (priority <= BACNET_MAX_PRIORITY) && - (priority != 6 /* reserved */) && (value >= 0.0) && - (value <= 100.0)) { - Analog_Output_Level[index][priority - 1] = (uint8_t)value; - /* 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) */ + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if ((priority >= 1) && (priority <= BACNET_MAX_PRIORITY)) { + pObject->Relinquished[priority - 1] = false; + pObject->Priority_Array[priority - 1] = value; + Analog_Output_Present_Value_COV_Detect( + pObject, Analog_Output_Present_Value(object_instance)); status = true; } } @@ -207,23 +370,25 @@ bool Analog_Output_Present_Value_Set( return status; } +/** + * @brief For a given object instance-number, relinquishes the present-value + * @param object_instance - object-instance number of the object + * @param priority - priority-array index value 1..16 + * @return true if values are within range and present-value is relinquished. + */ bool Analog_Output_Present_Value_Relinquish( uint32_t object_instance, unsigned priority) { - unsigned index = 0; bool status = false; + struct object_data *pObject; - index = Analog_Output_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_OUTPUTS) { - if (priority && (priority <= BACNET_MAX_PRIORITY) && - (priority != 6 /* reserved */)) { - Analog_Output_Level[index][priority - 1] = AO_LEVEL_NULL; - /* 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) */ + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if ((priority >= 1) && (priority <= BACNET_MAX_PRIORITY)) { + pObject->Relinquished[priority - 1] = true; + pObject->Priority_Array[priority - 1] = 0.0; + Analog_Output_Present_Value_COV_Detect( + pObject, Analog_Output_Present_Value(object_instance)); status = true; } } @@ -231,90 +396,644 @@ bool Analog_Output_Present_Value_Relinquish( return status; } -/* note: the object name must be unique within this device */ -bool Analog_Output_Object_Name( - uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) +/** + * @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 priority - priority-array index value 1..16 + * @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 Analog_Output_Present_Value_Write( + uint32_t object_instance, float value, uint8_t priority, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code) { - static char text_string[32] = ""; /* okay for single thread */ bool status = false; + struct object_data *pObject; + float old_value = 0.0; + float new_value = 0.0; - if (object_instance < MAX_ANALOG_OUTPUTS) { - sprintf( - text_string, "ANALOG OUTPUT %lu", (unsigned long)object_instance); - status = characterstring_init_ansi(object_name, text_string); + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if ((priority >= 1) && (priority <= BACNET_MAX_PRIORITY) && + (value >= 0.0) && (value <= 100.0)) { + if (priority != 6) { + old_value = Analog_Output_Present_Value(object_instance); + Analog_Output_Present_Value_Set(object_instance, value, priority); + 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 output when the value of Out_Of_Service + is true. */ + } else if (Analog_Output_Write_Present_Value_Callback) { + new_value = Analog_Output_Present_Value(object_instance); + Analog_Output_Write_Present_Value_Callback( + object_instance, old_value, new_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; } -bool Analog_Output_Out_Of_Service(uint32_t instance) +/** + * @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 priority - priority-array index value 1..16 + * @param error_class - the BACnet error class + * @param error_code - BACnet Error code + * @return true if values are within range and write is requested + */ +static bool Analog_Output_Present_Value_Relinquish_Write( + uint32_t object_instance, uint8_t priority, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code) { - unsigned index = 0; - bool oos_flag = false; + bool status = false; + struct object_data *pObject; + float old_value = 0.0; + float new_value = 0.0; - index = Analog_Output_Instance_To_Index(instance); - if (index < MAX_ANALOG_OUTPUTS) { - oos_flag = Out_Of_Service[index]; + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if ((priority >= 1) && (priority <= BACNET_MAX_PRIORITY)) { + if (priority != 6) { + old_value = Analog_Output_Present_Value(object_instance); + Analog_Output_Present_Value_Relinquish(object_instance, priority); + 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 output when the value of Out_Of_Service + is true. */ + } else if (Analog_Output_Write_Present_Value_Callback) { + new_value = Analog_Output_Present_Value(object_instance); + Analog_Output_Write_Present_Value_Callback( + object_instance, old_value, new_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 oos_flag; + return status; } -void Analog_Output_Out_Of_Service_Set(uint32_t instance, bool oos_flag) +/** + * 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 Analog_Output_Object_Name( + uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) { - unsigned index = 0; + bool status = false; + struct object_data *pObject; + char name_text[32]; - index = Analog_Output_Instance_To_Index(instance); - if (index < MAX_ANALOG_OUTPUTS) { - Out_Of_Service[index] = oos_flag; + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (pObject->Object_Name) { + status = characterstring_init_ansi(object_name, + pObject->Object_Name); + } else { + snprintf(name_text, sizeof(name_text), "ANALOG OUTPUT %u", + object_instance); + status = characterstring_init_ansi(object_name, name_text); + } + } + + return status; +} + +/** + * 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 Analog_Output_Name_Set(uint32_t object_instance, char *new_name) +{ + bool status = false; /* return value */ + BACNET_CHARACTER_STRING object_name; + BACNET_OBJECT_TYPE found_type = 0; + uint32_t found_instance = 0; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject && new_name) { + /* All the object names in a device must be unique */ + characterstring_init_ansi(&object_name, new_name); + if (Device_Valid_Object_Name( + &object_name, &found_type, &found_instance)) { + if ((found_type == OBJECT_ANALOG_OUTPUT) && + (found_instance == object_instance)) { + /* writing same name to same object */ + status = true; + } else { + /* duplicate name! */ + status = false; + } + } else { + status = true; + pObject->Object_Name = new_name; + Device_Inc_Database_Revision(); + } + } + + return status; +} + +/** + * For a given object instance-number, returns the units property value + * + * @param object_instance - object-instance number of the object + * + * @return units property value + */ +uint16_t Analog_Output_Units(uint32_t object_instance) +{ + uint16_t units = UNITS_NO_UNITS; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + units = pObject->Units; + } + + return units; +} + +/** + * For a given object instance-number, sets the units property value + * + * @param object_instance - object-instance number of the object + * @param units - units property value + * + * @return true if the units property value was set + */ +bool Analog_Output_Units_Set(uint32_t object_instance, uint16_t units) +{ + bool status = false; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + pObject->Units = units; + status = true; + } + + return status; +} + +/** + * For a given object instance-number, returns the out-of-service + * status flag + * + * @param object_instance - object-instance number of the object + * + * @return out-of-service status flag + */ +bool Analog_Output_Out_Of_Service(uint32_t object_instance) +{ + bool value = false; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + value = pObject->Out_Of_Service; + } + + return value; +} + +/** + * @brief For a given object instance-number, sets the out-of-service status + * flag + * @param object_instance - object-instance number of the object + * @param value - boolean out-of-service value + * @return true if the out-of-service status flag was set + */ +void Analog_Output_Out_Of_Service_Set(uint32_t object_instance, bool value) +{ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (pObject->Out_Of_Service != value) { + pObject->Out_Of_Service = value; + pObject->Changed = true; + } } } -/* return apdu len, or BACNET_STATUS_ERROR on error */ +/** + * @brief For a given object instance-number, returns the overridden + * status flag value + * @param object_instance - object-instance number of the object + * @return out-of-service property value + */ +bool Analog_Output_Overridden(uint32_t object_instance) +{ + bool value = false; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + value = pObject->Overridden; + } + + return value; +} + +/** + * @brief For a given object instance-number, sets the overridden status flag + * @param object_instance - object-instance number of the object + * @param value - boolean out-of-service value + * @return true if the overridden status flag was set + */ +void Analog_Output_Overridden_Set(uint32_t object_instance, bool value) +{ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (pObject->Overridden != value) { + pObject->Overridden = value; + pObject->Changed = true; + } + } +} + +/** + * @brief For a given object instance-number, gets the reliability. + * @param object_instance - object-instance number of the object + * @return reliability value + */ +BACNET_RELIABILITY Analog_Output_Reliability(uint32_t object_instance) +{ + BACNET_RELIABILITY reliability = RELIABILITY_NO_FAULT_DETECTED; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + reliability = (BACNET_RELIABILITY)pObject->Reliability; + } + + return reliability; +} + +/** + * @brief For a given object instance-number, gets the Fault status flag + * @param object_instance - object-instance number of the object + * @return true the status flag is in Fault + */ +static bool Analog_Output_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 Analog_Output_Reliability_Set( + uint32_t object_instance, BACNET_RELIABILITY value) +{ + struct object_data *pObject; + bool status = false; + bool fault = false; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (value <= 255) { + fault = Analog_Output_Object_Fault(pObject); + pObject->Reliability = value; + if (fault != Analog_Output_Object_Fault(pObject)) { + pObject->Changed = 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 Analog_Output_Fault(uint32_t object_instance) +{ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + + return Analog_Output_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 *Analog_Output_Description(uint32_t object_instance) +{ + char *name = NULL; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, 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 Analog_Output_Description_Set(uint32_t object_instance, char *new_name) +{ + bool status = false; /* return value */ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject && new_name) { + status = true; + pObject->Description = new_name; + } + + return status; +} + +/** + * @brief For a given object instance-number, returns the min-pres-value + * @param object_instance - object-instance number of the object + * @return value or 0.0 if not found + */ +float Analog_Output_Min_Pres_Value(uint32_t object_instance) +{ + float value = 0.0; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + value = pObject->Min_Pres_Value; + } + + return value; +} + +/** + * @brief For a given object instance-number, sets the min-pres-value + * @param object_instance - object-instance number of the object + * @param value - value to be set + * @return true if valid object-instance and value within range + */ +bool Analog_Output_Min_Pres_Value_Set(uint32_t object_instance, float value) +{ + bool status = false; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + pObject->Min_Pres_Value = value; + status = true; + } + + return status; +} + +/** + * @brief For a given object instance-number, returns the max-pres-value + * @param object_instance - object-instance number of the object + * @return value or 0.0 if not found + */ +float Analog_Output_Max_Pres_Value(uint32_t object_instance) +{ + float value = 0.0; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + value = pObject->Max_Pres_Value; + } + + return value; +} + +/** + * @brief For a given object instance-number, sets the max-pres-value + * @param object_instance - object-instance number of the object + * @param value - value to be set + * @return true if valid object-instance and value within range + */ +bool Analog_Output_Max_Pres_Value_Set(uint32_t object_instance, float value) +{ + bool status = false; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + pObject->Max_Pres_Value = value; + status = true; + } + + 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 Analog_Output_Change_Of_Value(uint32_t object_instance) +{ + bool changed = false; + + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + changed = pObject->Changed; + } + + return changed; +} + +/** + * @brief Clear the COV change flag + * @param object_instance - object-instance number of the object + */ +void Analog_Output_Change_Of_Value_Clear(uint32_t object_instance) +{ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + pObject->Changed = 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 Analog_Output_Encode_Value_List( + uint32_t object_instance, BACNET_PROPERTY_VALUE *value_list) +{ + bool status = false; + struct object_data *pObject; + const bool in_alarm = false; + const bool fault = false; + const bool overridden = false; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + status = cov_value_list_encode_real(value_list, pObject->Prior_Value, + in_alarm, fault, overridden, pObject->Out_Of_Service); + } + return status; +} + +/** + * @brief Get the COV change flag status + * @param object_instance - object-instance number of the object + * @return the COV change flag status + */ +float Analog_Output_COV_Increment(uint32_t object_instance) +{ + float value = 0.0; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + value = pObject->COV_Increment; + } + + return value; +} + +/** + * @brief Get the COV change flag status + * @param object_instance - object-instance number of the object + * @param value - COV Increment value to set + * @return the COV change flag status + */ +void Analog_Output_COV_Increment_Set(uint32_t object_instance, float value) +{ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + pObject->COV_Increment = value; + } +} + +/** + * @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 Analog_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) { int len = 0; int apdu_len = 0; /* return value */ BACNET_BIT_STRING bit_string; BACNET_CHARACTER_STRING char_string; - float real_value = (float)1.414; - unsigned object_index = 0; + uint8_t *apdu = NULL; + uint32_t units = 0; + float real_value = 0.0; unsigned i = 0; bool state = false; - uint8_t *apdu = NULL; if ((rpdata == NULL) || (rpdata->application_data == NULL) || (rpdata->application_data_len == 0)) { return 0; } + apdu = rpdata->application_data; switch (rpdata->object_property) { - case PROP_OBJECT_IDENTIFIER: - apdu_len = encode_application_object_id( - &apdu[0], OBJECT_ANALOG_OUTPUT, rpdata->object_instance); - break; - case PROP_OBJECT_NAME: - Analog_Output_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_ANALOG_OUTPUT); - break; case PROP_PRESENT_VALUE: real_value = Analog_Output_Present_Value(rpdata->object_instance); apdu_len = encode_application_real(&apdu[0], real_value); break; + case PROP_MIN_PRES_VALUE: + real_value = Analog_Output_Min_Pres_Value(rpdata->object_instance); + apdu_len = encode_application_real(&apdu[0], real_value); + break; + case PROP_MAX_PRES_VALUE: + real_value = Analog_Output_Max_Pres_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, false); - bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); - bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + state = Analog_Output_Fault(rpdata->object_instance); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, state); + state = Analog_Output_Overridden(rpdata->object_instance); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, state); state = Analog_Output_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], Analog_Output_Reliability(rpdata->object_instance)); + break; case PROP_EVENT_STATE: apdu_len = encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); @@ -324,26 +1043,23 @@ int Analog_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) apdu_len = encode_application_boolean(&apdu[0], state); break; case PROP_UNITS: - apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); + units = Analog_Output_Units(rpdata->object_instance); + apdu_len = encode_application_enumerated(&apdu[0], units); break; case PROP_PRIORITY_ARRAY: - /* Array element zero is the number of elements in the array */ if (rpdata->array_index == 0) { + /* Array element zero = the number of elements in the array */ apdu_len = encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); - /* if no index was specified, then try to encode the entire list - */ - /* into one packet. */ } else if (rpdata->array_index == BACNET_ARRAY_ALL) { - object_index = - Analog_Output_Instance_To_Index(rpdata->object_instance); - for (i = 0; i < BACNET_MAX_PRIORITY; i++) { - /* FIXME: check if we have room before adding it to APDU */ - if (Analog_Output_Level[object_index][i] == AO_LEVEL_NULL) { + /* no index was specified; try to encode the entire list */ + for (i = 1; i <= BACNET_MAX_PRIORITY; i++) { + if (Analog_Output_Priority_Array_Null( + rpdata->object_instance, i)) { len = encode_application_null(&apdu[apdu_len]); } else { - real_value = - (float)Analog_Output_Level[object_index][i]; + real_value = Analog_Output_Priority_Array( + rpdata->object_instance, i); len = encode_application_real( &apdu[apdu_len], real_value); } @@ -351,6 +1067,7 @@ int Analog_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) if ((apdu_len + len) < MAX_APDU) { apdu_len += len; } else { + /* Abort response */ rpdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; apdu_len = BACNET_STATUS_ABORT; @@ -358,18 +1075,15 @@ int Analog_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) } } } else { - object_index = - Analog_Output_Instance_To_Index(rpdata->object_instance); if (rpdata->array_index <= BACNET_MAX_PRIORITY) { - if (Analog_Output_Level[object_index][rpdata->array_index - - 1] == AO_LEVEL_NULL) { - apdu_len = encode_application_null(&apdu[0]); + if (Analog_Output_Priority_Array_Null( + rpdata->object_instance, rpdata->array_index)) { + apdu_len = encode_application_null(&apdu[apdu_len]); } else { - real_value = - (float)Analog_Output_Level[object_index] - [rpdata->array_index - 1]; - apdu_len = - encode_application_real(&apdu[0], real_value); + real_value = Analog_Output_Priority_Array( + rpdata->object_instance, rpdata->array_index); + apdu_len = encode_application_real( + &apdu[apdu_len], real_value); } } else { rpdata->error_class = ERROR_CLASS_PROPERTY; @@ -379,9 +1093,30 @@ int Analog_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) } break; case PROP_RELINQUISH_DEFAULT: - real_value = AO_RELINQUISH_DEFAULT; + real_value = + Analog_Output_Relinquish_Default(rpdata->object_instance); apdu_len = encode_application_real(&apdu[0], real_value); break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Analog_Output_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], Analog_Output_COV_Increment(rpdata->object_instance)); + break; +#if (BACNET_PROTOCOL_REVISION >= 17) + case PROP_CURRENT_COMMAND_PRIORITY: + i = Analog_Output_Present_Value_Priority(rpdata->object_instance); + if ((i >= BACNET_MIN_PRIORITY) && (i <= BACNET_MAX_PRIORITY)) { + apdu_len = encode_application_unsigned(&apdu[0], i); + } else { + apdu_len = encode_application_null(&apdu[0]); + } + break; +#endif default: rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; @@ -390,6 +1125,7 @@ int Analog_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) } /* only array properties can have array options */ if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) && + (rpdata->object_property != PROP_EVENT_TIME_STAMPS) && (rpdata->array_index != BACNET_ARRAY_ALL)) { rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; @@ -399,7 +1135,13 @@ int Analog_Output_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_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) { bool status = false; /* return value */ @@ -416,63 +1158,57 @@ bool Analog_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; return false; } - /* only array properties can have array options */ if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->object_property != PROP_EVENT_TIME_STAMPS) && (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; return false; } switch (wp_data->object_property) { case PROP_PRESENT_VALUE: - status = write_property_type_valid( - wp_data, &value, BACNET_APPLICATION_TAG_REAL); + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_REAL); if (status) { - /* Command priority 6 is reserved for use by Minimum On/Off - algorithm and may not be used for other purposes in any - object. */ status = - Analog_Output_Present_Value_Set(wp_data->object_instance, - value.type.Real, wp_data->priority); - if (wp_data->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 if (!status) { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } + Analog_Output_Present_Value_Write(wp_data->object_instance, + value.type.Real, wp_data->priority, + &wp_data->error_class, &wp_data->error_code); } else { - status = write_property_type_valid( - wp_data, &value, BACNET_APPLICATION_TAG_NULL); + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_NULL); if (status) { - status = Analog_Output_Present_Value_Relinquish( - wp_data->object_instance, wp_data->priority); - if (!status) { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } + status = Analog_Output_Present_Value_Relinquish_Write( + wp_data->object_instance, wp_data->priority, + &wp_data->error_class, &wp_data->error_code); } } break; case PROP_OUT_OF_SERVICE: - status = write_property_type_valid( - wp_data, &value, BACNET_APPLICATION_TAG_BOOLEAN); + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_BOOLEAN); if (status) { Analog_Output_Out_Of_Service_Set( wp_data->object_instance, value.type.Boolean); } break; + case PROP_COV_INCREMENT: case PROP_OBJECT_IDENTIFIER: - case PROP_OBJECT_NAME: case PROP_OBJECT_TYPE: + case PROP_OBJECT_NAME: case PROP_STATUS_FLAGS: case PROP_EVENT_STATE: case PROP_UNITS: + case PROP_RELIABILITY: case PROP_PRIORITY_ARRAY: case PROP_RELINQUISH_DEFAULT: + case PROP_MAX_PRES_VALUE: + case PROP_MIN_PRES_VALUE: + 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; @@ -484,3 +1220,105 @@ bool Analog_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) return status; } + + +/** + * @brief Sets a callback used when present-value is written from BACnet + * @param cb - callback used to provide indications + */ +void Analog_Output_Write_Present_Value_Callback_Set( + analog_output_write_present_value_callback cb) +{ + Analog_Output_Write_Present_Value_Callback = cb; +} + +/** + * @brief Creates a Analog Value object + * @param object_instance - object-instance number of the object + * @return true if the object-instance was created + */ +bool Analog_Output_Create(uint32_t object_instance) +{ + bool status = false; + struct object_data *pObject = NULL; + int index = 0; + unsigned priority = 0; + + pObject = Keylist_Data(Object_List, object_instance); + if (!pObject) { + pObject = calloc(1, sizeof(struct object_data)); + if (pObject) { + pObject->Object_Name = NULL; + pObject->Reliability = RELIABILITY_NO_FAULT_DETECTED; + pObject->Overridden = false; + for (priority = 0; priority < BACNET_MAX_PRIORITY; priority++) { + pObject->Relinquished[priority] = true; + pObject->Priority_Array[priority] = 0.0; + } + pObject->Relinquish_Default = 0.0; + pObject->COV_Increment = 1.0; + pObject->Prior_Value = 0.0; + pObject->Units = UNITS_NO_UNITS; + pObject->Out_Of_Service = false; + pObject->Changed = false; + pObject->Min_Pres_Value = 0; + pObject->Max_Pres_Value = 100; + /* add to list */ + index = Keylist_Data_Add(Object_List, object_instance, pObject); + if (index >= 0) { + status = true; + Device_Inc_Database_Revision(); + } + } + } + + return status; +} + +/** + * @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_Output_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; + Device_Inc_Database_Revision(); + } + + return status; +} + +/** + * @brief Deletes all the Analog Values and their data + */ +void Analog_Output_Cleanup(void) +{ + struct object_data *pObject; + + if (Object_List) { + do { + pObject = Keylist_Data_Pop(Object_List); + if (pObject) { + free(pObject); + Device_Inc_Database_Revision(); + } + } while (pObject); + Keylist_Delete(Object_List); + Object_List = NULL; + } +} + +/** + * @brief Initializes the Analog Value object data + */ +void Analog_Output_Init(void) +{ + Object_List = Keylist_Create(); +} diff --git a/src/bacnet/basic/object/ao.h b/src/bacnet/basic/object/ao.h index c56e11b3..4be79a14 100644 --- a/src/bacnet/basic/object/ao.h +++ b/src/bacnet/basic/object/ao.h @@ -1,38 +1,58 @@ -/************************************************************************** -* -* 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 AO_H -#define AO_H +/** + * @file + * @author Steve Karg + * @date 2005 + * @brief Analog Output objects, customize for your use + * + * @section DESCRIPTION + * + * The Analog Output object is an object with a present-value that + * uses a single precision floating point data type, and includes + * a present-value derived from the priority array. + * + * @section LICENSE + * + * 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 BACNET_ANALOG_OUTPUT_H +#define BACNET_ANALOG_OUTPUT_H #include #include +#include "bacnet/config.h" #include "bacnet/bacnet_stack_exports.h" #include "bacnet/bacdef.h" +#include "bacnet/bacenum.h" #include "bacnet/bacerror.h" #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 - floating point analog value prior to write + * @param value - floating point analog value of the write + */ +typedef void (*analog_output_write_present_value_callback)( + uint32_t object_instance, float old_value, float value); + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ @@ -62,9 +82,6 @@ extern "C" { float Analog_Output_Present_Value( uint32_t object_instance); BACNET_STACK_EXPORT - unsigned Analog_Output_Present_Value_Priority( - uint32_t object_instance); - BACNET_STACK_EXPORT bool Analog_Output_Present_Value_Set( uint32_t object_instance, float value, @@ -73,6 +90,12 @@ extern "C" { bool Analog_Output_Present_Value_Relinquish( uint32_t object_instance, unsigned priority); + BACNET_STACK_EXPORT + unsigned Analog_Output_Present_Value_Priority( + uint32_t object_instance); + BACNET_STACK_EXPORT + void Analog_Output_Write_Present_Value_Callback_Set( + analog_output_write_present_value_callback cb); BACNET_STACK_EXPORT float Analog_Output_Relinquish_Default( @@ -117,14 +140,6 @@ extern "C" { uint32_t instance, char *new_name); - BACNET_STACK_EXPORT - BACNET_RELIABILITY Analog_Output_Reliability( - uint32_t object_instance); - BACNET_STACK_EXPORT - bool Analog_Output_Reliability_Set( - uint32_t object_instance, - BACNET_RELIABILITY value); - BACNET_STACK_EXPORT bool Analog_Output_Units_Set( uint32_t instance, @@ -142,6 +157,33 @@ extern "C" { bool oos_flag); BACNET_STACK_EXPORT + bool Analog_Output_Overridden( + uint32_t instance); + BACNET_STACK_EXPORT + void Analog_Output_Overridden_Set( + uint32_t instance, + bool oos_flag); + + BACNET_STACK_EXPORT + BACNET_RELIABILITY Analog_Output_Reliability( + uint32_t object_instance); + BACNET_STACK_EXPORT + bool Analog_Output_Reliability_Set( + uint32_t object_instance, BACNET_RELIABILITY value); + + BACNET_STACK_EXPORT + float Analog_Output_Min_Pres_Value( + uint32_t object_instance); + BACNET_STACK_EXPORT + bool Analog_Output_Min_Pres_Value_Set( + uint32_t object_instance, float value); + BACNET_STACK_EXPORT + float Analog_Output_Max_Pres_Value( + uint32_t object_instance); + BACNET_STACK_EXPORT + bool Analog_Output_Max_Pres_Value_Set( + uint32_t object_instance, float value); + int Analog_Output_Read_Property( BACNET_READ_PROPERTY_DATA * rpdata); BACNET_STACK_EXPORT diff --git a/src/bacnet/basic/object/bo.c b/src/bacnet/basic/object/bo.c index e86d9415..c01db30f 100644 --- a/src/bacnet/basic/object/bo.c +++ b/src/bacnet/basic/object/bo.c @@ -1,6 +1,15 @@ -/************************************************************************** +/** + * @file + * @author Steve Karg + * @date 2005 + * @brief Binary Output objects, customize for your use * - * Copyright (C) 2005 Steve Karg + * @section DESCRIPTION + * + * The Binary Output object is a command object, and the present-value + * property uses a priority array and an enumerated 2-state data type. + * + * @section LICENSE * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -20,49 +29,80 @@ * 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 */ - + */ #include #include #include +#include +#include +#include "bacnet/config.h" #include "bacnet/bacdef.h" #include "bacnet/bacdcode.h" #include "bacnet/bacenum.h" +#include "bacnet/bacerror.h" #include "bacnet/bacapp.h" -#include "bacnet/config.h" /* the custom stuff */ +#include "bacnet/bactext.h" +#include "bacnet/cov.h" +#include "bacnet/apdu.h" +#include "bacnet/npdu.h" +#include "bacnet/abort.h" +#include "bacnet/reject.h" #include "bacnet/rp.h" #include "bacnet/wp.h" -#include "bacnet/basic/object/bo.h" +#include "bacnet/basic/object/device.h" #include "bacnet/basic/services.h" +#include "bacnet/basic/sys/keylist.h" +/* me! */ +#include "bo.h" -#ifndef MAX_BINARY_OUTPUTS -#define MAX_BINARY_OUTPUTS 4 -#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_Output_Level[MAX_BINARY_OUTPUTS] - [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_OUTPUTS]; +static const char *Default_Active_Text = "Active"; +static const char *Default_Inactive_Text = "Inactive"; +struct object_data { + bool Out_Of_Service : 1; + bool Changed : 1; + bool Present_Value : 1; + bool Relinquish_Default : 1; + bool Polarity : 1; + uint16_t Priority_Array; + uint16_t Priority_Active_Bits; + 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; +/* callback for present value writes */ +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, 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, -1 }; + PROP_RELINQUISH_DEFAULT, +#if (BACNET_PROTOCOL_REVISION >= 17) + PROP_CURRENT_COMMAND_PRIORITY, +#endif + -1 }; -static const int Binary_Output_Properties_Optional[] = { PROP_DESCRIPTION, - PROP_ACTIVE_TEXT, PROP_INACTIVE_TEXT, -1 }; +static const int Binary_Output_Properties_Optional[] = { PROP_RELIABILITY, + PROP_DESCRIPTION, PROP_ACTIVE_TEXT, PROP_INACTIVE_TEXT, -1 }; static const int Binary_Output_Properties_Proprietary[] = { -1 }; +/** + * Returns the list of required, optional, and proprietary properties. + * Used by ReadPropertyMultiple service. + * + * @param pRequired - pointer to list of int terminated by -1, of + * BACnet required properties for this object. + * @param pOptional - pointer to list of int terminated by -1, of + * BACnet optkional properties for this object. + * @param pProprietary - pointer to list of int terminated by -1, of + * BACnet proprietary properties for this object. + */ void Binary_Output_Property_Lists( const int **pRequired, const int **pOptional, const int **pProprietary) { @@ -79,77 +119,87 @@ void Binary_Output_Property_Lists( return; } -void Binary_Output_Init(void) -{ - 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_OUTPUTS; i++) { - for (j = 0; j < BACNET_MAX_PRIORITY; j++) { - Binary_Output_Level[i][j] = BINARY_NULL; - } - } - } - - return; -} - -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need validate that the */ -/* given instance exists */ +/** + * Determines if a given Binary Output 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_Output_Valid_Instance(uint32_t object_instance) { - if (object_instance < MAX_BINARY_OUTPUTS) { + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { return true; } return false; } -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then count how many you have */ +/** + * Determines the number of Binary Output objects + * + * @return Number of Binary Output objects + */ unsigned Binary_Output_Count(void) { - return MAX_BINARY_OUTPUTS; + 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 */ +/** + * Determines the object instance-number for a given 0..N index + * of Binary Output objects where N is Binary_Output_Count(). + * + * @param index - 0..MAX_BINARY_OUTPUTS value + * + * @return object instance-number for the given index + */ uint32_t Binary_Output_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 */ +/** + * For a given object instance-number, determines a 0..N index + * of Binary Output objects where N is Binary_Output_Count(). + * + * @param object_instance - object-instance number of the object + * + * @return index for the given instance-number, or MAX_BINARY_OUTPUTS + * if not valid. + */ unsigned Binary_Output_Instance_To_Index(uint32_t object_instance) { - unsigned index = MAX_BINARY_OUTPUTS; - - if (object_instance < MAX_BINARY_OUTPUTS) { - 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_Output_Present_Value(uint32_t object_instance) { - BACNET_BINARY_PV value = RELINQUISH_DEFAULT; - unsigned index = 0; - unsigned i = 0; + BACNET_BINARY_PV value = BINARY_INACTIVE; + unsigned p = 0; + struct object_data *pObject; - index = Binary_Output_Instance_To_Index(object_instance); - if (index < MAX_BINARY_OUTPUTS) { - for (i = 0; i < BACNET_MAX_PRIORITY; i++) { - if (Binary_Output_Level[index][i] != BINARY_NULL) { - value = Binary_Output_Level[index][i]; + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (pObject->Relinquish_Default) { + value = BINARY_ACTIVE; + } + for (p = 0; p < BACNET_MAX_PRIORITY; p++) { + if (BIT_CHECK(pObject->Priority_Active_Bits, p)) { + if (BIT_CHECK(pObject->Priority_Array, p)) { + value = BINARY_ACTIVE; + } else { + value = BINARY_INACTIVE; + } break; } } @@ -158,36 +208,767 @@ BACNET_BINARY_PV Binary_Output_Present_Value(uint32_t object_instance) return value; } -bool Binary_Output_Out_Of_Service(uint32_t object_instance) +/** + * For a given object instance-number, determines the value at a + * given priority. + * + * @param object_instance - object-instance number of the object + * @param priority - priority 1..16 + * + * @return priority-array element of the object + */ +static BACNET_BINARY_PV Binary_Output_Priority_Array_Value( + uint32_t object_instance, unsigned priority) { - bool value = false; - unsigned index = 0; + BACNET_BINARY_PV value = BINARY_INACTIVE; + struct object_data *pObject; - index = Binary_Output_Instance_To_Index(object_instance); - if (index < MAX_BINARY_OUTPUTS) { - value = Out_Of_Service[index]; + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + if (BIT_CHECK(pObject->Priority_Array, priority)) { + value = BINARY_ACTIVE; + } else { + value = BINARY_INACTIVE; + } + } } return value; } -/* note: the object name must be unique within this device */ -bool Binary_Output_Object_Name( - uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) +/** + * For a given object instance-number, determines if the given priority + * is active or NULL. + * + * @param object_instance - object-instance number of the object + * @param priority - priority 1..16 + * + * @return true if the priority slot is active + */ +static bool Binary_Output_Priority_Active( + uint32_t object_instance, unsigned priority) { - static char text_string[32] = ""; /* okay for single thread */ bool status = false; + struct object_data *pObject; - if (object_instance < MAX_BINARY_OUTPUTS) { - sprintf( - text_string, "BINARY OUTPUT %lu", (unsigned long)object_instance); - status = characterstring_init_ansi(object_name, text_string); + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + if (BIT_CHECK(pObject->Priority_Active_Bits, priority)) { + status = true; + } + } } return status; } -/* return apdu len, or BACNET_STATUS_ERROR on error */ +/** + * For a given object instance-number, determines the active priority + * + * @param object_instance - object-instance number of the object + * + * @return active priority 1..16, or 0 if no priority is active + */ +unsigned Binary_Output_Present_Value_Priority(uint32_t object_instance) +{ + unsigned p = 0; /* loop counter */ + unsigned priority = 0; /* return value */ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + for (p = 0; p < BACNET_MAX_PRIORITY; p++) { + if (BIT_CHECK(pObject->Priority_Active_Bits, p)) { + priority = p + 1; + break; + } + } + } + + return priority; +} + +/** + * For a given object instance-number, sets the present-value at a given + * priority 1..16. + * + * @param object_instance - object-instance number of the object + * @param value - enumerated 2-state active or inactive value + * @param priority - priority 1..16 + * + * @return true if values are within range and present-value is set. + */ +bool Binary_Output_Present_Value_Set( + uint32_t object_instance, BACNET_BINARY_PV binary_value, unsigned priority) +{ + bool status = false; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */)) { + priority--; + if (binary_value <= MAX_BINARY_PV) { + BIT_SET(pObject->Priority_Active_Bits, priority); + if (binary_value == BINARY_ACTIVE) { + BIT_SET(pObject->Priority_Array, priority); + } else { + BIT_CLEAR(pObject->Priority_Array, priority); + } + status = true; + } + } + } + + return status; +} + +/** + * For a given object instance-number, relinquishes the present-value + * at a given priority 1..16. + * + * @param object_instance - object-instance number of the object + * @param priority - priority 1..16 + * + * @return true if values are within range and present-value is set. + */ +bool Binary_Output_Present_Value_Relinquish( + uint32_t object_instance, unsigned priority) +{ + bool status = false; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */)) { + priority--; + BIT_CLEAR(pObject->Priority_Active_Bits, priority); + BIT_CLEAR(pObject->Priority_Array, priority); + status = true; + } + } + + return status; +} + +/** + * @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 priority - priority-array index value 1..16 + * @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 Binary_Output_Present_Value_Write( + uint32_t object_instance, BACNET_BINARY_PV value, uint8_t priority, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code) +{ + bool status = false; + struct object_data *pObject; + BACNET_BINARY_PV old_value = BINARY_INACTIVE; + BACNET_BINARY_PV new_value = BINARY_INACTIVE; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if ((priority >= 1) && (priority <= BACNET_MAX_PRIORITY) && + (value >= 0.0) && (value <= 100.0)) { + if (priority != 6) { + old_value = Binary_Output_Present_Value(object_instance); + Binary_Output_Present_Value_Set(object_instance, value, + priority); + 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 output when the value of Out_Of_Service + is true. */ + } else if (Binary_Output_Write_Present_Value_Callback) { + new_value = Binary_Output_Present_Value(object_instance); + Binary_Output_Write_Present_Value_Callback( + object_instance, old_value, new_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, writes the present-value to the + * remote node + * @param object_instance - object-instance number of the object + * @param priority - priority-array index value 1..16 + * @param error_class - the BACnet error class + * @param error_code - BACnet Error code + * @return true if values are within range and write is requested + */ +static bool Binary_Output_Present_Value_Relinquish_Write( + uint32_t object_instance, uint8_t priority, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code) +{ + bool status = false; + struct object_data *pObject; + BACNET_BINARY_PV old_value = BINARY_INACTIVE; + BACNET_BINARY_PV new_value = BINARY_INACTIVE; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if ((priority >= 1) && (priority <= BACNET_MAX_PRIORITY)) { + if (priority != 6) { + old_value = Binary_Output_Present_Value(object_instance); + Binary_Output_Present_Value_Relinquish(object_instance, priority); + 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 output when the value of Out_Of_Service + is true. */ + } else if (Binary_Output_Write_Present_Value_Callback) { + new_value = Binary_Output_Present_Value(object_instance); + Binary_Output_Write_Present_Value_Callback( + object_instance, old_value, new_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; +} + +/** + * 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_Output_Out_Of_Service(uint32_t object_instance) +{ + bool value = false; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + value = pObject->Out_Of_Service; + } + + return value; +} + +/** + * 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_Output_Out_Of_Service_Set(uint32_t object_instance, bool value) +{ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (pObject->Out_Of_Service != value) { + pObject->Out_Of_Service = value; + pObject->Changed = true; + } + } +} + +/** + * 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 Binary_Output_Object_Name( + uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) +{ + bool status = false; + struct object_data *pObject; + char name_text[32]; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (pObject->Object_Name) { + status = characterstring_init_ansi(object_name, + pObject->Object_Name); + } else { + snprintf(name_text, sizeof(name_text), "BINARY OUTPUT %u", + object_instance); + status = characterstring_init_ansi(object_name, name_text); + } + } + + return status; +} + +/** + * 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 Binary_Output_Name_Set(uint32_t object_instance, char *new_name) +{ + bool status = false; /* return value */ + BACNET_CHARACTER_STRING object_name; + BACNET_OBJECT_TYPE found_type = OBJECT_NONE; + uint32_t found_instance = 0; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject && new_name) { + /* All the object names in a device must be unique */ + characterstring_init_ansi(&object_name, new_name); + if (Device_Valid_Object_Name( + &object_name, &found_type, &found_instance)) { + if ((found_type == OBJECT_BINARY_OUTPUT) && + (found_instance == object_instance)) { + /* writing same name to same object */ + status = true; + } else { + /* duplicate name! */ + status = false; + } + } else { + status = true; + pObject->Object_Name = new_name; + Device_Inc_Database_Revision(); + } + } + + return status; +} + +/** + * 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_Output_Polarity(uint32_t object_instance) +{ + BACNET_POLARITY polarity = POLARITY_NORMAL; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (pObject->Polarity) { + polarity = POLARITY_REVERSE; + } + } + + return polarity; +} + +/** + * For a given object instance-number, sets the polarity property. + * + * @param object_instance - object-instance number of the object + * @param polarity - enumerated polarity property value + * + * @return true if the polarity property value was set + */ +bool Binary_Output_Polarity_Set( + uint32_t object_instance, BACNET_POLARITY polarity) +{ + bool status = false; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (polarity < MAX_POLARITY) { + if (polarity == POLARITY_NORMAL) { + pObject->Polarity = false; + } else { + pObject->Polarity = true; + } + status = true; + } + } + + return status; +} + +/** + * For a given object instance-number, returns the relinquish-default + * property value + * + * @param object_instance - object-instance number of the object + * + * @return relinquish-default property value + */ +BACNET_BINARY_PV Binary_Output_Relinquish_Default(uint32_t object_instance) +{ + BACNET_BINARY_PV value = BINARY_INACTIVE; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (pObject->Relinquish_Default) { + value = BINARY_ACTIVE; + } else { + value = BINARY_INACTIVE; + } + } + + return value; +} + +/** + * For a given object instance-number, sets the relinquish-default + * property value + * + * @param object_instance - object-instance number of the object + * @param value - floating point relinquish-default value + * + * @return true if the relinquish-default property value was set + */ +bool Binary_Output_Relinquish_Default_Set( + uint32_t object_instance, BACNET_BINARY_PV value) +{ + bool status = false; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (value == BINARY_ACTIVE) { + pObject->Relinquish_Default = true; + status = true; + } else if (value == BINARY_INACTIVE) { + pObject->Relinquish_Default = false; + status = true; + } + } + + return status; +} + +/** + * For a given object instance-number, gets the reliability. + * + * @param object_instance - object-instance number of the object + * + * @return reliability value + */ +BACNET_RELIABILITY Binary_Output_Reliability(uint32_t object_instance) +{ + BACNET_RELIABILITY reliability = RELIABILITY_NO_FAULT_DETECTED; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + reliability = (BACNET_RELIABILITY)pObject->Reliability; + } + + return reliability; +} + +/** + * 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_Output_Object_Fault(struct object_data *pObject) +{ + bool fault = false; + + if (pObject) { + if (pObject->Reliability != RELIABILITY_NO_FAULT_DETECTED) { + fault = true; + } + } + + return fault; +} + +/** + * 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 Binary_Output_Reliability_Set( + uint32_t object_instance, BACNET_RELIABILITY value) +{ + struct object_data *pObject; + bool status = false; + bool fault = false; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (value <= 255) { + fault = Binary_Output_Object_Fault(pObject); + pObject->Reliability = value; + if (fault != Binary_Output_Object_Fault(pObject)) { + pObject->Changed = true; + } + status = true; + } + } + + return status; +} + +/** + * 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_Output_Fault(uint32_t object_instance) +{ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + + return Binary_Output_Object_Fault(pObject); +} + +/** + * 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_Output_Description(uint32_t object_instance) +{ + char *name = NULL; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + name = (char *)pObject->Description; + } + + return name; +} + +/** + * For a given object instance-number, sets the description + * + * @param object_instance - object-instance number of the object + * @param new_name - holds the description to be set + * + * @return true if object-name was set + */ +bool Binary_Output_Description_Set(uint32_t object_instance, char *new_name) +{ + bool status = false; /* return value */ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject && new_name) { + 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_Output_Active_Text(uint32_t object_instance) +{ + char *name = NULL; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, 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_Output_Active_Text_Set(uint32_t object_instance, char *new_name) +{ + bool status = false; /* return value */ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, 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_Output_Inactive_Text(uint32_t object_instance) +{ + char *name = NULL; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, 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_Output_Inactive_Text_Set(uint32_t object_instance, char *new_name) +{ + bool status = false; /* return value */ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject && new_name) { + status = true; + pObject->Inactive_Text = new_name; + } + + return status; +} + +/** + * Get the COV change flag status + * + * @param object_instance - object-instance number of the object + * @return the COV change flag status + */ +bool Binary_Output_Change_Of_Value(uint32_t object_instance) +{ + bool changed = false; + + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + changed = pObject->Changed; + } + + return changed; +} + +/** + * Clear the COV change flag + * + * @param object_instance - object-instance number of the object + */ +void Binary_Output_Change_Of_Value_Clear(uint32_t object_instance) +{ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + pObject->Changed = false; + } +} + +/** + * 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 Binary_Output_Encode_Value_List( + uint32_t object_instance, BACNET_PROPERTY_VALUE *value_list) +{ + bool status = false; + struct object_data *pObject; + const bool in_alarm = false; + const bool fault = false; + const bool overridden = false; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + status = + cov_value_list_encode_enumerated(value_list, pObject->Present_Value, + in_alarm, fault, overridden, pObject->Out_Of_Service); + } + 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_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) { int len = 0; @@ -196,33 +977,16 @@ int Binary_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) BACNET_CHARACTER_STRING char_string; BACNET_BINARY_PV present_value = BINARY_INACTIVE; BACNET_POLARITY polarity = POLARITY_NORMAL; - unsigned object_index = 0; unsigned i = 0; bool state = false; uint8_t *apdu = NULL; - if ((rpdata == NULL) || (rpdata->application_data == NULL) || + if ((rpdata->application_data == NULL) || (rpdata->application_data_len == 0)) { return 0; } apdu = rpdata->application_data; switch (rpdata->object_property) { - case PROP_OBJECT_IDENTIFIER: - apdu_len = encode_application_object_id( - &apdu[0], OBJECT_BINARY_OUTPUT, 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: - Binary_Output_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_OUTPUT); - break; case PROP_PRESENT_VALUE: present_value = Binary_Output_Present_Value(rpdata->object_instance); @@ -232,23 +996,27 @@ int Binary_Output_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_Output_Fault(rpdata->object_instance); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, state); bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); - bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + state = Binary_Output_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], Binary_Output_Reliability(rpdata->object_instance)); + 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; case PROP_OUT_OF_SERVICE: - object_index = - Binary_Output_Instance_To_Index(rpdata->object_instance); - state = Out_Of_Service[object_index]; + state = Binary_Output_Out_Of_Service(rpdata->object_instance); apdu_len = encode_application_boolean(&apdu[0], state); break; case PROP_POLARITY: + polarity = Binary_Output_Polarity(rpdata->object_instance); apdu_len = encode_application_enumerated(&apdu[0], polarity); break; case PROP_PRIORITY_ARRAY: @@ -260,40 +1028,36 @@ int Binary_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) */ /* into one packet. */ } else if (rpdata->array_index == BACNET_ARRAY_ALL) { - object_index = - Binary_Output_Instance_To_Index(rpdata->object_instance); - for (i = 0; i < BACNET_MAX_PRIORITY; i++) { - /* FIXME: check if we have room before adding it to APDU */ - if (Binary_Output_Level[object_index][i] == BINARY_NULL) { - len = encode_application_null(&apdu[apdu_len]); - } else { - present_value = Binary_Output_Level[object_index][i]; + for (i = 1; i <= BACNET_MAX_PRIORITY; i++) { + if (Binary_Output_Priority_Active( + rpdata->object_instance, i)) { + present_value = Binary_Output_Priority_Array_Value( + rpdata->object_instance, i); len = encode_application_enumerated( &apdu[apdu_len], present_value); + } else { + len = encode_application_null(&apdu[apdu_len]); } /* 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; + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; break; } } } else { - object_index = - Binary_Output_Instance_To_Index(rpdata->object_instance); if (rpdata->array_index <= BACNET_MAX_PRIORITY) { - if (Binary_Output_Level[object_index][rpdata->array_index - - 1] == BINARY_NULL) { - apdu_len = encode_application_null(&apdu[apdu_len]); - } else { - present_value = - Binary_Output_Level[object_index] - [rpdata->array_index - 1]; + if (Binary_Output_Priority_Active( + rpdata->object_instance, rpdata->array_index)) { + present_value = Binary_Output_Priority_Array_Value( + rpdata->object_instance, rpdata->array_index); apdu_len = encode_application_enumerated( - &apdu[apdu_len], present_value); + &apdu[0], present_value); + } else { + apdu_len = encode_application_null(&apdu[0]); } } else { rpdata->error_class = ERROR_CLASS_PROPERTY; @@ -301,22 +1065,40 @@ int Binary_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) apdu_len = BACNET_STATUS_ERROR; } } - break; case PROP_RELINQUISH_DEFAULT: - present_value = RELINQUISH_DEFAULT; + present_value = + Binary_Output_Relinquish_Default(rpdata->object_instance); apdu_len = encode_application_enumerated(&apdu[0], present_value); break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Binary_Output_Description(rpdata->object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; case PROP_ACTIVE_TEXT: - characterstring_init_ansi(&char_string, "on"); + characterstring_init_ansi(&char_string, + Binary_Output_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, "off"); + characterstring_init_ansi(&char_string, + Binary_Output_Inactive_Text(rpdata->object_instance)); apdu_len = encode_application_character_string(&apdu[0], &char_string); break; +#if (BACNET_PROTOCOL_REVISION >= 17) + case PROP_CURRENT_COMMAND_PRIORITY: + i = Binary_Output_Present_Value_Priority(rpdata->object_instance); + if ((i >= BACNET_MIN_PRIORITY) && (i <= BACNET_MAX_PRIORITY)) { + apdu_len = encode_application_unsigned(&apdu[0], i); + } else { + apdu_len = encode_application_null(&apdu[0]); + } + break; +#endif default: rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; @@ -334,13 +1116,18 @@ int Binary_Output_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_Output_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; @@ -363,84 +1150,44 @@ bool Binary_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) } switch (wp_data->object_property) { case PROP_PRESENT_VALUE: - status = write_property_type_valid( - wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED); + 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; - object_index = Binary_Output_Instance_To_Index( - wp_data->object_instance); - priority--; - Binary_Output_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; - } + status = + Binary_Output_Present_Value_Write(wp_data->object_instance, + value.type.Enumerated, wp_data->priority, + &wp_data->error_class, &wp_data->error_code); } else { - status = write_property_type_valid( - wp_data, &value, BACNET_APPLICATION_TAG_NULL); + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_NULL); if (status) { - level = BINARY_NULL; - object_index = Binary_Output_Instance_To_Index( - wp_data->object_instance); - priority = wp_data->priority; - if (priority && (priority <= BACNET_MAX_PRIORITY)) { - priority--; - Binary_Output_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_Output_Present_Value_Relinquish_Write( + wp_data->object_instance, wp_data->priority, + &wp_data->error_class, &wp_data->error_code); } } break; case PROP_OUT_OF_SERVICE: - status = write_property_type_valid( - wp_data, &value, BACNET_APPLICATION_TAG_BOOLEAN); + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_BOOLEAN); if (status) { - object_index = - Binary_Output_Instance_To_Index(wp_data->object_instance); - Out_Of_Service[object_index] = value.type.Boolean; + Binary_Output_Out_Of_Service_Set( + wp_data->object_instance, value.type.Boolean); } break; case PROP_OBJECT_IDENTIFIER: - case PROP_OBJECT_NAME: case PROP_OBJECT_TYPE: + case PROP_OBJECT_NAME: case PROP_STATUS_FLAGS: - case PROP_RELIABILITY: case PROP_EVENT_STATE: + case PROP_DESCRIPTION: case PROP_POLARITY: - case PROP_PRIORITY_ARRAY: - case PROP_RELINQUISH_DEFAULT: + 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; @@ -449,6 +1196,98 @@ bool Binary_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; break; } + /* not using len at this time */ + len = len; return status; } + +/** + * @brief Sets a callback used when present-value is written from BACnet + * @param cb - callback used to provide indications + */ +void Binary_Output_Write_Present_Value_Callback_Set( + binary_output_write_present_value_callback cb) +{ + Binary_Output_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_Output_Create(uint32_t object_instance) +{ + bool status = false; + struct object_data *pObject = NULL; + int index = 0; + + pObject = Keylist_Data(Object_List, object_instance); + if (!pObject) { + pObject = calloc(1, sizeof(struct object_data)); + if (pObject) { + pObject->Object_Name = NULL; + pObject->Reliability = RELIABILITY_NO_FAULT_DETECTED; + pObject->Present_Value = false; + pObject->Out_Of_Service = false; + pObject->Active_Text = Default_Active_Text; + pObject->Inactive_Text = Default_Inactive_Text; + pObject->Changed = false; + /* add to list */ + index = Keylist_Data_Add(Object_List, object_instance, pObject); + if (index >= 0) { + status = true; + Device_Inc_Database_Revision(); + } + } + } + + return status; +} + +/** + * Initializes the Binary Input object data + */ +void Binary_Output_Cleanup(void) +{ + struct object_data *pObject; + + if (Object_List) { + do { + pObject = Keylist_Data_Pop(Object_List); + if (pObject) { + free(pObject); + Device_Inc_Database_Revision(); + } + } while (pObject); + Keylist_Delete(Object_List); + Object_List = NULL; + } +} + +/** + * Creates a Binary Input object + */ +bool Binary_Output_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; + Device_Inc_Database_Revision(); + } + + return status; +} + +/** + * Initializes the Binary Input object data + */ +void Binary_Output_Init(void) +{ + Object_List = Keylist_Create(); +} diff --git a/src/bacnet/basic/object/bo.h b/src/bacnet/basic/object/bo.h index d2207cc9..94c01eab 100644 --- a/src/bacnet/basic/object/bo.h +++ b/src/bacnet/basic/object/bo.h @@ -1,38 +1,59 @@ -/************************************************************************** -* -* 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 BO_H -#define BO_H +/** + * @file + * @author Steve Karg + * @date 2005 + * @brief Binary Output objects, customize for your use + * + * @section DESCRIPTION + * + * The Binary Output object is a command object, and the present-value + * property uses a priority array and an enumerated 2-state data type. + * + * @section LICENSE + * + * 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 BACNET_BINARY_OUTPUT_H +#define BACNET_BINARY_OUTPUT_H #include #include +#include "bacnet/config.h" #include "bacnet/bacnet_stack_exports.h" #include "bacnet/bacdef.h" +#include "bacnet/bacenum.h" #include "bacnet/bacerror.h" +#include "bacnet/cov.h" #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_output_write_present_value_callback)( + uint32_t object_instance, BACNET_BINARY_PV old_value, + BACNET_BINARY_PV value); + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ @@ -95,6 +116,25 @@ extern "C" { uint32_t instance, char *new_name); + BACNET_STACK_EXPORT + int Binary_Output_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + + BACNET_STACK_EXPORT + bool Binary_Output_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + BACNET_STACK_EXPORT + bool Binary_Output_Encode_Value_List( + uint32_t object_instance, + BACNET_PROPERTY_VALUE * value_list); + BACNET_STACK_EXPORT + bool Binary_Output_Change_Of_Value( + uint32_t instance); + BACNET_STACK_EXPORT + void Binary_Output_Change_Of_Value_Clear( + uint32_t instance); + BACNET_STACK_EXPORT BACNET_BINARY_PV Binary_Output_Present_Value( uint32_t instance); @@ -112,12 +152,8 @@ extern "C" { uint32_t object_instance); BACNET_STACK_EXPORT - BACNET_POLARITY Binary_Output_Polarity( - uint32_t instance); - BACNET_STACK_EXPORT - bool Binary_Output_Polarity_Set( - uint32_t object_instance, - BACNET_POLARITY polarity); + void Binary_Output_Write_Present_Value_Callback_Set( + binary_output_write_present_value_callback cb); BACNET_STACK_EXPORT bool Binary_Output_Out_Of_Service( @@ -127,6 +163,44 @@ extern "C" { uint32_t object_instance, bool value); + BACNET_STACK_EXPORT + char *Binary_Output_Description( + uint32_t instance); + BACNET_STACK_EXPORT + bool Binary_Output_Description_Set( + uint32_t object_instance, + char *text_string); + + BACNET_STACK_EXPORT + char *Binary_Output_Inactive_Text( + uint32_t instance); + BACNET_STACK_EXPORT + bool Binary_Output_Inactive_Text_Set( + uint32_t instance, + char *new_name); + BACNET_STACK_EXPORT + char *Binary_Output_Active_Text( + uint32_t instance); + BACNET_STACK_EXPORT + bool Binary_Output_Active_Text_Set( + uint32_t instance, + char *new_name); + + BACNET_STACK_EXPORT + BACNET_POLARITY Binary_Output_Polarity( + uint32_t instance); + BACNET_STACK_EXPORT + bool Binary_Output_Polarity_Set( + uint32_t object_instance, + BACNET_POLARITY polarity); + + BACNET_STACK_EXPORT + bool Binary_Output_Reliability_Set( + uint32_t object_instance, BACNET_RELIABILITY value); + BACNET_STACK_EXPORT + BACNET_RELIABILITY Binary_Output_Reliability( + uint32_t object_instance); + BACNET_STACK_EXPORT BACNET_BINARY_PV Binary_Output_Relinquish_Default( uint32_t object_instance); diff --git a/src/bacnet/basic/object/device.c b/src/bacnet/basic/object/device.c index e2e187e9..ac5e1279 100644 --- a/src/bacnet/basic/object/device.c +++ b/src/bacnet/basic/object/device.c @@ -1875,6 +1875,10 @@ void Device_Init(object_functions_t *object_table) } pObject++; } + /* create some dynamically created objects as examples */ + Analog_Output_Create(1); + Binary_Output_Create(1); + Multistate_Output_Create(1); #if (BACNET_PROTOCOL_REVISION >= 24) Color_Create(1); Color_Temperature_Create(1); diff --git a/src/bacnet/basic/object/lo.c b/src/bacnet/basic/object/lo.c index 1eb45e93..3d4e4202 100644 --- a/src/bacnet/basic/object/lo.c +++ b/src/bacnet/basic/object/lo.c @@ -82,7 +82,11 @@ static const int Lighting_Output_Properties_Required[] = { PROP_BLINK_WARN_ENABLE, PROP_EGRESS_TIME, PROP_EGRESS_ACTIVE, PROP_DEFAULT_FADE_TIME, PROP_DEFAULT_RAMP_RATE, PROP_DEFAULT_STEP_INCREMENT, PROP_PRIORITY_ARRAY, PROP_RELINQUISH_DEFAULT, - PROP_LIGHTING_COMMAND_DEFAULT_PRIORITY, -1 + PROP_LIGHTING_COMMAND_DEFAULT_PRIORITY, +#if (BACNET_PROTOCOL_REVISION >= 17) + PROP_CURRENT_COMMAND_PRIORITY, +#endif + -1 }; static const int Lighting_Output_Properties_Optional[] = { -1 }; @@ -1060,6 +1064,16 @@ int Lighting_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) Lighting_Output_Default_Priority(rpdata->object_instance); apdu_len = encode_application_unsigned(&apdu[0], unsigned_value); break; +#if (BACNET_PROTOCOL_REVISION >= 17) + case PROP_CURRENT_COMMAND_PRIORITY: + i = Lighting_Output_Present_Value_Priority(rpdata->object_instance); + if ((i >= BACNET_MIN_PRIORITY) && (i <= BACNET_MAX_PRIORITY)) { + apdu_len = encode_application_unsigned(&apdu[0], i); + } else { + apdu_len = encode_application_null(&apdu[0]); + } + break; +#endif default: rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; @@ -1187,6 +1201,9 @@ bool Lighting_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) case PROP_DEFAULT_STEP_INCREMENT: case PROP_PRIORITY_ARRAY: case PROP_RELINQUISH_DEFAULT: +#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; diff --git a/src/bacnet/basic/object/mso.c b/src/bacnet/basic/object/mso.c index 886481be..ad04f0e6 100644 --- a/src/bacnet/basic/object/mso.c +++ b/src/bacnet/basic/object/mso.c @@ -1,6 +1,15 @@ -/************************************************************************** +/** + * @file + * @author Steve Karg + * @date 2005 + * @brief Multi-State Output objects, customize for your use * - * Copyright (C) 2006 Steve Karg + * @section DESCRIPTION + * + * The Multi-State object is an object with a present-value that + * uses an integer data type with a sequence of 1 to N values. + * + * @section LICENSE * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -20,143 +29,224 @@ * 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 Output Objects - customize for your use */ - + */ #include #include #include +#include +#include +#include "bacnet/config.h" #include "bacnet/bacdef.h" #include "bacnet/bacdcode.h" #include "bacnet/bacenum.h" +#include "bacnet/bacerror.h" #include "bacnet/bacapp.h" -#include "bacnet/config.h" /* the custom stuff */ +#include "bacnet/bactext.h" +#include "bacnet/cov.h" +#include "bacnet/apdu.h" +#include "bacnet/npdu.h" +#include "bacnet/abort.h" +#include "bacnet/reject.h" #include "bacnet/rp.h" #include "bacnet/wp.h" -#include "bacnet/basic/object/mso.h" +#include "bacnet/basic/object/device.h" #include "bacnet/basic/services.h" +#include "bacnet/basic/sys/keylist.h" +/* me! */ +#include "mso.h" -#ifndef MAX_MULTISTATE_OUTPUTS -#define MAX_MULTISTATE_OUTPUTS 4 -#endif - -/* When all the priorities are level null, the present value returns */ -/* the Relinquish Default value, 0 is not allowed */ -#define MULTISTATE_RELINQUISH_DEFAULT 1 - -/* NULL part of the array */ -#define MULTISTATE_NULL (255) -/* how many states? 1 to 254 states, 0 is not allowed */ -#define MULTISTATE_NUMBER_OF_STATES (254) -/* Here is our Priority Array.*/ -static uint8_t Multistate_Output_Level[MAX_MULTISTATE_OUTPUTS] - [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_MULTISTATE_OUTPUTS]; - -/* These three arrays are used by the ReadPropertyMultiple handler */ -static const int Multistate_Output_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_NUMBER_OF_STATES, PROP_PRIORITY_ARRAY, - PROP_RELINQUISH_DEFAULT, -1 +struct object_data { + bool Out_Of_Service : 1; + bool Changed : 1; + bool Relinquished[BACNET_MAX_PRIORITY]; + uint8_t Priority_Array[BACNET_MAX_PRIORITY]; + uint8_t Relinquish_Default; + 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; }; - -static const int Multistate_Output_Properties_Optional[] = { PROP_DESCRIPTION, +/* Key List for storing the object data sorted by instance number */ +static OS_Keylist Object_List; +/* callback for present value writes */ +static multistate_output_write_present_value_callback + Multistate_Output_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, + PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS, + PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, PROP_NUMBER_OF_STATES, + PROP_PRIORITY_ARRAY, PROP_RELINQUISH_DEFAULT, +#if (BACNET_PROTOCOL_REVISION >= 17) + PROP_CURRENT_COMMAND_PRIORITY, +#endif -1 }; -static const int Multistate_Output_Properties_Proprietary[] = { -1 }; +static const int Properties_Optional[] = { PROP_STATE_TEXT, PROP_DESCRIPTION, + -1 }; +static const int Properties_Proprietary[] = { -1 }; + +/** + * @brief Returns the list of required, optional, and proprietary properties. + * Used by ReadPropertyMultiple service. + * @param pRequired - pointer to list of int terminated by -1, of + * BACnet required properties for this object. + * @param pOptional - pointer to list of int terminated by -1, of + * BACnet optkional properties for this object. + * @param pProprietary - pointer to list of int terminated by -1, of + * BACnet proprietary properties for this object. + */ void Multistate_Output_Property_Lists( const int **pRequired, const int **pOptional, const int **pProprietary) { if (pRequired) { - *pRequired = Multistate_Output_Properties_Required; + *pRequired = Properties_Required; } if (pOptional) { - *pOptional = Multistate_Output_Properties_Optional; + *pOptional = Properties_Optional; } if (pProprietary) { - *pProprietary = Multistate_Output_Properties_Proprietary; + *pProprietary = Properties_Proprietary; } return; } -void Multistate_Output_Init(void) -{ - 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_MULTISTATE_OUTPUTS; i++) { - for (j = 0; j < BACNET_MAX_PRIORITY; j++) { - Multistate_Output_Level[i][j] = MULTISTATE_NULL; - } - } - } - - return; -} - -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need validate that the */ -/* given instance exists */ +/** + * @brief Determines if a given Multistate 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_Output_Valid_Instance(uint32_t object_instance) { - if (object_instance < MAX_MULTISTATE_OUTPUTS) { + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { return true; } return false; } -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then count how many you have */ +/** + * @brief Determines the number of Multistate objects + * @return Number of Multistate objects + */ unsigned Multistate_Output_Count(void) { - return MAX_MULTISTATE_OUTPUTS; + 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 Multistate objects where N is Multistate_Output_Count(). + * @param index - 0..Multistate_Output_Count() value + * @return object instance-number for the given index + */ uint32_t Multistate_Output_Index_To_Instance(unsigned index) { - return index; + return Keylist_Key(Object_List, index); } -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need to return the index */ -/* that correlates to the correct instance number */ +/** + * @brief For a given object instance-number, determines a 0..N index + * of Multistate objects where N is Multistate_Output_Count(). + * @param object_instance - object-instance number of the object + * @return index for the given instance-number, or Multistate_Output_Count() + * if not valid. + */ unsigned Multistate_Output_Instance_To_Index(uint32_t object_instance) { - unsigned index = MAX_MULTISTATE_OUTPUTS; - - if (object_instance < MAX_MULTISTATE_OUTPUTS) { - index = object_instance; - } - - return index; + return Keylist_Index(Object_List, object_instance); } -uint32_t Multistate_Output_Present_Value(uint32_t object_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) { - uint32_t value = MULTISTATE_RELINQUISH_DEFAULT; - unsigned index = 0; - unsigned i = 0; + unsigned count = 0; + int len = 0; - index = Multistate_Output_Instance_To_Index(object_instance); - if (index < MAX_MULTISTATE_OUTPUTS) { - for (i = 0; i < BACNET_MAX_PRIORITY; i++) { - if (Multistate_Output_Level[index][i] != MULTISTATE_NULL) { - value = Multistate_Output_Level[index][i]; + 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 + */ +static uint32_t Multistate_Output_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, determines the present-value + * @param object_instance - object-instance number of the object + * @return present-value of the object + */ +static uint32_t Object_Present_Value(struct object_data *pObject) +{ + uint32_t value = 1; + uint8_t priority = 0; /* loop counter */ + + if (pObject) { + value = pObject->Relinquish_Default; + for (priority = 0; priority < BACNET_MAX_PRIORITY; priority++) { + if (!pObject->Relinquished[priority]) { + value = pObject->Priority_Array[priority]; break; } } @@ -165,45 +255,661 @@ uint32_t Multistate_Output_Present_Value(uint32_t object_instance) return value; } -/* note: the object name must be unique within this device */ -bool Multistate_Output_Object_Name( - uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) +/** + * @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 + */ +uint32_t Multistate_Output_Present_Value(uint32_t object_instance) { - static char text_string[32] = ""; /* okay for single thread */ - bool status = false; + uint32_t value = 1; + struct object_data *pObject; - if (object_instance < MAX_MULTISTATE_OUTPUTS) { - sprintf(text_string, "MULTISTATE OUTPUT %u", object_instance); - status = characterstring_init_ansi(object_name, text_string); + pObject = Keylist_Data(Object_List, object_instance); + value = Object_Present_Value(pObject); + + return value; +} + +/** + * @brief For a given object instance-number, determines the active priority + * @param object_instance - object-instance number of the object + * @return active priority 1..16, or 0 if no priority is active + */ +unsigned Multistate_Output_Present_Value_Priority(uint32_t object_instance) +{ + unsigned p = 0; /* loop counter */ + unsigned priority = 0; /* return value */ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + for (p = 0; p < BACNET_MAX_PRIORITY; p++) { + if (!pObject->Relinquished[p]) { + priority = p + 1; + break; + } + } + } + + return priority; +} + +/** + * @brief For a given object instance-number and priority 1..16, determines the + * priority-array value + * @param object_instance - object-instance number of the object + * @param priority - priority-array index value 1..16 + * @return priority-array value of the object + */ +static uint32_t Multistate_Output_Priority_Array( + uint32_t object_instance, unsigned priority) +{ + uint32_t value = 1; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if ((priority >= 1) && (priority <= BACNET_MAX_PRIORITY)) { + value = pObject->Priority_Array[priority - 1]; + } + } + + return value; +} + +/** + * @brief For a given object instance-number and priority 1..16, determines + * if the priority-array slot is NULL + * @param object_instance - object-instance number of the object + * @param priority - priority-array index value 1..16 + * @return true if the priority array slot is NULL + */ +static bool Multistate_Output_Priority_Array_Null( + uint32_t object_instance, unsigned priority) +{ + bool null_value = false; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if ((priority >= 1) && (priority <= BACNET_MAX_PRIORITY)) { + if (pObject->Relinquished[priority - 1]) { + null_value = true; + } + } + } + + return null_value; +} + +/** + * @brief For a given object instance-number, determines the + * relinquish-default value + * @param object_instance - object-instance number + * @return relinquish-default value of the object + */ +uint32_t Multistate_Output_Relinquish_Default(uint32_t object_instance) +{ + uint32_t value = 1; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + value = pObject->Relinquish_Default; + } + + return value; +} + +/** + * @brief For a given object instance-number, sets the relinquish-default value + * @param object_instance - object-instance number of the object + * @param value - floating point analog output relinquish-default value + * @return true if values are within range and relinquish-default value is set. + */ +bool Multistate_Output_Relinquish_Default_Set(uint32_t object_instance, + uint32_t value) +{ + bool status = false; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + pObject->Relinquish_Default = value; + status = true; } return status; } -bool Multistate_Output_Out_Of_Service(uint32_t instance) +/** + * @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 + * @param priority - priority-array index value 1..16 + * @return true if values are within range and present-value is set. + */ +bool Multistate_Output_Present_Value_Set( + uint32_t object_instance, uint32_t value, unsigned priority) { - unsigned index = 0; - bool oos_flag = false; + bool status = false; + uint32_t old_value = 0; + uint32_t new_value = 0; + struct object_data *pObject; + unsigned max_states = 0; - index = Multistate_Output_Instance_To_Index(instance); - if (index < MAX_MULTISTATE_OUTPUTS) { - oos_flag = Out_Of_Service[index]; + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + max_states = state_name_count(pObject->State_Text); + if ((value >= 1) && (value <= max_states) && + (priority >= 1) && (priority <= BACNET_MAX_PRIORITY)) { + old_value = Object_Present_Value(pObject); + pObject->Relinquished[priority - 1] = false; + pObject->Priority_Array[priority - 1] = value; + new_value = Object_Present_Value(pObject); + if (old_value != new_value) { + pObject->Changed = true; + } + status = true; + } } - return oos_flag; + return status; } -void Multistate_Output_Out_Of_Service_Set(uint32_t instance, bool oos_flag) +/** + * @brief For a given object instance-number, relinquishes the present-value + * @param object_instance - object-instance number of the object + * @param priority - priority-array index value 1..16 + * @return true if values are within range and present-value is relinquished. + */ +bool Multistate_Output_Present_Value_Relinquish( + uint32_t object_instance, unsigned priority) { - unsigned index = 0; + bool status = false; + uint32_t old_value = 0; + uint32_t new_value = 0; + struct object_data *pObject; - index = Multistate_Output_Instance_To_Index(instance); - if (index < MAX_MULTISTATE_OUTPUTS) { - Out_Of_Service[index] = oos_flag; + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if ((priority >= 1) && (priority <= BACNET_MAX_PRIORITY)) { + old_value = Object_Present_Value(pObject); + pObject->Relinquished[priority - 1] = true; + pObject->Priority_Array[priority - 1] = 0; + new_value = Object_Present_Value(pObject); + if (old_value != new_value) { + pObject->Changed = true; + } + status = true; + } + } + + return status; +} + +/** + * @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 priority - priority-array index value 1..16 + * @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_Output_Present_Value_Write( + uint32_t object_instance, uint32_t value, uint8_t priority, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code) +{ + bool status = false; + struct object_data *pObject; + uint32_t old_value = 0; + uint32_t new_value = 0; + unsigned max_states = 0; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + max_states = state_name_count(pObject->State_Text); + if ((priority >= 1) && (priority <= BACNET_MAX_PRIORITY) && + (value >= 1) && (value <= max_states)) { + if (priority != 6) { + old_value = Object_Present_Value(pObject); + Multistate_Output_Present_Value_Set(object_instance, value, + priority); + 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 output when the value of Out_Of_Service + is true. */ + } else if (Multistate_Output_Write_Present_Value_Callback) { + new_value = Object_Present_Value(pObject); + Multistate_Output_Write_Present_Value_Callback( + object_instance, old_value, new_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, writes the present-value to the + * remote node + * @param object_instance - object-instance number of the object + * @param priority - priority-array index value 1..16 + * @param error_class - the BACnet error class + * @param error_code - BACnet Error code + * @return true if values are within range and write is requested + */ +static bool Multistate_Output_Present_Value_Relinquish_Write( + uint32_t object_instance, uint8_t priority, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code) +{ + bool status = false; + struct object_data *pObject; + uint32_t old_value = 0; + uint32_t new_value = 0; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if ((priority >= 1) && (priority <= BACNET_MAX_PRIORITY)) { + if (priority != 6) { + old_value = Object_Present_Value(pObject); + Multistate_Output_Present_Value_Relinquish(object_instance, + priority); + 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 output when the value of Out_Of_Service + is true. */ + } else if (Multistate_Output_Write_Present_Value_Callback) { + new_value = Object_Present_Value(pObject); + Multistate_Output_Write_Present_Value_Callback( + object_instance, old_value, new_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_Output_Out_Of_Service(uint32_t object_instance) +{ + bool value = false; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + value = pObject->Out_Of_Service; + } + + return value; +} + +/** + * @brief For a given object instance-number, sets the out-of-service state + * @param object_instance - object-instance number of the object + * @param value - out-of-service state + */ +void Multistate_Output_Out_Of_Service_Set(uint32_t object_instance, bool value) +{ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (pObject->Out_Of_Service != value) { + pObject->Out_Of_Service = value; + pObject->Changed = true; + } } } -/* return apdu len, or BACNET_STATUS_ERROR on error */ +/** + * @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_Output_Object_Name( + uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) +{ + bool status = false; + struct object_data *pObject; + char name_text[32]; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (pObject->Object_Name) { + status = characterstring_init_ansi(object_name, + pObject->Object_Name); + } else { + snprintf(name_text, sizeof(name_text), "MULTI-STATE OUTPUT %u", + object_instance); + status = characterstring_init_ansi(object_name, name_text); + } + } + + return status; +} + +/** + * @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_Output_Name_Set(uint32_t object_instance, char *new_name) +{ + bool status = false; /* return value */ + BACNET_CHARACTER_STRING object_name; + BACNET_OBJECT_TYPE found_type = OBJECT_NONE; + uint32_t found_instance = 0; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject && new_name) { + /* All the object names in a device must be unique */ + characterstring_init_ansi(&object_name, new_name); + if (Device_Valid_Object_Name( + &object_name, &found_type, &found_instance)) { + if ((found_type == OBJECT_MULTI_STATE_VALUE) && + (found_instance == object_instance)) { + /* writing same name to same object */ + status = true; + } else { + /* duplicate name! */ + status = false; + } + } else { + status = true; + pObject->Object_Name = new_name; + Device_Inc_Database_Revision(); + } + } + + return status; +} + +/** + * @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_Output_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 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_Output_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, gets the reliability. + * @param object_instance - object-instance number of the object + * @return reliability value + */ +BACNET_RELIABILITY Multistate_Output_Reliability(uint32_t object_instance) +{ + BACNET_RELIABILITY reliability = RELIABILITY_NO_FAULT_DETECTED; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + reliability = (BACNET_RELIABILITY)pObject->Reliability; + } + + return reliability; +} + +/** + * @brief For a given object instance-number, 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_Output_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_Output_Reliability_Set( + uint32_t object_instance, BACNET_RELIABILITY value) +{ + struct object_data *pObject; + bool status = false; + bool fault = false; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + if (value <= 255) { + fault = Multistate_Output_Object_Fault(pObject); + pObject->Reliability = value; + if (fault != Multistate_Output_Object_Fault(pObject)) { + pObject->Changed = 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_Output_Fault(uint32_t object_instance) +{ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + + return Multistate_Output_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_Output_Description(uint32_t object_instance) +{ + char *name = NULL; + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, 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_Output_Description_Set(uint32_t object_instance, char *new_name) +{ + bool status = false; /* return value */ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, 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_Output_Change_Of_Value(uint32_t object_instance) +{ + bool changed = false; + + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + changed = pObject->Changed; + } + + return changed; +} + +/** + * @brief Clear the COV change flag + * @param object_instance - object-instance number of the object + */ +void Multistate_Output_Change_Of_Value_Clear(uint32_t object_instance) +{ + struct object_data *pObject; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + pObject->Changed = 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_Output_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; + uint32_t present_value = 1; + + pObject = Keylist_Data(Object_List, object_instance); + if (pObject) { + fault = Multistate_Output_Object_Fault(pObject); + present_value = Object_Present_Value(pObject); + status = + cov_value_list_encode_unsigned(value_list, 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_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) { int len = 0; @@ -211,8 +917,8 @@ int Multistate_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) BACNET_BIT_STRING bit_string; BACNET_CHARACTER_STRING char_string; uint32_t present_value = 0; - unsigned object_index = 0; unsigned i = 0; + uint32_t max_states = 0; bool state = false; uint8_t *apdu = NULL; @@ -222,23 +928,6 @@ int Multistate_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) } apdu = rpdata->application_data; switch (rpdata->object_property) { - case PROP_OBJECT_IDENTIFIER: - apdu_len = encode_application_object_id( - &apdu[0], OBJECT_MULTI_STATE_OUTPUT, 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: - Multistate_Output_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_MULTI_STATE_OUTPUT); - break; case PROP_PRESENT_VALUE: present_value = Multistate_Output_Present_Value(rpdata->object_instance); @@ -248,12 +937,17 @@ int Multistate_Output_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_Output_Fault(rpdata->object_instance); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, state); bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); state = Multistate_Output_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_Output_Reliability(rpdata->object_instance)); + break; case PROP_EVENT_STATE: /* note: see the details in the standard on how to use this */ apdu_len = @@ -263,25 +957,24 @@ int Multistate_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) state = Multistate_Output_Out_Of_Service(rpdata->object_instance); apdu_len = encode_application_boolean(&apdu[0], state); break; + case PROP_NUMBER_OF_STATES: + apdu_len = encode_application_unsigned(&apdu[apdu_len], + Multistate_Output_Max_States(rpdata->object_instance)); + break; case PROP_PRIORITY_ARRAY: - /* Array element zero is the number of elements in the array */ if (rpdata->array_index == 0) { + /* Array element zero = the number of elements in the array */ apdu_len = encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); - /* if no index was specified, then try to encode the entire list - */ - /* into one packet. */ } else if (rpdata->array_index == BACNET_ARRAY_ALL) { - object_index = Multistate_Output_Instance_To_Index( - rpdata->object_instance); - for (i = 0; i < BACNET_MAX_PRIORITY; i++) { - /* FIXME: check if we have room before adding it to APDU */ - if (Multistate_Output_Level[object_index][i] == - MULTISTATE_NULL) { + /* no index was specified; try to encode the entire list */ + for (i = 1; i <= BACNET_MAX_PRIORITY; i++) { + if (Multistate_Output_Priority_Array_Null( + rpdata->object_instance, i)) { len = encode_application_null(&apdu[apdu_len]); } else { - present_value = - Multistate_Output_Level[object_index][i]; + present_value = Multistate_Output_Priority_Array( + rpdata->object_instance, i); len = encode_application_unsigned( &apdu[apdu_len], present_value); } @@ -289,6 +982,7 @@ int Multistate_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) if ((apdu_len + len) < MAX_APDU) { apdu_len += len; } else { + /* Abort response */ rpdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; apdu_len = BACNET_STATUS_ABORT; @@ -296,19 +990,15 @@ int Multistate_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) } } } else { - object_index = Multistate_Output_Instance_To_Index( - rpdata->object_instance); if (rpdata->array_index <= BACNET_MAX_PRIORITY) { - if (Multistate_Output_Level[object_index] - [rpdata->array_index - 1] == - MULTISTATE_NULL) { - apdu_len = encode_application_null(&apdu[0]); + if (Multistate_Output_Priority_Array_Null( + rpdata->object_instance, rpdata->array_index)) { + apdu_len = encode_application_null(&apdu[apdu_len]); } else { - present_value = - Multistate_Output_Level[object_index] - [rpdata->array_index - 1]; + present_value = Multistate_Output_Priority_Array( + rpdata->object_instance, rpdata->array_index); apdu_len = encode_application_unsigned( - &apdu[0], present_value); + &apdu[apdu_len], present_value); } } else { rpdata->error_class = ERROR_CLASS_PROPERTY; @@ -316,17 +1006,69 @@ int Multistate_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) apdu_len = BACNET_STATUS_ERROR; } } - break; case PROP_RELINQUISH_DEFAULT: - present_value = MULTISTATE_RELINQUISH_DEFAULT; + present_value = + Multistate_Output_Relinquish_Default(rpdata->object_instance); apdu_len = encode_application_unsigned(&apdu[0], present_value); break; - case PROP_NUMBER_OF_STATES: - apdu_len = encode_application_unsigned( - &apdu[apdu_len], MULTISTATE_NUMBER_OF_STATES); + case PROP_STATE_TEXT: + max_states = Multistate_Output_Max_States(rpdata->object_instance); + if (rpdata->array_index == 0) { + /* Array element zero is the number of elements in the array */ + apdu_len = encode_application_unsigned(&apdu[0], max_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 <= max_states; i++) { + characterstring_init_ansi(&char_string, + Multistate_Output_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_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + if (rpdata->array_index <= max_states) { + characterstring_init_ansi(&char_string, + Multistate_Output_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; + } + } break; - + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Multistate_Output_Description(rpdata->object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; +#if (BACNET_PROTOCOL_REVISION >= 17) + case PROP_CURRENT_COMMAND_PRIORITY: + i = Multistate_Output_Present_Value_Priority( + rpdata->object_instance); + if ((i >= BACNET_MIN_PRIORITY) && (i <= BACNET_MAX_PRIORITY)) { + apdu_len = encode_application_unsigned(&apdu[0], i); + } else { + apdu_len = encode_application_null(&apdu[0]); + } + break; +#endif default: rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; @@ -335,7 +1077,6 @@ int Multistate_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) } /* only array properties can have array options */ if ((apdu_len >= 0) && (rpdata->object_property != PROP_STATE_TEXT) && - (rpdata->object_property != PROP_PRIORITY_ARRAY) && (rpdata->array_index != BACNET_ARRAY_ALL)) { rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; @@ -345,20 +1086,23 @@ int Multistate_Output_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_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) { bool status = false; /* return value */ - unsigned int object_index = 0; - unsigned int priority = 0; - uint32_t level = 0; int len = 0; BACNET_APPLICATION_DATA_VALUE value; - /* decode the some of the request */ + /* decode the first chunk 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; @@ -366,7 +1110,6 @@ bool Multistate_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) return false; } if ((wp_data->object_property != PROP_STATE_TEXT) && - (wp_data->object_property != PROP_PRIORITY_ARRAY) && (wp_data->array_index != BACNET_ARRAY_ALL)) { /* only array properties can have array options */ wp_data->error_class = ERROR_CLASS_PROPERTY; @@ -375,84 +1118,50 @@ bool Multistate_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) } switch (wp_data->object_property) { case PROP_PRESENT_VALUE: - status = write_property_type_valid( - wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT); + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_UNSIGNED_INT); 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.Unsigned_Int > 0) && - (value.type.Unsigned_Int <= MULTISTATE_NUMBER_OF_STATES)) { - level = value.type.Unsigned_Int; - object_index = Multistate_Output_Instance_To_Index( - wp_data->object_instance); - priority--; - Multistate_Output_Level[object_index][priority] = - (uint8_t)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; + status = false; + if (value.type.Unsigned_Int <= UINT32_MAX) { + status = + Multistate_Output_Present_Value_Write( + wp_data->object_instance, + value.type.Unsigned_Int, wp_data->priority, + &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; } } else { - status = write_property_type_valid( - wp_data, &value, BACNET_APPLICATION_TAG_NULL); + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_NULL); if (status) { - level = MULTISTATE_NULL; - object_index = Multistate_Output_Instance_To_Index( - wp_data->object_instance); - priority = wp_data->priority; - if (priority && (priority <= BACNET_MAX_PRIORITY)) { - priority--; - Multistate_Output_Level[object_index][priority] = - (uint8_t)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 = Multistate_Output_Present_Value_Relinquish_Write( + wp_data->object_instance, wp_data->priority, + &wp_data->error_class, &wp_data->error_code); } } break; case PROP_OUT_OF_SERVICE: - status = write_property_type_valid( - wp_data, &value, BACNET_APPLICATION_TAG_BOOLEAN); + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_BOOLEAN); if (status) { Multistate_Output_Out_Of_Service_Set( wp_data->object_instance, value.type.Boolean); } break; - case PROP_OBJECT_IDENTIFIER: 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: - case PROP_PRIORITY_ARRAY: - case PROP_RELINQUISH_DEFAULT: +#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; @@ -464,3 +1173,99 @@ bool Multistate_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) return status; } + +/** + * @brief Sets a callback used when present-value is written from BACnet + * @param cb - callback used to provide indications + */ +void Multistate_Output_Write_Present_Value_Callback_Set( + multistate_output_write_present_value_callback cb) +{ + Multistate_Output_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 true if the object is created + */ +bool Multistate_Output_Create(uint32_t object_instance) +{ + bool status = false; + struct object_data *pObject = NULL; + int index = 0; + unsigned priority = 0; + + 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->Changed = false; + for (priority = 0; priority < BACNET_MAX_PRIORITY; priority++) { + pObject->Relinquished[priority] = true; + pObject->Priority_Array[priority] = 0; + } + pObject->Relinquish_Default = 1; + /* add to list */ + index = Keylist_Data_Add(Object_List, object_instance, pObject); + if (index >= 0) { + status = true; + Device_Inc_Database_Revision(); + } + } + } + + return status; +} + +/** + * @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_Output_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; + Device_Inc_Database_Revision(); + } + + return status; +} + +/** + * @brief Cleans up the object list and its data + */ +void Multistate_Output_Cleanup(void) +{ + struct object_data *pObject; + + if (Object_List) { + do { + pObject = Keylist_Data_Pop(Object_List); + if (pObject) { + free(pObject); + Device_Inc_Database_Revision(); + } + } while (pObject); + Keylist_Delete(Object_List); + Object_List = NULL; + } +} + +/** + * @brief Initializes the object list + */ +void Multistate_Output_Init(void) +{ + Object_List = Keylist_Create(); +} diff --git a/src/bacnet/basic/object/mso.h b/src/bacnet/basic/object/mso.h index 0d436504..be8c58dd 100644 --- a/src/bacnet/basic/object/mso.h +++ b/src/bacnet/basic/object/mso.h @@ -1,38 +1,59 @@ -/************************************************************************** -* -* 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 MSO_H -#define MSO_H +/** + * @file + * @author Steve Karg + * @date 2005 + * @brief Multi-State Output objects, customize for your use + * + * @section DESCRIPTION + * + * The Multi-State object is an object with a present-value that + * uses an integer data type with a sequence of 1 to N values. + * + * @section LICENSE + * + * 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 BACNET_MULTISTATE_OUTPUT_H +#define BACNET_MULTISTATE_OUTPUT_H #include #include +#include "bacnet/config.h" #include "bacnet/bacnet_stack_exports.h" #include "bacnet/bacdef.h" +#include "bacnet/bacenum.h" #include "bacnet/bacerror.h" +#include "bacnet/cov.h" #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_output_write_present_value_callback)( + uint32_t object_instance, uint32_t old_value, + uint32_t value); + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ @@ -55,15 +76,6 @@ extern "C" { unsigned Multistate_Output_Instance_To_Index( uint32_t object_instance); - BACNET_STACK_EXPORT - bool Multistate_Output_Object_Name( - uint32_t object_instance, - BACNET_CHARACTER_STRING * object_name); - - BACNET_STACK_EXPORT - void Multistate_Output_Init( - void); - BACNET_STACK_EXPORT int Multistate_Output_Read_Property( BACNET_READ_PROPERTY_DATA * rpdata); @@ -72,6 +84,34 @@ extern "C" { bool Multistate_Output_Write_Property( BACNET_WRITE_PROPERTY_DATA * wp_data); + BACNET_STACK_EXPORT + bool Multistate_Output_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + BACNET_STACK_EXPORT + bool Multistate_Output_Name_Set( + uint32_t object_instance, + char *new_name); + + BACNET_STACK_EXPORT + uint32_t Multistate_Output_Present_Value( + uint32_t object_instance); + BACNET_STACK_EXPORT + bool Multistate_Output_Present_Value_Set( + uint32_t object_instance, + uint32_t value, + unsigned priority); + BACNET_STACK_EXPORT + bool Multistate_Output_Present_Value_Relinquish( + uint32_t instance, + unsigned priority); + BACNET_STACK_EXPORT + unsigned Multistate_Output_Present_Value_Priority( + uint32_t object_instance); + BACNET_STACK_EXPORT + void Multistate_Output_Write_Present_Value_Callback_Set( + multistate_output_write_present_value_callback cb); + BACNET_STACK_EXPORT bool Multistate_Output_Change_Of_Value( uint32_t instance); @@ -83,25 +123,12 @@ extern "C" { uint32_t object_instance, BACNET_PROPERTY_VALUE * value_list); - BACNET_STACK_EXPORT - uint32_t Multistate_Output_Present_Value( - uint32_t instance); - BACNET_STACK_EXPORT - bool Multistate_Output_Present_Value_Set( - uint32_t instance, - unsigned value, - unsigned priority); - BACNET_STACK_EXPORT - bool Multistate_Output_Present_Value_Relinquish( - uint32_t instance, - unsigned priority); - BACNET_STACK_EXPORT bool Multistate_Output_Out_Of_Service( - uint32_t instance); + uint32_t object_instance); BACNET_STACK_EXPORT void Multistate_Output_Out_Of_Service_Set( - uint32_t instance, + uint32_t object_instance, bool value); BACNET_STACK_EXPORT @@ -125,6 +152,40 @@ extern "C" { char *Multistate_Output_State_Text( uint32_t object_instance, uint32_t state_index); + BACNET_STACK_EXPORT + bool Multistate_Output_State_Text_List_Set( + uint32_t object_instance, + const char *state_text_list); + + BACNET_STACK_EXPORT + BACNET_RELIABILITY Multistate_Output_Reliability( + uint32_t object_instance); + BACNET_STACK_EXPORT + bool Multistate_Output_Reliability_Set( + uint32_t object_instance, + BACNET_RELIABILITY value); + + BACNET_STACK_EXPORT + uint32_t Multistate_Output_Relinquish_Default( + uint32_t object_instance); + BACNET_STACK_EXPORT + bool Multistate_Output_Relinquish_Default_Set( + uint32_t object_instance, + uint32_t value); + + BACNET_STACK_EXPORT + bool Multistate_Output_Create( + uint32_t object_instance); + BACNET_STACK_EXPORT + bool Multistate_Output_Delete( + uint32_t object_instance); + BACNET_STACK_EXPORT + void Multistate_Output_Cleanup( + void); + + BACNET_STACK_EXPORT + void Multistate_Output_Init( + void); #ifdef __cplusplus } diff --git a/src/bacnet/property.c b/src/bacnet/property.c index 681b95a3..b003d5dc 100644 --- a/src/bacnet/property.c +++ b/src/bacnet/property.c @@ -204,7 +204,11 @@ static const int Analog_Input_Properties_Optional[] = { PROP_DESCRIPTION, static const int Analog_Output_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_UNITS, PROP_PRIORITY_ARRAY, - PROP_RELINQUISH_DEFAULT, PROP_CURRENT_COMMAND_PRIORITY, -1 }; + PROP_RELINQUISH_DEFAULT, +#if (BACNET_PROTOCOL_REVISION >= 17) + PROP_CURRENT_COMMAND_PRIORITY, +#endif + -1 }; static const int Analog_Output_Properties_Optional[] = { PROP_DESCRIPTION, PROP_DEVICE_TYPE, PROP_RELIABILITY, PROP_MIN_PRES_VALUE, @@ -290,7 +294,11 @@ static const int Binary_Lighting_Output_Properties_Optional[] = { static const int Binary_Output_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, PROP_CURRENT_COMMAND_PRIORITY, -1 }; + PROP_RELINQUISH_DEFAULT, +#if (BACNET_PROTOCOL_REVISION >= 17) + PROP_CURRENT_COMMAND_PRIORITY, +#endif + -1 }; static const int Binary_Output_Properties_Optional[] = { PROP_DESCRIPTION, PROP_DEVICE_TYPE, PROP_RELIABILITY, PROP_INACTIVE_TEXT, PROP_ACTIVE_TEXT, @@ -780,7 +788,11 @@ static const int Multistate_Output_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_NUMBER_OF_STATES, PROP_PRIORITY_ARRAY, - PROP_RELINQUISH_DEFAULT, PROP_CURRENT_COMMAND_PRIORITY, -1 + PROP_RELINQUISH_DEFAULT, +#if (BACNET_PROTOCOL_REVISION >= 17) + PROP_CURRENT_COMMAND_PRIORITY, +#endif + -1 }; static const int Multistate_Output_Properties_Optional[] = { PROP_DESCRIPTION,