From 4d79c26a8c52533bc22caf011743327ad418c339 Mon Sep 17 00:00:00 2001 From: skarg Date: Mon, 11 Nov 2013 22:27:55 +0000 Subject: [PATCH] Updated Lighting Output object. Added Channel object. Not complete and not fully tested. --- bacnet-stack/demo/object/channel.c | 1480 ++++++++++++++++++++++++++++ bacnet-stack/demo/object/channel.h | 168 ++++ bacnet-stack/demo/object/device.c | 35 +- bacnet-stack/demo/object/lo.c | 1238 ++++++++++++++++++----- bacnet-stack/demo/object/lo.h | 117 ++- bacnet-stack/demo/server/Makefile | 2 + bacnet-stack/include/bacapp.h | 4 + bacnet-stack/include/bacdef.h | 2 +- bacnet-stack/include/bacenum.h | 16 +- bacnet-stack/include/bactext.h | 3 + bacnet-stack/include/config.h | 2 + bacnet-stack/include/lighting.h | 82 ++ bacnet-stack/lib/Makefile | 1 + bacnet-stack/src/bacapp.c | 57 +- bacnet-stack/src/bactext.c | 40 +- bacnet-stack/src/lighting.c | 343 +++++++ 16 files changed, 3301 insertions(+), 289 deletions(-) create mode 100644 bacnet-stack/demo/object/channel.c create mode 100644 bacnet-stack/demo/object/channel.h create mode 100644 bacnet-stack/include/lighting.h create mode 100644 bacnet-stack/src/lighting.c diff --git a/bacnet-stack/demo/object/channel.c b/bacnet-stack/demo/object/channel.c new file mode 100644 index 00000000..7a30d9f3 --- /dev/null +++ b/bacnet-stack/demo/object/channel.c @@ -0,0 +1,1480 @@ +/** + * @file + * @author Steve Karg + * @date 2013 + * @brief Channel objects, customize for your use + * + * @section DESCRIPTION + * + * The Channel object is a command object without a priority array, and the + * present-value property uses a priority array and a single precision floating point + * 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. + */ +#include +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" +#include "handlers.h" +#include "lighting.h" +#include "device.h" +#if defined (CHANNEL_LIGHTING_COMMAND) || defined (BACAPP_LIGHTING_COMMAND) +#include "lo.h" +#endif +/* me! */ +#include "channel.h" + +#ifndef BACNET_CHANNELS_MAX +#define BACNET_CHANNELS_MAX 1 +#endif + +#ifndef CONTROL_GROUPS_MAX +#define CONTROL_GROUPS_MAX 8 +#endif + +#ifndef CHANNEL_MEMBERS_MAX +#define CHANNEL_MEMBERS_MAX 8 +#endif + +struct bacnet_channel_object { + bool Out_Of_Service:1; + BACNET_CHANNEL_VALUE Present_Value; + unsigned Last_Priority; + BACNET_WRITE_STATUS Write_Status; + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE Members[CHANNEL_MEMBERS_MAX]; + uint16_t Number; + uint32_t Control_Groups[CONTROL_GROUPS_MAX]; +}; + +struct bacnet_channel_object Channel[BACNET_CHANNELS_MAX]; + +/* 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_LAST_PRIORITY, + PROP_WRITE_STATUS, + PROP_STATUS_FLAGS, + PROP_OUT_OF_SERVICE, + PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES, + PROP_CHANNEL_NUMBER, + PROP_CONTROL_GROUPS, + -1 +}; + +static const int Properties_Optional[] = { + -1 +}; + +static const int 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 Channel_Property_Lists(const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Properties_Required; + if (pOptional) + *pOptional = Properties_Optional; + if (pProprietary) + *pProprietary = Properties_Proprietary; + + return; +} + +/** + * Determines if a given Channel instance is valid + * + * @param object_instance - object-instance number of the object + * + * @return true if the instance is valid, and false if not + */ +bool Channel_Valid_Instance(uint32_t object_instance) +{ + unsigned int index; + + index = Channel_Instance_To_Index(object_instance); + if (index < BACNET_CHANNELS_MAX) { + return true; + } + + return false; +} + +/** + * Determines the number of Channel objects + * + * @return Number of Channel objects + */ +unsigned Channel_Count(void) +{ + return BACNET_CHANNELS_MAX; +} + +/** + * Determines the object instance-number for a given 0..N index + * of Channel objects where N is Channel_Count(). + * + * @param index - 0..BACNET_CHANNELS_MAX value + * + * @return object instance-number for the given index + */ +uint32_t Channel_Index_To_Instance(unsigned index) +{ + uint32_t instance = 1; + + instance += index; + + return instance; +} + +/** + * For a given object instance-number, determines a 0..N index + * of Channel objects where N is Channel_Count(). + * + * @param object_instance - object-instance number of the object + * + * @return index for the given instance-number, or BACNET_CHANNELS_MAX + * if not valid. + */ +unsigned Channel_Instance_To_Index(uint32_t object_instance) +{ + unsigned index = BACNET_CHANNELS_MAX; + + if (object_instance) { + index = object_instance - 1; + if (index > BACNET_CHANNELS_MAX) { + index = BACNET_CHANNELS_MAX; + } + } + + return index; +} + +/** + * For a given object instance-number, determines the present-value + * + * @param object_instance - object-instance number of the object + * @return pointer to the BACNET_CHANNEL_VALUE present-value + */ +BACNET_CHANNEL_VALUE * Channel_Present_Value(uint32_t object_instance) +{ + unsigned index = 0; + BACNET_CHANNEL_VALUE *cvalue = NULL; + + index = Channel_Instance_To_Index(object_instance); + if (index < BACNET_CHANNELS_MAX) { + cvalue = &Channel[index].Present_Value; + } + + return cvalue; +} + +/** + * For a given object instance-number, determines the last priority. + * + * @param object_instance - object-instance number of the object + * + * @return priority - priority 1..16 + */ +unsigned Channel_Last_Priority(uint32_t object_instance) +{ + unsigned index = 0; + unsigned priority = 0; + + index = Channel_Instance_To_Index(object_instance); + if (index < BACNET_CHANNELS_MAX) { + priority = Channel[index].Last_Priority; + } + + return priority; +} + +/** + * For a given object instance-number, determines the write status. + * + * @param object_instance - object-instance number of the object + * + * @return BACNET_WRITE_STATUS value + */ +BACNET_WRITE_STATUS Channel_Write_Status(uint32_t object_instance) +{ + unsigned index = 0; + unsigned priority = 0; + + index = Channel_Instance_To_Index(object_instance); + if (index < BACNET_CHANNELS_MAX) { + priority = Channel[index].Write_Status; + } + + return priority; +} + +/** + * For a given object instance-number, determines the Number + * + * @param object_instance - object-instance number of the object + * + * @return Channel Number value + */ +uint16_t Channel_Number(uint32_t object_instance) +{ + unsigned index = 0; + uint16_t value = 0; + + index = Channel_Instance_To_Index(object_instance); + if (index < BACNET_CHANNELS_MAX) { + value = Channel[index].Number; + } + + return value; +} + +/** + * For a given object instance-number, sets the channel-number + * property value + * + * @param object_instance - object-instance number of the object + * @param value - channel-number value to set + * + * @return true if set + */ +bool Channel_Number_Set(uint32_t object_instance, uint16_t value) +{ + bool status = false; + unsigned index = 0; + + index = Channel_Instance_To_Index(object_instance); + if (index < BACNET_CHANNELS_MAX) { + Channel[index].Number = value; + status = true; + } + + return status; +} + +/** + * For a given object instance-number, determines the Number + * + * @param object_instance - object-instance number of the object + * @param array_index - 1-based array index + * + * @return group number in the array + */ +uint16_t Channel_Control_Groups_Element( + uint32_t object_instance, + int32_t array_index) +{ + unsigned index = 0; + uint16_t value = 0; + + index = Channel_Instance_To_Index(object_instance); + if ((index < BACNET_CHANNELS_MAX) && + (array_index > 0) && + (array_index <= CONTROL_GROUPS_MAX)) { + array_index--; + value = Channel[index].Control_Groups[array_index]; + } + + return value; +} + +/** + * For a given object instance-number, determines the Number + * + * @param object_instance - object-instance number of the object + * @param array_index - 1-based array index + * @param value - control group value 0..65535 + * + * @return true if parameters are value and control group is set + */ +bool Channel_Control_Groups_Element_Set( + uint32_t object_instance, + int32_t array_index, + uint16_t value) +{ + bool status = false; + unsigned index = 0; + + index = Channel_Instance_To_Index(object_instance); + if ((index < BACNET_CHANNELS_MAX) && + (array_index > 0) && + (array_index <= CONTROL_GROUPS_MAX)) { + array_index--; + Channel[index].Control_Groups[array_index] = value; + status = true; + } + + return status; +} + +/** + * For a given application value, copy to the channel value + * + * @param cvalue - BACNET_CHANNEL_VALUE value + * @param value - BACNET_APPLICATION_DATA_VALUE value + * + * @return true if values are able to be copied + */ +bool Channel_Value_Copy(BACNET_CHANNEL_VALUE * cvalue, + BACNET_APPLICATION_DATA_VALUE * value) +{ + bool status = false; + + if (!value || !cvalue) { + return false; + } + switch (value->tag) { +#if defined (BACAPP_NULL) + case BACNET_APPLICATION_TAG_NULL: + cvalue->tag = value->tag; + status = true; + break; +#endif +#if defined (BACAPP_BOOLEAN) && defined (CHANNEL_BOOLEAN) + case BACNET_APPLICATION_TAG_BOOLEAN: + cvalue->tag = value->tag; + cvalue->type.Boolean = value->type.Boolean; + status = true; + break; +#endif +#if defined (BACAPP_UNSIGNED) && defined (CHANNEL_UNSIGNED) + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + cvalue->tag = value->tag; + cvalue->type.Unsigned_Int = value->type.Unsigned_Int; + status = true; + break; +#endif +#if defined (BACAPP_SIGNED) && defined (CHANNEL_SIGNED) + case BACNET_APPLICATION_TAG_SIGNED_INT: + cvalue->tag = value->tag; + cvalue->type.Signed_Int = value->type.Signed_Int; + status = true; + break; +#endif +#if defined (BACAPP_REAL) && defined (CHANNEL_REAL) + case BACNET_APPLICATION_TAG_REAL: + cvalue->tag = value->tag; + cvalue->type.Real = value->type.Real; + status = true; + break; +#endif +#if defined (BACAPP_DOUBLE) && defined (CHANNEL_DOUBLE) + case BACNET_APPLICATION_TAG_DOUBLE: + cvalue->tag = value->tag; + cvalue->type.Double = value->type.Double; + status = true; + break; +#endif +#if defined (BACAPP_OCTET_STRING) && defined (CHANNEL_OCTET_STRING) + case BACNET_APPLICATION_TAG_OCTET_STRING: + cvalue->tag = value->tag; + octetstring_copy( + &cvalue->type.Octet_String, + &value->type.Octet_String); + status = true; + break; +#endif +#if defined (BACAPP_CHARACTER_STRING) && defined (CHANNEL_CHARACTER_STRING) + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + cvalue->tag = value->tag; + characterstring_copy( + &cvalue->type.Character_String, + &value->type.Character_String); + status = true; + break; +#endif +#if defined (BACAPP_BIT_STRING) && defined (CHANNEL_BIT_STRING) + case BACNET_APPLICATION_TAG_BIT_STRING: + cvalue->tag = value->tag; + bitstring_copy( + &cvalue->type.Bit_String, + &value->type.Bit_String); + status = true; + break; +#endif +#if defined (BACAPP_ENUMERATED) && defined (CHANNEL_ENUMERATED) + case BACNET_APPLICATION_TAG_ENUMERATED: + cvalue->tag = value->tag; + cvalue->type.Enumerated = value->type.Enumerated; + status = true; + break; +#endif +#if defined (BACAPP_DATE) && defined (CHANNEL_DATE) + case BACNET_APPLICATION_TAG_DATE: + cvalue->tag = value->tag; + datetime_date_copy( + &cvalue->type.Date, + &value->type.Date); + apdu_len = encode_application_date(&apdu[0], &value->type.Date); + status = true; + break; +#endif +#if defined (BACAPP_TIME) && defined (CHANNEL_TIME) + case BACNET_APPLICATION_TAG_TIME: + cvalue->tag = value->tag; + datetime_time_copy( + &cvalue->type.Time, + &value->type.Time); + break; +#endif +#if defined (BACAPP_OBJECT_ID) && defined (CHANNEL_OBJECT_ID) + case BACNET_APPLICATION_TAG_OBJECT_ID: + cvalue->tag = value->tag; + cvalue->type.Object_Id.type = value->type.Object_Id.type; + cvalue->type.Object_Id.instance = value->type.Object_Id.instance; + status = true; + break; +#endif +#if defined (BACAPP_LIGHTING_COMMAND) && defined (CHANNEL_LIGHTING_COMMAND) + case BACNET_APPLICATION_TAG_LIGHTING_COMMAND: + cvalue->tag = value->tag; + lighting_command_copy( + &cvalue->type.Lighting_Command, + &value->type.Lighting_Command); + status = true; + break; +#endif + default: + break; + } + + return status; +} + +/** + * For a given application value, copy to the channel value + * + * @param apdu - APDU buffer for storing the encoded data + * @param apdu_max - size of APDU buffer available for storing data + * @param value - BACNET_CHANNEL_VALUE value + * + * @return number of bytes in the APDU, or BACNET_STATUS_ERROR + */ +int Channel_Value_Encode(uint8_t *apdu, int apdu_max, + BACNET_CHANNEL_VALUE * value) +{ + int apdu_len = BACNET_STATUS_ERROR; + + if (!apdu || !value) { + return BACNET_STATUS_ERROR; + } + switch (value->tag) { + case BACNET_APPLICATION_TAG_NULL: + apdu_len = encode_application_null(&apdu[0]); + break; +#if defined (CHANNEL_BOOLEAN) + case BACNET_APPLICATION_TAG_BOOLEAN: + apdu_len = encode_application_boolean(&apdu[0], + value->type.Boolean); + break; +#endif +#if defined (CHANNEL_UNSIGNED) + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + apdu_len = + encode_application_unsigned(&apdu[0], + value->type.Unsigned_Int); + break; +#endif +#if defined (CHANNEL_SIGNED) + case BACNET_APPLICATION_TAG_SIGNED_INT: + apdu_len = + encode_application_signed(&apdu[0], + value->type.Signed_Int); + break; +#endif +#if defined (CHANNEL_REAL) + case BACNET_APPLICATION_TAG_REAL: + apdu_len = encode_application_real(&apdu[0], value->type.Real); + break; +#endif +#if defined (CHANNEL_DOUBLE) + case BACNET_APPLICATION_TAG_DOUBLE: + apdu_len = + encode_application_double(&apdu[0], value->type.Double); + break; +#endif +#if defined (CHANNEL_OCTET_STRING) + case BACNET_APPLICATION_TAG_OCTET_STRING: + apdu_len = + encode_application_octet_string(&apdu[0], + &value->type.Octet_String); + break; +#endif +#if defined (CHANNEL_CHARACTER_STRING) + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + apdu_len = + encode_application_character_string(&apdu[0], + &value->type.Character_String); + break; +#endif +#if defined (CHANNEL_BIT_STRING) + case BACNET_APPLICATION_TAG_BIT_STRING: + apdu_len = + encode_application_bitstring(&apdu[0], + &value->type.Bit_String); + break; +#endif +#if defined (CHANNEL_ENUMERATED) + case BACNET_APPLICATION_TAG_ENUMERATED: + apdu_len = + encode_application_enumerated(&apdu[0], + value->type.Enumerated); + break; +#endif +#if defined (CHANNEL_DATE) + case BACNET_APPLICATION_TAG_DATE: + apdu_len = + encode_application_date(&apdu[0], &value->type.Date); + break; +#endif +#if defined (CHANNEL_TIME) + case BACNET_APPLICATION_TAG_TIME: + apdu_len = + encode_application_time(&apdu[0], &value->type.Time); + break; +#endif +#if defined (CHANNEL_OBJECT_ID) + case BACNET_APPLICATION_TAG_OBJECT_ID: + apdu_len = + encode_application_object_id(&apdu[0], + (int) value->type.Object_Id.type, + value->type.Object_Id.instance); + break; +#endif +#if defined (CHANNEL_LIGHTING_COMMAND) + case BACNET_APPLICATION_TAG_LIGHTING_COMMAND: + apdu_len = + lighting_command_encode(&apdu[0], + &value->type.Lighting_Command); + break; +#endif + default: + break; + } + + return apdu_len; +} + +/** + * For a given application value, coerce the encoding, if necessary + * + * @param apdu - buffer to hold the encoding + * @param apdu_max - max size of the buffer to hold the encoding + * @param value - BACNET_APPLICATION_DATA_VALUE value + * @param tag - application tag to be coerced, if possible + * + * @return number of bytes in the APDU, or BACNET_STATUS_ERROR if error. + */ +int Channel_Coerce_Data_Encode( + uint8_t * apdu, + unsigned max_apdu, + BACNET_APPLICATION_DATA_VALUE * value, + BACNET_APPLICATION_TAG tag) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + float float_value = 0.0; + double double_value = 0.0; + uint32_t unsigned_value = 0; + int32_t signed_value = 0; + bool boolean_value = false; + + (void)max_apdu; + if (apdu && value) { + switch (value->tag) { +#if defined (BACAPP_NULL) + case BACNET_APPLICATION_TAG_NULL: + if (tag == BACNET_APPLICATION_TAG_LIGHTING_COMMAND) { + apdu_len = BACNET_STATUS_ERROR; + } else { + /* no coercion */ + apdu[0] = value->tag; + apdu_len++; + } + break; +#endif +#if defined (BACAPP_BOOLEAN) + case BACNET_APPLICATION_TAG_BOOLEAN: + if (tag == BACNET_APPLICATION_TAG_BOOLEAN) { + apdu_len = + encode_application_boolean(&apdu[0], value->type.Boolean); + } else if (tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if (value->type.Boolean) { + unsigned_value = 1; + } + apdu_len = encode_application_unsigned(&apdu[0], unsigned_value); + } else if (tag == BACNET_APPLICATION_TAG_SIGNED_INT) { + if (value->type.Boolean) { + signed_value = 1; + } + apdu_len = encode_application_signed(&apdu[0], signed_value); + } else if (tag == BACNET_APPLICATION_TAG_REAL) { + if (value->type.Boolean) { + float_value = 1; + } + apdu_len = encode_application_real(&apdu[0], float_value); + } else if (tag == BACNET_APPLICATION_TAG_DOUBLE) { + if (value->type.Boolean) { + double_value = 1; + } + apdu_len = encode_application_double(&apdu[0], double_value); + } else if (tag == BACNET_APPLICATION_TAG_ENUMERATED) { + if (value->type.Boolean) { + unsigned_value = 1; + } + apdu_len = + encode_application_enumerated(&apdu[0], unsigned_value); + } else { + apdu_len = BACNET_STATUS_ERROR; + } + break; +#endif +#if defined (BACAPP_UNSIGNED) + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + if (tag == BACNET_APPLICATION_TAG_BOOLEAN) { + if (value->type.Unsigned_Int) { + boolean_value = true; + } + apdu_len = + encode_application_boolean(&apdu[0], boolean_value); + } else if (tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + unsigned_value = value->type.Unsigned_Int; + apdu_len = encode_application_unsigned(&apdu[0], + unsigned_value); + } else if (tag == BACNET_APPLICATION_TAG_SIGNED_INT) { + if (value->type.Unsigned_Int <= 2147483647) { + signed_value = value->type.Unsigned_Int; + apdu_len = encode_application_signed(&apdu[0], + signed_value); + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else if (tag == BACNET_APPLICATION_TAG_REAL) { + if (value->type.Unsigned_Int <= 9999999) { + float_value = value->type.Unsigned_Int; + apdu_len = encode_application_real(&apdu[0], + float_value); + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else if (tag == BACNET_APPLICATION_TAG_DOUBLE) { + double_value = value->type.Unsigned_Int; + apdu_len = encode_application_double(&apdu[0], + double_value); + } else if (tag == BACNET_APPLICATION_TAG_ENUMERATED) { + unsigned_value = value->type.Unsigned_Int; + apdu_len = encode_application_enumerated(&apdu[0], + unsigned_value); + } else { + apdu_len = BACNET_STATUS_ERROR; + } + break; +#endif +#if defined (BACAPP_SIGNED) + case BACNET_APPLICATION_TAG_SIGNED_INT: + if (tag == BACNET_APPLICATION_TAG_BOOLEAN) { + if (value->type.Signed_Int) { + boolean_value = true; + } + apdu_len = + encode_application_boolean(&apdu[0], boolean_value); + } else if (tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if ((value->type.Signed_Int >= 0) && + (value->type.Signed_Int <= 2147483647)) { + unsigned_value = value->type.Signed_Int; + apdu_len = encode_application_unsigned(&apdu[0], + unsigned_value); + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else if (tag == BACNET_APPLICATION_TAG_SIGNED_INT) { + signed_value = value->type.Signed_Int; + apdu_len = encode_application_signed(&apdu[0], + signed_value); + } else if (tag == BACNET_APPLICATION_TAG_REAL) { + if (value->type.Signed_Int <= 9999999) { + float_value = value->type.Signed_Int; + apdu_len = encode_application_real(&apdu[0], + float_value); + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else if (tag == BACNET_APPLICATION_TAG_DOUBLE) { + double_value = value->type.Signed_Int; + apdu_len = encode_application_double(&apdu[0], + double_value); + } else if (tag == BACNET_APPLICATION_TAG_ENUMERATED) { + unsigned_value = value->type.Signed_Int; + apdu_len = encode_application_enumerated(&apdu[0], + unsigned_value); + } else { + apdu_len = BACNET_STATUS_ERROR; + } + break; +#endif +#if defined (BACAPP_REAL) + case BACNET_APPLICATION_TAG_REAL: + if (tag == BACNET_APPLICATION_TAG_BOOLEAN) { + if (value->type.Real) { + boolean_value = true; + } + apdu_len = + encode_application_boolean(&apdu[0], boolean_value); + } else if (tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if ((value->type.Real >= 0.0) && + (value->type.Real <= 2147483000.0)) { + unsigned_value = value->type.Real; + apdu_len = encode_application_unsigned(&apdu[0], + unsigned_value); + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else if (tag == BACNET_APPLICATION_TAG_SIGNED_INT) { + if ((value->type.Real >= -2147483000.0) && + (value->type.Real <= 214783000.0)) { + signed_value = value->type.Real; + apdu_len = encode_application_signed(&apdu[0], + signed_value); + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else if (tag == BACNET_APPLICATION_TAG_REAL) { + float_value = value->type.Real; + apdu_len = encode_application_real(&apdu[0], + float_value); + } else if (tag == BACNET_APPLICATION_TAG_DOUBLE) { + double_value = value->type.Real; + apdu_len = encode_application_double(&apdu[0], + double_value); + } else if (tag == BACNET_APPLICATION_TAG_ENUMERATED) { + if ((value->type.Real >= 0.0) && + (value->type.Real <= 2147483000.0)) { + unsigned_value = value->type.Real; + apdu_len = encode_application_enumerated(&apdu[0], + unsigned_value); + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else { + apdu_len = BACNET_STATUS_ERROR; + } + break; +#endif +#if defined (BACAPP_DOUBLE) + case BACNET_APPLICATION_TAG_DOUBLE: + if (tag == BACNET_APPLICATION_TAG_BOOLEAN) { + if (value->type.Double) { + boolean_value = true; + } + apdu_len = + encode_application_boolean(&apdu[0], boolean_value); + } else if (tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if ((value->type.Double >= 0.0) && + (value->type.Double <= 2147483000.0)) { + unsigned_value = value->type.Double; + apdu_len = encode_application_unsigned(&apdu[0], + unsigned_value); + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else if (tag == BACNET_APPLICATION_TAG_SIGNED_INT) { + if ((value->type.Double >= -2147483000.0) && + (value->type.Double <= 214783000.0)) { + signed_value = value->type.Double; + apdu_len = encode_application_signed(&apdu[0], + signed_value); + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else if (tag == BACNET_APPLICATION_TAG_REAL) { + if ((value->type.Double >= 3.4E-38) && + (value->type.Double <= 3.4E+38)) { + float_value = value->type.Double; + apdu_len = encode_application_real(&apdu[0], + float_value); + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else if (tag == BACNET_APPLICATION_TAG_DOUBLE) { + double_value = value->type.Double; + apdu_len = encode_application_double(&apdu[0], + double_value); + } else if (tag == BACNET_APPLICATION_TAG_ENUMERATED) { + if ((value->type.Double >= 0.0) && + (value->type.Double <= 2147483000.0)) { + unsigned_value = value->type.Double; + apdu_len = encode_application_enumerated(&apdu[0], + unsigned_value); + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else { + apdu_len = BACNET_STATUS_ERROR; + } + break; +#endif +#if defined (BACAPP_ENUMERATED) + case BACNET_APPLICATION_TAG_ENUMERATED: + if (tag == BACNET_APPLICATION_TAG_BOOLEAN) { + if (value->type.Enumerated) { + boolean_value = true; + } + apdu_len = + encode_application_boolean(&apdu[0], boolean_value); + } else if (tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + unsigned_value = value->type.Enumerated; + apdu_len = encode_application_unsigned(&apdu[0], + unsigned_value); + } else if (tag == BACNET_APPLICATION_TAG_SIGNED_INT) { + if (value->type.Enumerated <= 2147483647) { + signed_value = value->type.Enumerated; + apdu_len = encode_application_signed(&apdu[0], + signed_value); + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else if (tag == BACNET_APPLICATION_TAG_REAL) { + if (value->type.Enumerated <= 9999999) { + float_value = value->type.Enumerated; + apdu_len = encode_application_real(&apdu[0], + float_value); + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else if (tag == BACNET_APPLICATION_TAG_DOUBLE) { + double_value = value->type.Enumerated; + apdu_len = encode_application_double(&apdu[0], + double_value); + } else if (tag == BACNET_APPLICATION_TAG_ENUMERATED) { + unsigned_value = value->type.Enumerated; + apdu_len = encode_application_enumerated(&apdu[0], + unsigned_value); + } else { + apdu_len = BACNET_STATUS_ERROR; + } + break; +#endif +#if defined (BACAPP_LIGHTING_COMMAND) + case BACNET_APPLICATION_TAG_LIGHTING_COMMAND: + if (tag == BACNET_APPLICATION_TAG_LIGHTING_COMMAND) { + apdu_len = + lighting_command_encode(&apdu[0], + &value->type.Lighting_Command); + } else { + apdu_len = BACNET_STATUS_ERROR; + } + break; +#endif + default: + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + + return apdu_len; +} + +/** + * For a given object instance-number, sets the present-value at a given + * priority 1..16. + * + * @param wp_data - all of the WriteProperty data structure + * + * @return true if values are within range and present-value is sent. + */ +bool Channel_Write_Member_Value( + BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_APPLICATION_DATA_VALUE * value) +{ + bool status = false; + int apdu_len = 0; + + if (wp_data && value) { + if (((wp_data->object_type == OBJECT_ANALOG_INPUT) || + (wp_data->object_type == OBJECT_ANALOG_OUTPUT) || + (wp_data->object_type == OBJECT_ANALOG_VALUE)) && + (wp_data->object_property == PROP_PRESENT_VALUE) && + (wp_data->array_index == BACNET_ARRAY_ALL)) { + apdu_len = Channel_Coerce_Data_Encode( + wp_data->application_data, + wp_data->application_data_len, + value, + BACNET_APPLICATION_TAG_REAL); + if (apdu_len != BACNET_STATUS_ERROR) { + wp_data->application_data_len = apdu_len; + status = true; + } + } else if (((wp_data->object_type == OBJECT_BINARY_INPUT) || + (wp_data->object_type == OBJECT_BINARY_OUTPUT) || + (wp_data->object_type == OBJECT_BINARY_VALUE)) && + (wp_data->object_property == PROP_PRESENT_VALUE) && + (wp_data->array_index == BACNET_ARRAY_ALL)) { + apdu_len = Channel_Coerce_Data_Encode( + wp_data->application_data, + wp_data->application_data_len, + value, + BACNET_APPLICATION_TAG_ENUMERATED); + if (apdu_len != BACNET_STATUS_ERROR) { + wp_data->application_data_len = apdu_len; + status = true; + } + } else if (((wp_data->object_type == OBJECT_MULTI_STATE_INPUT) || + (wp_data->object_type == OBJECT_MULTI_STATE_OUTPUT) || + (wp_data->object_type == OBJECT_MULTI_STATE_VALUE)) && + (wp_data->object_property == PROP_PRESENT_VALUE) && + (wp_data->array_index == BACNET_ARRAY_ALL)) { + apdu_len = Channel_Coerce_Data_Encode( + wp_data->application_data, + wp_data->application_data_len, + value, + BACNET_APPLICATION_TAG_UNSIGNED_INT); + if (apdu_len != BACNET_STATUS_ERROR) { + wp_data->application_data_len = apdu_len; + status = true; + } + } else if (wp_data->object_type == OBJECT_LIGHTING_OUTPUT) { + if ((wp_data->object_property == PROP_PRESENT_VALUE) && + (wp_data->array_index == BACNET_ARRAY_ALL)) { + apdu_len = Channel_Coerce_Data_Encode( + wp_data->application_data, + wp_data->application_data_len, + value, + BACNET_APPLICATION_TAG_REAL); + if (apdu_len != BACNET_STATUS_ERROR) { + wp_data->application_data_len = apdu_len; + status = true; + } + } else if ((wp_data->object_property == PROP_LIGHTING_COMMAND) && + (wp_data->array_index == BACNET_ARRAY_ALL)) { + apdu_len = Channel_Coerce_Data_Encode( + wp_data->application_data, + wp_data->application_data_len, + value, + BACNET_APPLICATION_TAG_LIGHTING_COMMAND); + if (apdu_len != BACNET_STATUS_ERROR) { + wp_data->application_data_len = apdu_len; + status = true; + } + } + } + } + + return status; +} + +/** + * For a given object instance-number, sets the present-value at a given + * priority 1..16. + * + * @param wp_data - all of the WriteProperty data structure + * + * @return true if values are within range and present-value is sent. + */ +bool Channel_Write_Members( + struct bacnet_channel_object * pChannel, + BACNET_APPLICATION_DATA_VALUE * value, + uint8_t priority) +{ + BACNET_WRITE_PROPERTY_DATA wp_data = {0}; + bool status = false; + unsigned m = 0; + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *pMember = NULL; + + if (pChannel && value) { + pChannel->Write_Status = BACNET_WRITE_STATUS_IN_PROGRESS; + for (m = 0; m < CHANNEL_MEMBERS_MAX; m++) { + pMember = &pChannel->Members[m]; + /* NOTE: our implementation is for internal objects only */ + /* NOTE: we could check to match our Device ID, but then + we would need to update all channels when our device ID + changed. Instead, we'll just screen when members are + set. */ + if ((pMember->deviceIndentifier.type == OBJECT_DEVICE) && + (pMember->deviceIndentifier.instance != BACNET_MAX_INSTANCE) && + (pMember->objectIdentifier.instance != BACNET_MAX_INSTANCE)) { + wp_data.object_type = pMember->objectIdentifier.type; + wp_data.object_instance = pMember->objectIdentifier.instance; + wp_data.object_property = pMember->propertyIdentifier; + wp_data.array_index = pMember->arrayIndex; + wp_data.priority = priority; + wp_data.application_data_len = + sizeof(wp_data.application_data); + status = Channel_Write_Member_Value(&wp_data, value); + if (status) { + status = Device_Write_Property(&wp_data); + } else { + pChannel->Write_Status = BACNET_WRITE_STATUS_FAILED; + } + } + } + if (pChannel->Write_Status == BACNET_WRITE_STATUS_IN_PROGRESS) { + pChannel->Write_Status = BACNET_WRITE_STATUS_SUCCESSFUL; + } + } + + return status; +} + +/** + * For a given object instance-number, sets the present-value at a given + * priority 1..16. + * + * @param wp_data - all of the WriteProperty data structure + * + * @return true if values are within range and present-value is sent. + */ +bool Channel_Present_Value_Set( + BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_APPLICATION_DATA_VALUE * value) +{ + unsigned index = 0; + bool status = false; + + index = Channel_Instance_To_Index(wp_data->object_instance); + if (index < BACNET_CHANNELS_MAX) { + if ((wp_data->priority > 0) && + (wp_data->priority <= BACNET_MAX_PRIORITY)) { + if (wp_data->priority != 6 /* reserved */ ) { + status = Channel_Value_Copy(&Channel[index].Present_Value, + value); + status = Channel_Write_Members(&Channel[index], value, + wp_data->priority); + status = true; + } else { + /* 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; + } + } + + return status; +} + +/** + * 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 Channel_Object_Name(uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + char text_string[32] = ""; + bool status = false; + unsigned index = 0; + + index = Channel_Instance_To_Index(object_instance); + if (index < BACNET_CHANNELS_MAX) { + sprintf(text_string, "CHANNEL %lu", + (unsigned long) object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + 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 Channel_Out_Of_Service(uint32_t instance) +{ + unsigned int index = 0; + bool value = false; + + index = Channel_Instance_To_Index(instance); + if (index < BACNET_CHANNELS_MAX) { + value = Channel[index].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 Channel_Out_Of_Service_Set(uint32_t instance, + bool value) +{ + unsigned int index = 0; + + index = Channel_Instance_To_Index(instance); + if (index < BACNET_CHANNELS_MAX) { + Channel[index].Out_Of_Service = value; + } +} + +/** + * ReadProperty handler for this object. For the given ReadProperty + * data, the application_data is loaded or the error flags are set. + * + * @param rpdata - ReadProperty data, including requested data and + * data for the reply, or error response. + * + * @return number of APDU bytes in the response, or + * BACNET_STATUS_ERROR on error. + */ +int Channel_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; + BACNET_CHANNEL_VALUE * cvalue = NULL; + uint32_t unsigned_value = 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_CHANNEL, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + Channel_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_CHANNEL); + break; + case PROP_PRESENT_VALUE: + cvalue = Channel_Present_Value(rpdata->object_instance); + apdu_len = Channel_Value_Encode(&apdu[0], MAX_APDU, cvalue); + if (apdu_len == BACNET_STATUS_ERROR) { + apdu_len = encode_application_null(&apdu[0]); + } + break; + case PROP_LAST_PRIORITY: + unsigned_value = Channel_Last_Priority(rpdata->object_instance); + apdu_len = + encode_application_unsigned(&apdu[0], unsigned_value); + break; + case PROP_WRITE_STATUS: + unsigned_value = + (BACNET_WRITE_STATUS)Channel_Write_Status( + rpdata->object_instance); + apdu_len = + encode_application_unsigned(&apdu[0], unsigned_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 = Channel_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_OUT_OF_SERVICE: + state = Channel_Out_Of_Service(rpdata->object_instance); + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES: + break; + case PROP_CHANNEL_NUMBER: + unsigned_value = Channel_Number(rpdata->object_instance); + apdu_len = + encode_application_unsigned(&apdu[0], unsigned_value); + break; + case PROP_CONTROL_GROUPS: + /* Array element zero is the number of elements in the array */ + if (rpdata->array_index == 0) { + apdu_len = encode_application_unsigned(&apdu[0], + CONTROL_GROUPS_MAX); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + } else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 1; i <= CONTROL_GROUPS_MAX; i++) { + unsigned_value = Channel_Control_Groups_Element( + rpdata->object_instance, i); + len = + encode_application_unsigned(&apdu[apdu_len], + unsigned_value); + /* 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 <= CONTROL_GROUPS_MAX) { + unsigned_value = Channel_Control_Groups_Element( + rpdata->object_instance, rpdata->array_index); + apdu_len = + encode_application_unsigned(&apdu[apdu_len], + unsigned_value); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) + && (rpdata->object_property != PROP_PRIORITY_ARRAY) + && (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/** + * 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 Channel_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + int element_len = 0; + uint32_t count = 0; + uint32_t array_index = 0; + + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((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; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + status = Channel_Present_Value_Set(wp_data, &value); + break; + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Channel_Out_Of_Service_Set(wp_data->object_instance, + value.type.Boolean); + } + break; + case PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES: +// FIXME: add property handling +// status = Channel_List_Of_Object_Property_References_Set( +// wp_data, +// &value); + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED; + break; + case PROP_CHANNEL_NUMBER: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Channel_Number_Set(wp_data->object_instance, + value.type.Unsigned_Int); + } + break; + case PROP_CONTROL_GROUPS: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if (wp_data->array_index == 0) { + /* Array element zero is the number of elements in the array */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else if (wp_data->array_index == BACNET_ARRAY_ALL) { + count = CONTROL_GROUPS_MAX; + array_index = 1; + /* extra elements still encoded in application data */ + element_len = len; + do { + if ((element_len > 0) && + (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT)) { + if ((wp_data->array_index <= CONTROL_GROUPS_MAX) && + (value.type.Unsigned_Int <= 65535)) { + status = Channel_Control_Groups_Element_Set( + wp_data->object_instance, + wp_data->array_index, + value.type.Unsigned_Int); + } + if (!status) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + break; + } + } + count--; + array_index++; + if (count) { + element_len = bacapp_decode_application_data( + &wp_data->application_data[len], + wp_data->application_data_len-len, + &value); + if (element_len < 0) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + break; + } + len += element_len; + } + } while (count); + } else { + if ((wp_data->array_index <= CONTROL_GROUPS_MAX) && + (value.type.Unsigned_Int <= 65535)) { + status = Channel_Control_Groups_Element_Set( + wp_data->object_instance, + wp_data->array_index, + value.type.Unsigned_Int); + } + if (!status) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_LAST_PRIORITY: + case PROP_WRITE_STATUS: + case PROP_STATUS_FLAGS: + case PROP_PROPERTY_LIST: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + +/** + * Initializes the Channel object data + */ +void Channel_Init(void) +{ + unsigned i, m, g; + + for (i = 0; i < BACNET_CHANNELS_MAX; i++) { + Channel[i].Present_Value.tag = BACNET_APPLICATION_TAG_EMPTYLIST; + Channel[i].Out_Of_Service = false; + Channel[i].Last_Priority = BACNET_NO_PRIORITY; + Channel[i].Write_Status = BACNET_WRITE_STATUS_IDLE; + for (m = 0; m < CHANNEL_MEMBERS_MAX; m++) { + Channel[i].Members[m].objectIdentifier.type = + OBJECT_CHANNEL; + Channel[i].Members[m].objectIdentifier.instance = + BACNET_MAX_INSTANCE; + Channel[i].Members[m].propertyIdentifier = MAX_BACNET_PROPERTY_ID; + Channel[i].Members[m].arrayIndex = BACNET_ARRAY_ALL; + Channel[i].Members[m].deviceIndentifier.type = + OBJECT_DEVICE; + Channel[i].Members[m].deviceIndentifier.instance = + BACNET_MAX_INSTANCE; + } + Channel[i].Number = 0; + for (g = 0; g < CONTROL_GROUPS_MAX; g++) { + Channel[i].Control_Groups[g] = 0; + } + } + + return; +} diff --git a/bacnet-stack/demo/object/channel.h b/bacnet-stack/demo/object/channel.h new file mode 100644 index 00000000..09624604 --- /dev/null +++ b/bacnet-stack/demo/object/channel.h @@ -0,0 +1,168 @@ +/** + * @file + * @author Steve Karg + * @date 2013 + * @brief Channel objects, customize for your use + * + * @section DESCRIPTION + * + * The Channel object is a command object without a priority array, and the + * present-value property uses a priority array and a single precision floating point + * 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 CHANNEL_H +#define CHANNEL_H + +#include +#include +#include "bacdef.h" +#include "rp.h" +#include "wp.h" +#include "lo.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* BACNET_CHANNEL_VALUE decodes WriteProperty service requests + Choose the datatypes that your application supports */ +#if !(defined(CHANNEL_NUMERIC) || \ + defined(CHANNEL_NULL) || \ + defined(CHANNEL_BOOLEAN) || \ + defined(CHANNEL_UNSIGNED) || \ + defined(CHANNEL_SIGNED) || \ + defined(CHANNEL_REAL) || \ + defined(CHANNEL_DOUBLE) || \ + defined(CHANNEL_OCTET_STRING) || \ + defined(CHANNEL_CHARACTER_STRING) || \ + defined(CHANNEL_BIT_STRING) || \ + defined(CHANNEL_ENUMERATED) || \ + defined(CHANNEL_DATE) || \ + defined(CHANNEL_TIME) || \ + defined(CHANNEL_OBJECT_ID) || \ + defined(CHANNEL_LIGHTING_COMMAND)) +#define CHANNEL_NUMERIC +#endif + +#if defined (CHANNEL_NUMERIC) +#define CHANNEL_NULL +#define CHANNEL_BOOLEAN +#define CHANNEL_UNSIGNED +#define CHANNEL_SIGNED +#define CHANNEL_REAL +#define CHANNEL_DOUBLE +#define CHANNEL_ENUMERATED +#define CHANNEL_LIGHTING_COMMAND +#endif + + typedef struct BACnet_Channel_Value_t { + uint8_t tag; + union { + /* NULL - not needed as it is encoded in the tag alone */ +#if defined (CHANNEL_BOOLEAN) + bool Boolean; +#endif +#if defined (CHANNEL_UNSIGNED) + uint32_t Unsigned_Int; +#endif +#if defined (CHANNEL_SIGNED) + int32_t Signed_Int; +#endif +#if defined (CHANNEL_REAL) + float Real; +#endif +#if defined (CHANNEL_DOUBLE) + double Double; +#endif +#if defined (CHANNEL_OCTET_STRING) + BACNET_OCTET_STRING Octet_String; +#endif +#if defined (CHANNEL_CHARACTER_STRING) + BACNET_CHARACTER_STRING Character_String; +#endif +#if defined (CHANNEL_BIT_STRING) + BACNET_BIT_STRING Bit_String; +#endif +#if defined (CHANNEL_ENUMERATED) + uint32_t Enumerated; +#endif +#if defined (CHANNEL_DATE) + BACNET_DATE Date; +#endif +#if defined (CHANNEL_TIME) + BACNET_TIME Time; +#endif +#if defined (CHANNEL_OBJECT_ID) + BACNET_OBJECT_ID Object_Id; +#endif +#if defined (CHANNEL_LIGHTING_COMMAND) + BACNET_LIGHTING_COMMAND Lighting_Command; +#endif + } type; + /* simple linked list if needed */ + struct BACnet_Channel_Value_t *next; + } BACNET_CHANNEL_VALUE; + + void Channel_Property_Lists(const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool Channel_Valid_Instance(uint32_t object_instance); + unsigned Channel_Count(void); + uint32_t Channel_Index_To_Instance(unsigned index); + unsigned Channel_Instance_To_Index(uint32_t instance); + bool Channel_Object_Instance_Add(uint32_t instance); + + bool Channel_Object_Name(uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool Channel_Name_Set(uint32_t object_instance, + char *new_name); + + int Channel_Read_Property(BACNET_READ_PROPERTY_DATA * rpdata); + bool Channel_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data); + + BACNET_CHANNEL_VALUE * Channel_Present_Value(uint32_t object_instance); + bool Channel_Present_Value_Set( + BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_APPLICATION_DATA_VALUE * value); + + bool Channel_Out_Of_Service(uint32_t object_instance); + void Channel_Out_Of_Service_Set(uint32_t object_instance, + bool oos_flag); + + unsigned Channel_Last_Priority(uint32_t object_instance); + BACNET_WRITE_STATUS Channel_Write_Status(uint32_t object_instance); + uint16_t Channel_Number(uint32_t object_instance); + bool Channel_Number_Set(uint32_t object_instance, uint16_t value); + + void Channel_Init(void); + +#ifdef TEST +#include "ctest.h" + void testChannelObject(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/demo/object/device.c b/bacnet-stack/demo/object/device.c index 1c27657c..08128bc9 100644 --- a/bacnet-stack/demo/object/device.c +++ b/bacnet-stack/demo/object/device.c @@ -54,6 +54,7 @@ #include "bi.h" #include "bo.h" #include "bv.h" +#include "channel.h" #include "csv.h" #include "lc.h" #include "lsp.h" @@ -197,7 +198,6 @@ static object_functions_t My_Object_Table[] = { NULL /* COV */ , NULL /* COV Clear */ , NULL /* Intrinsic Reporting */ }, -#if 0 {OBJECT_CHARACTERSTRING_VALUE, CharacterString_Value_Init, CharacterString_Value_Count, @@ -213,7 +213,6 @@ static object_functions_t My_Object_Table[] = { NULL /* COV */ , NULL /* COV Clear */ , NULL /* Intrinsic Reporting */ }, -#endif #if defined(INTRINSIC_REPORTING) {OBJECT_NOTIFICATION_CLASS, Notification_Class_Init, @@ -291,7 +290,6 @@ static object_functions_t My_Object_Table[] = { NULL /* COV */ , NULL /* COV Clear */ , NULL /* Intrinsic Reporting */ }, -#if 0 {OBJECT_MULTI_STATE_VALUE, Multistate_Value_Init, Multistate_Value_Count, @@ -307,7 +305,6 @@ static object_functions_t My_Object_Table[] = { NULL /* COV */ , NULL /* COV Clear */ , NULL /* Intrinsic Reporting */ }, -#endif {OBJECT_TRENDLOG, Trend_Log_Init, Trend_Log_Count, @@ -323,6 +320,36 @@ static object_functions_t My_Object_Table[] = { NULL /* COV */ , NULL /* COV Clear */ , NULL /* Intrinsic Reporting */ }, + {OBJECT_LIGHTING_OUTPUT, + Lighting_Output_Init, + Lighting_Output_Count, + Lighting_Output_Index_To_Instance, + Lighting_Output_Valid_Instance, + Lighting_Output_Object_Name, + Lighting_Output_Read_Property, + Lighting_Output_Write_Property, + Lighting_Output_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, + {OBJECT_CHANNEL, + Channel_Init, + Channel_Count, + Channel_Index_To_Instance, + Channel_Valid_Instance, + Channel_Object_Name, + Channel_Read_Property, + Channel_Write_Property, + Channel_Property_Lists, + NULL /* ReadRangeInfo */ , + NULL /* Iterator */ , + NULL /* Value_Lists */ , + NULL /* COV */ , + NULL /* COV Clear */ , + NULL /* Intrinsic Reporting */ }, #if defined(BACFILE) {OBJECT_FILE, bacfile_init, diff --git a/bacnet-stack/demo/object/lo.c b/bacnet-stack/demo/object/lo.c index 4374c34f..5c591f36 100644 --- a/bacnet-stack/demo/object/lo.c +++ b/bacnet-stack/demo/object/lo.c @@ -1,6 +1,10 @@ -/************************************************************************** +/** +* @file +* @author Steve Karg +* @date 2013 +* @brief Lighting Output object * -* Copyright (C) 2007 Steve Karg +* @section LICENSE * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -21,11 +25,7 @@ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * -*********************************************************************/ - -/* Lighting Output Objects - customize for your use */ - -/* FIXME: This object was written to the BACnet DRAFT addendum. */ +*/ #include #include @@ -37,217 +37,182 @@ #include "config.h" /* the custom stuff */ #include "rp.h" #include "wp.h" +#include "lighting.h" #include "handlers.h" +/* me! */ +#include "lo.h" #ifndef MAX_LIGHTING_OUTPUTS -#define MAX_LIGHTING_OUTPUTS 5 +#define MAX_LIGHTING_OUTPUTS 8 #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 LIGHTING_LEVEL_NULL 255 -/* When all the priorities are level null, the present value returns */ -/* the Relinquish Default value */ -#define LIGHTING_RELINQUISH_DEFAULT 0 +struct lighting_output_object { + float Present_Value; + float Tracking_Value; + BACNET_LIGHTING_COMMAND Lighting_Command; + BACNET_LIGHTING_IN_PROGRESS In_Progress; + bool Out_Of_Service:1; + bool Blink_Warn_Enable:1; + bool Egress_Active:1; + uint32_t Egress_Time; + uint32_t Default_Fade_Time; + float Default_Ramp_Rate; + float Default_Step_Increment; + BACNET_LIGHTING_TRANSITION Transition; + float Feedback_Value; + float Priority_Array[BACNET_MAX_PRIORITY]; + uint16_t Priority_Active_Bits; + float Relinquish_Default; + float Power; + float Instantaneous_Power; + float Min_Actual_Value; + float Max_Actual_Value; + uint8_t Lighting_Command_Default_Priority; +}; +struct lighting_output_object Lighting_Output[MAX_LIGHTING_OUTPUTS]; -/* note: although the standard specifies REAL values for some - of the optional parameters, we represent them interally as - integers. */ -typedef struct LightingCommand { - BACNET_LIGHTING_OPERATION operation; - uint8_t level; /* 0..100 percent, 255=not used */ - uint8_t ramp_rate; /* 0..100 percent-per-second, 255=not used */ - uint8_t step_increment; /* 0..100 amount to step, 255=not used */ - uint16_t fade_time; /* 1..65535 seconds to transition, 0=not used */ - uint16_t duration; /* 1..65535 minutes until relinquish, 0=not used */ -} BACNET_LIGHTING_COMMAND; +/* 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_UNITS, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + -1 +}; -/* Here is our Priority Array. They are supposed to be Real, but */ -/* we might not 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 - Lighting_Output_Level[MAX_LIGHTING_OUTPUTS][BACNET_MAX_PRIORITY]; -/* The Progress_Value tracks changes such as ramp and fade */ -static uint8_t Lighting_Output_Progress[MAX_LIGHTING_OUTPUTS]; -/* The minimum and maximum present values are used for clamping */ -static uint8_t Lighting_Output_Min_Present_Value[MAX_LIGHTING_OUTPUTS]; -static uint8_t Lighting_Output_Max_Present_Value[MAX_LIGHTING_OUTPUTS]; -/* Writable out-of-service allows others to play with our Present Value */ -/* without changing the physical output */ -static bool Lighting_Output_Out_Of_Service[MAX_LIGHTING_OUTPUTS]; -/* the lighting command is what we are doing */ -static uint8_t Lighting_Command_Priority = 16; -static BACNET_LIGHTING_COMMAND Lighting_Command[MAX_LIGHTING_OUTPUTS]; +static const int Properties_Optional[] = { + -1 +}; -int Lighting_Output_Encode_Lighting_Command( - uint8_t * apdu, - BACNET_LIGHTING_COMMAND * data) +static const int 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 Lighting_Output_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) { - int apdu_len = 0; /* total length of the apdu, return value */ - int len = 0; /* total length of the apdu, return value */ - float real_value = 0.0; - uint32_t unsigned_value = 0; - - if (apdu) { - len = encode_context_enumerated(&apdu[apdu_len], 0, data->operation); - apdu_len += len; - /* optional level? */ - if (data->level != 255) { - real_value = data->level; - len = encode_context_real(&apdu[apdu_len], 1, real_value); - apdu_len += len; - } - /* optional ramp-rate */ - if (data->ramp_rate != 255) { - real_value = data->ramp_rate; - len = encode_context_real(&apdu[apdu_len], 2, real_value); - apdu_len += len; - } - /* optional step increment */ - if (data->step_increment != 255) { - real_value = data->step_increment; - len = encode_context_real(&apdu[apdu_len], 3, real_value); - apdu_len += len; - } - /* optional fade time */ - if (data->fade_time != 0) { - real_value = data->fade_time; - len = encode_context_real(&apdu[apdu_len], 4, real_value); - apdu_len += len; - } - /* optional duration */ - if (data->duration != 0) { - unsigned_value = data->duration; - len = encode_context_unsigned(&apdu[apdu_len], 5, unsigned_value); - apdu_len += len; - } - } - - return apdu_len; -} - -int Lighting_Output_Decode_Lighting_Command( - uint8_t * apdu, - unsigned apdu_max_len, - BACNET_LIGHTING_COMMAND * data) -{ - int len = 0; - int apdu_len = 0; - uint8_t tag_number = 0; - uint32_t len_value_type = 0; - float real_value = 0.0; - - apdu_max_len = apdu_max_len; - - /* check for value pointers */ - if (apdu_len && data) { - /* Tag 0: operation */ - if (!decode_is_context_tag(&apdu[apdu_len], 0)) - return -1; - len = - decode_tag_number_and_value(&apdu[apdu_len], &tag_number, - &len_value_type); - apdu_len += len; - len = - decode_enumerated(&apdu[apdu_len], len_value_type, - (uint32_t *) & data->operation); - apdu_len += len; - /* Tag 1: level - OPTIONAL */ - if (decode_is_context_tag(&apdu[apdu_len], 1)) { - len = - decode_tag_number_and_value(&apdu[apdu_len], &tag_number, - &len_value_type); - apdu_len += len; - len = decode_real(&apdu[apdu_len], &real_value); - apdu_len += len; - data->level = (uint8_t) real_value; - /* FIXME: are we going to flag errors in decoding values here? */ - } - /* FIXME: finish me! */ - /* Tag 2: */ - - } - - return len; -} - - -void Lighting_Output_Init( - void) -{ - unsigned i, j; - - /* initialize all the analog output priority arrays to NULL */ - for (i = 0; i < MAX_LIGHTING_OUTPUTS; i++) { - for (j = 0; j < BACNET_MAX_PRIORITY; j++) { - Lighting_Output_Level[i][j] = LIGHTING_LEVEL_NULL; - } - Lighting_Command[i].operation = BACNET_LIGHTS_STOP; - Lighting_Output_Out_Of_Service[i] = false; - Lighting_Output_Progress[i] = LIGHTING_RELINQUISH_DEFAULT; - Lighting_Output_Min_Present_Value[i] = 0; - Lighting_Output_Max_Present_Value[i] = 100; - } + if (pRequired) + *pRequired = Properties_Required; + if (pOptional) + *pOptional = Properties_Optional; + if (pProprietary) + *pProprietary = Properties_Proprietary; 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 Lighting 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 Lighting_Output_Valid_Instance( uint32_t object_instance) { - if (object_instance < MAX_LIGHTING_OUTPUTS) + unsigned int index; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { 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 Lighting Output objects + * + * @return Number of Lighting Output objects + */ unsigned Lighting_Output_Count( void) { return MAX_LIGHTING_OUTPUTS; } -/* 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 Lighting Output objects where N is Lighting_Output_Count(). + * + * @param index - 0..MAX_LIGHTING_OUTPUTS value + * + * @return object instance-number for the given index + */ uint32_t Lighting_Output_Index_To_Instance( unsigned index) { - return index; + uint32_t instance = 1; + + instance += index; + + return instance; } -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need to return the index */ -/* that correlates to the correct instance number */ +/** + * For a given object instance-number, determines a 0..N index + * of Lighting Output objects where N is Lighting_Output_Count(). + * + * @param object_instance - object-instance number of the object + * + * @return index for the given instance-number, or MAX_LIGHTING_OUTPUTS + * if not valid. + */ unsigned Lighting_Output_Instance_To_Index( uint32_t object_instance) { unsigned index = MAX_LIGHTING_OUTPUTS; - if (object_instance < MAX_LIGHTING_OUTPUTS) - index = object_instance; + if (object_instance) { + index = object_instance - 1; + if (index > MAX_LIGHTING_OUTPUTS) { + index = MAX_LIGHTING_OUTPUTS; + } + } return index; } +/** + * 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 Lighting_Output_Present_Value( uint32_t object_instance) { - float value = LIGHTING_RELINQUISH_DEFAULT; + float value = 0.0; unsigned index = 0; - unsigned i = 0; + unsigned p = 0; index = Lighting_Output_Instance_To_Index(object_instance); if (index < MAX_LIGHTING_OUTPUTS) { - for (i = 0; i < BACNET_MAX_PRIORITY; i++) { - if (Lighting_Output_Level[index][i] != LIGHTING_LEVEL_NULL) { - value = Lighting_Output_Level[index][i]; + value = Lighting_Output[index].Relinquish_Default; + for (p = 0; p < BACNET_MAX_PRIORITY; p++) { + if (BIT_CHECK(Lighting_Output[index].Priority_Active_Bits, p)) { + value = Lighting_Output[index].Priority_Array[p]; break; } } @@ -256,18 +221,81 @@ float Lighting_Output_Present_Value( return value; } +/** + * 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-value of the object + */ +static float Lighting_Output_Priority_Value( + uint32_t object_instance, + unsigned priority) +{ + float value = 0.0; + unsigned index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + value = Lighting_Output[index].Priority_Array[priority]; + } + } + + return value; +} + +/** + * 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 Lighting_Output_Priority_Active( + uint32_t object_instance, + unsigned priority) +{ + bool status = false; + unsigned index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + if (BIT_CHECK(Lighting_Output[index].Priority_Active_Bits, priority)) { + status = true; + } + } + } + + return status; +} + +/** + * 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 Lighting_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 */ index = Lighting_Output_Instance_To_Index(object_instance); if (index < MAX_LIGHTING_OUTPUTS) { - for (i = 0; i < BACNET_MAX_PRIORITY; i++) { - if (Lighting_Output_Level[index][i] != LIGHTING_LEVEL_NULL) { - priority = i + 1; + for (p = 0; p < BACNET_MAX_PRIORITY; p++) { + if (BIT_CHECK(Lighting_Output[index].Priority_Active_Bits, p)) { + priority = p + 1; break; } } @@ -276,6 +304,16 @@ unsigned Lighting_Output_Present_Value_Priority( 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 - floating point analog value + * @param priority - priority 1..16 + * + * @return true if values are within range and present-value is set. + */ bool Lighting_Output_Present_Value_Set( uint32_t object_instance, float value, @@ -287,15 +325,10 @@ bool Lighting_Output_Present_Value_Set( index = Lighting_Output_Instance_To_Index(object_instance); if (index < MAX_LIGHTING_OUTPUTS) { if (priority && (priority <= BACNET_MAX_PRIORITY) && - (priority != 6 /* reserved */ ) && - (value >= 0.0) && (value <= 100.0)) { - Lighting_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) */ + (priority != 6 /* reserved */ )) { + priority--; + BIT_SET(Lighting_Output[index].Priority_Active_Bits, priority); + Lighting_Output[index].Priority_Array[priority] = value; status = true; } } @@ -303,9 +336,18 @@ bool Lighting_Output_Present_Value_Set( 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 Lighting_Output_Present_Value_Relinquish( uint32_t object_instance, - int priority) + unsigned priority) { unsigned index = 0; bool status = false; @@ -314,13 +356,9 @@ bool Lighting_Output_Present_Value_Relinquish( if (index < MAX_LIGHTING_OUTPUTS) { if (priority && (priority <= BACNET_MAX_PRIORITY) && (priority != 6 /* reserved */ )) { - Lighting_Output_Level[index][priority - 1] = LIGHTING_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) */ + priority--; + BIT_CLEAR(Lighting_Output[index].Priority_Active_Bits, priority); + Lighting_Output[index].Priority_Array[priority] = 0.0; status = true; } } @@ -328,35 +366,589 @@ bool Lighting_Output_Present_Value_Relinquish( return status; } -float Lighting_Output_Tracking_Value( - uint32_t object_instance) +/** + * 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 Lighting_Output_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) { - float value = LIGHTING_RELINQUISH_DEFAULT; + char text_string[32] = ""; + bool status = false; unsigned index = 0; index = Lighting_Output_Instance_To_Index(object_instance); if (index < MAX_LIGHTING_OUTPUTS) { - value = Lighting_Output_Progress[index]; + sprintf(text_string, "LIGHTING OUTPUT %lu", + (unsigned long) object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/** + * For a given object instance-number, sets the lighting-command. + * + * @param object_instance - object-instance number of the object + * @param value - holds the lighting command value + * + * @return true if lighting command was set + */ +bool Lighting_Output_Lighting_Command_Set( + uint32_t object_instance, + BACNET_LIGHTING_COMMAND *value) +{ + bool status = false; + unsigned index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + // FIXME: check lighting command member values + status = lighting_command_copy( + &Lighting_Output[index].Lighting_Command, + value); + // FIXME: set all the other values, and get the light levels moving + } + + return status; +} + +/** + * For a given object instance-number, gets the lighting-command. + * + * @param object_instance - object-instance number of the object + * @param value - holds the lighting command value + * + * @return true if lighting command was retrieved + */ +bool Lighting_Output_Lighting_Command( + uint32_t object_instance, + BACNET_LIGHTING_COMMAND *value) +{ + bool status = false; + unsigned index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + status = lighting_command_copy(value, + &Lighting_Output[index].Lighting_Command); + } + + return status; +} + +/** + * For a given object instance-number, gets the in-progress property value + * + * @param object_instance - object-instance number of the object + * + * @return the in-progress value of this object instance. + */ +BACNET_LIGHTING_IN_PROGRESS Lighting_Output_In_Progress( + uint32_t object_instance) +{ + BACNET_LIGHTING_IN_PROGRESS value = BACNET_LIGHTING_IDLE; + unsigned index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + value = Lighting_Output[index].In_Progress; } return value; } -/* note: the object name must be unique within this device */ -char *Lighting_Output_Name( - uint32_t object_instance) +/** + * For a given object instance-number, sets the in-progress value of the + * object. + * + * @param object_instance - object-instance number of the object + * @param in_progress - holds the value to be set + * + * @return true if value was set + */ +bool Lighting_Output_In_Progress_Set( + uint32_t object_instance, + BACNET_LIGHTING_IN_PROGRESS in_progress) { - static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + unsigned index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + Lighting_Output[index].In_Progress = in_progress; - if (object_instance < MAX_LIGHTING_OUTPUTS) { - sprintf(text_string, "LIGHTING OUTPUT %u", object_instance); - return text_string; } - return NULL; + return status; } -/* return apdu len, or BACNET_STATUS_ERROR on error */ +/** + * For a given object instance-number, gets the tracking-value property + * + * @param object_instance - object-instance number of the object + * + * @return the tracking-value of this object instance. + */ +float Lighting_Output_Tracking_Value( + uint32_t object_instance) +{ + float value = 0.0; + unsigned index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + value = Lighting_Output[index].Tracking_Value; + } + + return value; +} + +/** + * For a given object instance-number, sets the in-progress value of the + * object. + * + * @param object_instance - object-instance number of the object + * @param in_progress - holds the value to be set + * + * @return true if value was set + */ +bool Lighting_Output_Tracking_Value_Set( + uint32_t object_instance, + float value) +{ + bool status = false; + unsigned int index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + Lighting_Output[index].Tracking_Value = value; + status = true; + } + + return status; +} + +/** + * For a given object instance-number, gets the blink-warn-enable + * property value + * + * @param object_instance - object-instance number of the object + * + * @return the blink-warn-enable property value of this object + */ +bool Lighting_Output_Blink_Warn_Enable( + uint32_t object_instance) +{ + bool value = false; + unsigned index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + value = Lighting_Output[index].Blink_Warn_Enable; + } + + return value; +} + +/** + * For a given object instance-number, sets the blink-warn-enable + * property value in the object. + * + * @param object_instance - object-instance number of the object + * @param enable - holds the value to be set + * + * @return true if value was set + */ +bool Lighting_Output_Blink_Warn_Enable_Set( + uint32_t object_instance, + bool enable) +{ + bool status = false; + unsigned int index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + Lighting_Output[index].Blink_Warn_Enable = enable; + status = true; + } + + return status; +} + +/** + * For a given object instance-number, gets the egress-time + * property value + * + * @param object_instance - object-instance number of the object + * + * @return the egress-time property value of this object + */ +uint32_t Lighting_Output_Egress_Time( + uint32_t object_instance) +{ + uint32_t value = 0; + unsigned int index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + value = Lighting_Output[index].Egress_Time; + } + + return value; +} + +/** + * For a given object instance-number, sets the egress-time + * property value of the object. + * + * @param object_instance - object-instance number of the object + * @param seconds - holds the value to be set + * + * @return true if value was set + */ +bool Lighting_Output_Egress_Time_Set( + uint32_t object_instance, + uint32_t seconds) +{ + bool status = false; + unsigned int index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + Lighting_Output[index].Egress_Time = seconds; + status = true; + } + + return status; +} + +/** + * For a given object instance-number, gets the egress-active + * property value + * + * @param object_instance - object-instance number of the object + * + * @return the egress-active property value of this object + */ +bool Lighting_Output_Egress_Active( + uint32_t object_instance) +{ + bool value = false; + unsigned int index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + value = Lighting_Output[index].Egress_Active; + } + + return value; +} + +/** + * For a given object instance-number, gets the fade-time + * property value + * + * @param object_instance - object-instance number of the object + * + * @return the fade-time property value of this object + */ +uint32_t Lighting_Output_Default_Fade_Time( + uint32_t object_instance) +{ + uint32_t value = 0; + unsigned int index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + value = Lighting_Output[index].Default_Fade_Time; + } + + return value; +} + +/** + * For a given object instance-number, sets the fade-time + * property value of the object. + * + * @param object_instance - object-instance number of the object + * @param milliseconds - holds the value to be set + * + * @return true if value was set + */ +bool Lighting_Output_Default_Fade_Time_Set( + uint32_t object_instance, + uint32_t milliseconds) +{ + bool status = false; + unsigned int index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if ((index < MAX_LIGHTING_OUTPUTS) && + (milliseconds >= 100) && + (milliseconds <= 86400000)) { + Lighting_Output[index].Default_Fade_Time = milliseconds; + status = true; + } + + return status; +} + +/** + * For a given object instance-number, gets the ramp-rate + * property value + * + * @param object_instance - object-instance number of the object + * + * @return the ramp-rate property value of this object + */ +float Lighting_Output_Default_Ramp_Rate( + uint32_t object_instance) +{ + float value = 0.0; + unsigned int index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + value = Lighting_Output[index].Default_Ramp_Rate; + } + + return value; +} + +/** + * For a given object instance-number, sets the ramp-rate value of the + * object. + * + * @param object_instance - object-instance number of the object + * @param percent_per_second - holds the value to be set + * + * @return true if value was set + */ +bool Lighting_Output_Default_Ramp_Rate_Set( + uint32_t object_instance, + float percent_per_second) +{ + bool status = false; + unsigned int index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if ((index < MAX_LIGHTING_OUTPUTS) && + (percent_per_second >= 0.1) && + (percent_per_second <= 100.0)) { + Lighting_Output[index].Default_Ramp_Rate = percent_per_second; + status = true; + } + + return status; +} + +/** + * For a given object instance-number, gets the default-step-increment + * property value + * + * @param object_instance - object-instance number of the object + * + * @return the default-step-increment property value of this object + */ +float Lighting_Output_Default_Step_Increment( + uint32_t object_instance) +{ + float value = 0.0; + unsigned int index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + value = Lighting_Output[index].Default_Step_Increment; + } + + return value; +} + +/** + * For a given object instance-number, sets the default-step-increment + * property value of the object. + * + * @param object_instance - object-instance number of the object + * @param step_increment - holds the value to be set + * + * @return true if value was set + */ +bool Lighting_Output_Default_Step_Increment_Set( + uint32_t object_instance, + float step_increment) +{ + bool status = false; + unsigned int index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if ((index < MAX_LIGHTING_OUTPUTS) && + (step_increment >= 0.1) && + (step_increment <= 100.0)) { + Lighting_Output[index].Default_Step_Increment = step_increment; + status = true; + } + + return status; +} + +/** + * For a given object instance-number, gets the + * lighting-command-default-priority + * property value + * + * @param object_instance - object-instance number of the object + * + * @return the lighting-command-default-priority property value of + * this object + */ +unsigned Lighting_Output_Default_Priority( + uint32_t object_instance) +{ + unsigned value = 0.0; + unsigned int index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + value = Lighting_Output[index].Lighting_Command_Default_Priority; + } + + return value; +} + +/** + * For a given object instance-number, sets the + * lighting-command-default-priority property value of the object. + * + * @param object_instance - object-instance number of the object + * @param priority - holds the value to be set + * + * @return true if value was set + */ +bool Lighting_Output_Default_Priority_Set( + uint32_t object_instance, + unsigned priority) +{ + bool status = false; + unsigned int index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if ((index < MAX_LIGHTING_OUTPUTS) && + (priority >= BACNET_MIN_PRIORITY) && + (priority <= BACNET_MAX_PRIORITY)) { + Lighting_Output[index].Lighting_Command_Default_Priority = priority; + status = true; + } + + return status; +} + +/** + * For a given object instance-number, returns the out-of-service + * property value + * + * @param object_instance - object-instance number of the object + * + * @return out-of-service property value + */ +bool Lighting_Output_Out_Of_Service( + uint32_t object_instance) +{ + bool value = false; + unsigned int index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + value = Lighting_Output[index].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 Lighting_Output_Out_Of_Service_Set( + uint32_t object_instance, + bool value) +{ + unsigned int index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + Lighting_Output[index].Out_Of_Service = value; + } +} + +/** + * 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 + */ +float Lighting_Output_Relinquish_Default( + uint32_t object_instance) +{ + float value = 0.0; + unsigned int index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + value = Lighting_Output[index].Relinquish_Default; + } + + 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 Lighting_Output_Relinquish_Default_Set( + uint32_t object_instance, + float value) +{ + bool status = false; + unsigned int index = 0; + + index = Lighting_Output_Instance_To_Index(object_instance); + if (index < MAX_LIGHTING_OUTPUTS) { + Lighting_Output[index].Relinquish_Default = value; + } + + 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 - ReadProperty data, including requested data and + * data for the reply, or error response. + * + * @return number of APDU bytes in the response, or + * BACNET_STATUS_ERROR on error. + */ int Lighting_Output_Read_Property( BACNET_READ_PROPERTY_DATA * rpdata) { @@ -364,8 +956,9 @@ int Lighting_Output_Read_Property( int apdu_len = 0; /* return value */ BACNET_BIT_STRING bit_string; BACNET_CHARACTER_STRING char_string; + BACNET_LIGHTING_COMMAND lighting_command; float real_value = (float) 1.414; - unsigned object_index = 0; + uint32_t unsigned_value = 0; unsigned i = 0; bool state = false; uint8_t *apdu = NULL; @@ -382,22 +975,16 @@ int Lighting_Output_Read_Property( rpdata->object_instance); break; case PROP_OBJECT_NAME: - case PROP_DESCRIPTION: - /* object name must be unique in this device. */ - /* FIXME: description could be writable and different than object name */ - characterstring_init_ansi(&char_string, - Lighting_Output_Name(rpdata->object_instance)); + Lighting_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_LIGHTING_OUTPUT); + encode_application_enumerated(&apdu[0], OBJECT_LIGHTING_OUTPUT); break; case PROP_PRESENT_VALUE: - real_value = - Lighting_Output_Present_Value(rpdata->object_instance); + real_value = Lighting_Output_Present_Value(rpdata->object_instance); apdu_len = encode_application_real(&apdu[0], real_value); break; case PROP_TRACKING_VALUE: @@ -406,30 +993,60 @@ int Lighting_Output_Read_Property( apdu_len = encode_application_real(&apdu[0], real_value); break; case PROP_LIGHTING_COMMAND: - apdu_len = - Lighting_Output_Encode_Lighting_Command(&apdu[0], - &Lighting_Command[rpdata->object_instance]); + Lighting_Output_Lighting_Command( + rpdata->object_instance, + &lighting_command); + apdu_len = lighting_command_encode(&apdu[0], + &lighting_command); + break; + case PROP_IN_PROGRESS: + unsigned_value = Lighting_Output_In_Progress( + rpdata->object_instance); + apdu_len = encode_application_enumerated(&apdu[0], + unsigned_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); - bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + state = Lighting_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_EVENT_STATE: - apdu_len = - encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); - break; case PROP_OUT_OF_SERVICE: - object_index = - Lighting_Output_Instance_To_Index(rpdata->object_instance); - state = Lighting_Output_Out_Of_Service[object_index]; + state = Lighting_Output_Out_Of_Service(rpdata->object_instance); apdu_len = encode_application_boolean(&apdu[0], state); break; - case PROP_UNITS: - apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); + case PROP_BLINK_WARN_ENABLE: + state = Lighting_Output_Blink_Warn_Enable(rpdata->object_instance); + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_EGRESS_TIME: + unsigned_value = Lighting_Output_Egress_Time( + rpdata->object_instance); + apdu_len = encode_application_unsigned(&apdu[0], + unsigned_value); + break; + case PROP_EGRESS_ACTIVE: + state = Lighting_Output_Egress_Active(rpdata->object_instance); + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_DEFAULT_FADE_TIME: + unsigned_value = Lighting_Output_Default_Fade_Time( + rpdata->object_instance); + apdu_len = encode_application_unsigned(&apdu[0], + unsigned_value); + break; + case PROP_DEFAULT_RAMP_RATE: + real_value = + Lighting_Output_Default_Ramp_Rate(rpdata->object_instance); + apdu_len = encode_application_real(&apdu[0], real_value); + break; + case PROP_DEFAULT_STEP_INCREMENT: + real_value = + Lighting_Output_Default_Step_Increment(rpdata->object_instance); + apdu_len = encode_application_real(&apdu[0], real_value); break; case PROP_PRIORITY_ARRAY: /* Array element zero is the number of elements in the array */ @@ -439,18 +1056,16 @@ int Lighting_Output_Read_Property( /* 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 = - Lighting_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 (Lighting_Output_Level[object_index][i] == - LIGHTING_LEVEL_NULL) - len = encode_application_null(&apdu[apdu_len]); - else { - real_value = Lighting_Output_Level[object_index][i]; + for (i = 1; i <= BACNET_MAX_PRIORITY; i++) { + if (Lighting_Output_Priority_Active( + rpdata->object_instance, i)) { + real_value = Lighting_Output_Priority_Value( + rpdata->object_instance, i); len = encode_application_real(&apdu[apdu_len], real_value); + } else { + len = encode_application_null(&apdu[apdu_len]); } /* add it if we have room */ if ((apdu_len + len) < MAX_APDU) @@ -463,17 +1078,18 @@ int Lighting_Output_Read_Property( } } } else { - object_index = - Lighting_Output_Instance_To_Index(rpdata->object_instance); if (rpdata->array_index <= BACNET_MAX_PRIORITY) { - if (Lighting_Output_Level[object_index][rpdata->array_index - - 1] == LIGHTING_LEVEL_NULL) - apdu_len = encode_application_null(&apdu[0]); - else { - real_value = Lighting_Output_Level[object_index] - [rpdata->array_index - 1]; - apdu_len = - encode_application_real(&apdu[0], real_value); + if (Lighting_Output_Priority_Active( + rpdata->object_instance, + rpdata->array_index)) { + real_value = Lighting_Output_Priority_Value( + rpdata->object_instance, + rpdata->array_index); + len = + encode_application_real(&apdu[apdu_len], + real_value); + } else { + len = encode_application_null(&apdu[apdu_len]); } } else { rpdata->error_class = ERROR_CLASS_PROPERTY; @@ -481,12 +1097,18 @@ int Lighting_Output_Read_Property( apdu_len = BACNET_STATUS_ERROR; } } - break; case PROP_RELINQUISH_DEFAULT: - real_value = LIGHTING_RELINQUISH_DEFAULT; + real_value = Lighting_Output_Relinquish_Default( + rpdata->object_instance); apdu_len = encode_application_real(&apdu[0], real_value); break; + case PROP_LIGHTING_COMMAND_DEFAULT_PRIORITY: + unsigned_value = Lighting_Output_Default_Priority( + rpdata->object_instance); + apdu_len = encode_application_unsigned(&apdu[0], + unsigned_value); + break; default: rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; @@ -504,13 +1126,19 @@ int Lighting_Output_Read_Property( 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 Lighting_Output_Write_Property( BACNET_WRITE_PROPERTY_DATA * wp_data) { bool status = false; /* return value */ - unsigned int object_index = 0; - uint8_t level = LIGHTING_LEVEL_NULL; int len = 0; BACNET_APPLICATION_DATA_VALUE value; @@ -525,6 +1153,13 @@ bool Lighting_Output_Write_Property( wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; return false; } + if ((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; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } switch (wp_data->object_property) { case PROP_PRESENT_VALUE: if (value.tag == BACNET_APPLICATION_TAG_REAL) { @@ -549,13 +1184,6 @@ bool Lighting_Output_Write_Property( WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL, &wp_data->error_class, &wp_data->error_code); if (status) { - level = LIGHTING_LEVEL_NULL; - object_index = - Lighting_Output_Instance_To_Index - (wp_data->object_instance); - status = - Lighting_Output_Present_Value_Relinquish - (wp_data->object_instance, 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 @@ -563,40 +1191,108 @@ bool Lighting_Output_Write_Property( will have returned false because of this */ 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; + } else { + status = Lighting_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; + } } } } break; case PROP_LIGHTING_COMMAND: - /* FIXME: error checking? */ - Lighting_Output_Decode_Lighting_Command(wp_data->application_data, - wp_data->application_data_len, - &Lighting_Command[wp_data->object_instance]); + if (value.tag == BACNET_APPLICATION_TAG_LIGHTING_COMMAND) { + status = Lighting_Output_Lighting_Command_Set( + wp_data->object_instance, + &value.type.Lighting_Command); + if (!status) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } break; case PROP_OUT_OF_SERVICE: status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, &wp_data->error_class, &wp_data->error_code); if (status) { - object_index = - Lighting_Output_Instance_To_Index - (wp_data->object_instance); - Lighting_Output_Out_Of_Service[object_index] = - value.type.Boolean; + Lighting_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_TRACKING_VALUE: + case PROP_IN_PROGRESS: + case PROP_STATUS_FLAGS: + case PROP_BLINK_WARN_ENABLE: + case PROP_EGRESS_TIME: + case PROP_EGRESS_ACTIVE: + case PROP_DEFAULT_FADE_TIME: + case PROP_DEFAULT_RAMP_RATE: + case PROP_DEFAULT_STEP_INCREMENT: + case PROP_PRIORITY_ARRAY: + case PROP_RELINQUISH_DEFAULT: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; default: wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; break; } return status; } +/** + * Initializes the Lighting Output object data + */ +void Lighting_Output_Init( + void) +{ + unsigned i, p; + + for (i = 0; i < MAX_LIGHTING_OUTPUTS; i++) { + Lighting_Output[i].Present_Value = 0.0; + Lighting_Output[i].Tracking_Value = 0.0; + Lighting_Output[i].Lighting_Command.operation = BACNET_LIGHTS_NONE; + Lighting_Output[i].Lighting_Command.use_target_level = false; + Lighting_Output[i].Lighting_Command.use_ramp_rate = false; + Lighting_Output[i].Lighting_Command.use_step_increment = false; + Lighting_Output[i].Lighting_Command.use_fade_time = false; + Lighting_Output[i].Lighting_Command.use_priority = false; + Lighting_Output[i].In_Progress = BACNET_LIGHTING_IDLE; + Lighting_Output[i].Out_Of_Service = false; + Lighting_Output[i].Blink_Warn_Enable = false; + Lighting_Output[i].Egress_Active = false; + Lighting_Output[i].Egress_Time = 0; + Lighting_Output[i].Default_Fade_Time = 100; + Lighting_Output[i].Default_Ramp_Rate = 100.0; + Lighting_Output[i].Default_Step_Increment = 1.0; + Lighting_Output[i].Transition = BACNET_LIGHTING_TRANSITION_IDLE; + Lighting_Output[i].Feedback_Value = 0.0; + for (p = 0; p < BACNET_MAX_PRIORITY; p++) { + Lighting_Output[i].Priority_Array[p] = 0.0; + BIT_CLEAR(Lighting_Output[i].Priority_Active_Bits, p); + } + Lighting_Output[i].Relinquish_Default = 0.0; + Lighting_Output[i].Power = 0.0; + Lighting_Output[i].Instantaneous_Power = 0.0; + Lighting_Output[i].Min_Actual_Value = 0.0; + Lighting_Output[i].Max_Actual_Value = 100.0; + Lighting_Output[i].Lighting_Command_Default_Priority = 16; + } + + return; +} #ifdef TEST #include diff --git a/bacnet-stack/demo/object/lo.h b/bacnet-stack/demo/object/lo.h index 523e2d06..7bbaa512 100644 --- a/bacnet-stack/demo/object/lo.h +++ b/bacnet-stack/demo/object/lo.h @@ -22,8 +22,8 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * *********************************************************************/ -#ifndef LO_H -#define LO_H +#ifndef LIGHTING_OUTPUT_H +#define LIGHTING_OUTPUT_H #include #include @@ -36,14 +36,21 @@ extern "C" { #endif /* __cplusplus */ - bool Analog_Output_Valid_Instance( + void Lighting_Output_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool Lighting_Output_Valid_Instance( uint32_t object_instance); unsigned Lighting_Output_Count( void); uint32_t Lighting_Output_Index_To_Instance( unsigned index); - char *Lighting_Output_Name( - uint32_t object_instance); + unsigned Lighting_Output_Instance_To_Index( + uint32_t instance); + bool Lighting_Output_Object_Instance_Add( + uint32_t instance); + float Lighting_Output_Present_Value( uint32_t object_instance); unsigned Lighting_Output_Present_Value_Priority( @@ -54,12 +61,106 @@ extern "C" { unsigned priority); bool Lighting_Output_Present_Value_Relinquish( uint32_t object_instance, - int priority); + unsigned priority); + + float Lighting_Output_Relinquish_Default( + uint32_t object_instance); + bool Lighting_Output_Relinquish_Default_Set( + uint32_t object_instance, + float value); + + bool Lighting_Output_Change_Of_Value( + uint32_t instance); + void Lighting_Output_Change_Of_Value_Clear( + uint32_t instance); + bool Lighting_Output_Encode_Value_List( + uint32_t object_instance, + BACNET_PROPERTY_VALUE * value_list); + float Lighting_Output_COV_Increment( + uint32_t instance); + void Lighting_Output_COV_Increment_Set( + uint32_t instance, + float value); + + bool Lighting_Output_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool Lighting_Output_Name_Set( + uint32_t object_instance, + char *new_name); + + char *Lighting_Output_Description( + uint32_t instance); + bool Lighting_Output_Description_Set( + uint32_t instance, + char *new_name); + + bool Lighting_Output_Out_Of_Service( + uint32_t instance); + void Lighting_Output_Out_Of_Service_Set( + uint32_t instance, + bool oos_flag); + + bool Lighting_Output_Lighting_Command_Set( + uint32_t object_instance, + BACNET_LIGHTING_COMMAND *value); + bool Lighting_Output_Lighting_Command( + uint32_t object_instance, + BACNET_LIGHTING_COMMAND *value); + + BACNET_LIGHTING_IN_PROGRESS Lighting_Output_In_Progress( + uint32_t object_instance); + bool Lighting_Output_In_Progress_Set( + uint32_t object_instance, + BACNET_LIGHTING_IN_PROGRESS in_progress); + + float Lighting_Output_Tracking_Value( + uint32_t object_instance); + bool Lighting_Output_Tracking_Value_Set( + uint32_t object_instance, + float value); + + bool Lighting_Output_Blink_Warn_Enable( + uint32_t object_instance); + bool Lighting_Output_Blink_Warn_Enable_Set( + uint32_t object_instance, + bool enable); + + uint32_t Lighting_Output_Egress_Time( + uint32_t object_instance); + bool Lighting_Output_Egress_Time_Set( + uint32_t object_instance, + uint32_t seconds); + + uint32_t Lighting_Output_Default_Fade_Time( + uint32_t object_instance); + bool Lighting_Output_Default_Fade_Time_Set( + uint32_t object_instance, + uint32_t milliseconds); + + float Lighting_Output_Default_Ramp_Rate( + uint32_t object_instance); + bool Lighting_Output_Default_Ramp_Rate_Set( + uint32_t object_instance, + float percent_per_second); + + float Lighting_Output_Default_Step_Increment( + uint32_t object_instance); + bool Lighting_Output_Default_Step_Increment_Set( + uint32_t object_instance, + float step_increment); + + unsigned Lighting_Output_Default_Priority( + uint32_t object_instance); + bool Lighting_Output_Default_Priority_Set( + uint32_t object_instance, + unsigned priority); + + void Lighting_Output_Init( + void); - /* ReadProperty service support */ int Lighting_Output_Read_Property( BACNET_READ_PROPERTY_DATA * rpdata); - /* WriteProperty service support */ bool Lighting_Output_Write_Property( BACNET_WRITE_PROPERTY_DATA * wp_data); diff --git a/bacnet-stack/demo/server/Makefile b/bacnet-stack/demo/server/Makefile index 2e26c759..8aeb706a 100644 --- a/bacnet-stack/demo/server/Makefile +++ b/bacnet-stack/demo/server/Makefile @@ -27,6 +27,8 @@ OBJECT_SRC = \ $(BACNET_OBJECT)/msv.c \ $(BACNET_OBJECT)/nc.c \ $(BACNET_OBJECT)/trendlog.c \ + $(BACNET_OBJECT)/channel.c \ + $(BACNET_OBJECT)/lo.c \ $(BACNET_OBJECT)/bacfile.c SRCS = ${SRC} ${OBJECT_SRC} diff --git a/bacnet-stack/include/bacapp.h b/bacnet-stack/include/bacapp.h index 351bbbd1..e621b408 100644 --- a/bacnet-stack/include/bacapp.h +++ b/bacnet-stack/include/bacapp.h @@ -30,6 +30,7 @@ #include "bacdef.h" #include "bacstr.h" #include "datetime.h" +#include "lighting.h" struct BACnet_Application_Data_Value; typedef struct BACnet_Application_Data_Value { @@ -73,6 +74,9 @@ typedef struct BACnet_Application_Data_Value { #endif #if defined (BACAPP_OBJECT_ID) BACNET_OBJECT_ID Object_Id; +#endif +#if defined (BACAPP_LIGHTING_COMMAND) + BACNET_LIGHTING_COMMAND Lighting_Command; #endif } type; /* simple linked list if needed */ diff --git a/bacnet-stack/include/bacdef.h b/bacnet-stack/include/bacdef.h index 444125d8..6574d957 100644 --- a/bacnet-stack/include/bacdef.h +++ b/bacnet-stack/include/bacdef.h @@ -40,7 +40,7 @@ /* Although this stack can implement a later revision, * sometimes another revision is desired */ #ifndef BACNET_PROTOCOL_REVISION -#define BACNET_PROTOCOL_REVISION 12 +#define BACNET_PROTOCOL_REVISION 14 #endif /* there are a few dependencies on the BACnet Protocol-Revision */ diff --git a/bacnet-stack/include/bacenum.h b/bacnet-stack/include/bacenum.h index d9ca25a9..9c17d662 100644 --- a/bacnet-stack/include/bacenum.h +++ b/bacnet-stack/include/bacenum.h @@ -399,7 +399,7 @@ typedef enum { PROP_DEFAULT_FADE_TIME = 374, PROP_DEFAULT_RAMP_RATE = 375, PROP_DEFAULT_STEP_INCREMENT = 376, - PROP_EGRESS_TIMER = 377, + PROP_EGRESS_TIME = 377, PROP_IN_PROGRESS = 378, PROP_INSTANTANEOUS_POWER = 379, PROP_LIGHTING_COMMAND = 380, @@ -1689,7 +1689,12 @@ typedef enum BACnetLightingOperation { BACNET_LIGHTS_WARN = 7, BACNET_LIGHTS_WARN_OFF = 8, BACNET_LIGHTS_WARN_RELINQUISH = 9, - BACNET_LIGHTS_STOP = 10 + BACNET_LIGHTS_STOP = 10, + /* Enumerated values 0-255 are reserved for definition by ASHRAE. + Enumerated values 256-65535 may be used by others subject to + the procedures and constraints described in Clause 23 */ + BACNET_LIGHTS_PROPRIETARY_FIRST = 256, + BACNET_LIGHTS_PROPRIETARY_LAST = 65535 } BACNET_LIGHTING_OPERATION; typedef enum BACnetLightingInProgress { @@ -1703,7 +1708,12 @@ typedef enum BACnetLightingInProgress { typedef enum BACnetLightingTransition { BACNET_LIGHTING_TRANSITION_IDLE = 0, BACNET_LIGHTING_TRANSITION_FADE = 1, - BACNET_LIGHTING_TRANSITION_RAMP = 2 + BACNET_LIGHTING_TRANSITION_RAMP = 2, + /* Enumerated values 0-63 are reserved for definition by ASHRAE. + Enumerated values 64-255 may be used by others subject to + the procedures and constraints described in Clause 23. */ + BACNET_LIGHTING_TRANSITION_PROPRIETARY_FIRST = 64, + BACNET_LIGHTING_TRANSITION_PROPRIETARY_LAST = 255 } BACNET_LIGHTING_TRANSITION; /* NOTE: BACNET_DAYS_OF_WEEK is different than BACNET_WEEKDAY */ diff --git a/bacnet-stack/include/bactext.h b/bacnet-stack/include/bactext.h index c4a78691..0987e216 100644 --- a/bacnet-stack/include/bactext.h +++ b/bacnet-stack/include/bactext.h @@ -111,6 +111,9 @@ extern "C" { const char *bactext_network_layer_msg_name( unsigned index); + const char *bactext_lighting_operation_name( + unsigned index); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/bacnet-stack/include/config.h b/bacnet-stack/include/config.h index a2d6a195..330e6d39 100644 --- a/bacnet-stack/include/config.h +++ b/bacnet-stack/include/config.h @@ -125,6 +125,7 @@ defined(BACAPP_ENUMERATED) || \ defined(BACAPP_DATE) || \ defined(BACAPP_TIME) || \ + defined(BACAPP_LIGHTING_COMMAND) || \ defined(BACAPP_OBJECT_ID)) #define BACAPP_ALL #endif @@ -143,6 +144,7 @@ #define BACAPP_DATE #define BACAPP_TIME #define BACAPP_OBJECT_ID +#define BACAPP_LIGHTING_COMMAND #endif /* diff --git a/bacnet-stack/include/lighting.h b/bacnet-stack/include/lighting.h new file mode 100644 index 00000000..e4abb8e2 --- /dev/null +++ b/bacnet-stack/include/lighting.h @@ -0,0 +1,82 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef LIGHTING_H +#define LIGHTING_H + +#include +#include +#include "bacenum.h" + +/* BACnetLightingCommand ::= SEQUENCE { + operation [0] BACnetLightingOperation, + target-level [1] REAL (0.0..100.0) OPTIONAL, + ramp-rate [2] REAL (0.1..100.0) OPTIONAL, + step-increment [3] REAL (0.1..100.0) OPTIONAL, + fade-time [4] Unsigned (100.. 86400000) OPTIONAL, + priority [5] Unsigned (1..16) OPTIONAL +} +-- Note that the combination of level, ramp-rate, step-increment, and fade-time fields is +-- dependent on the specific lighting operation. See Table 12-67. +*/ +typedef struct BACnetLightingCommand { + BACNET_LIGHTING_OPERATION operation; + /* fields are optional */ + bool use_target_level:1; + bool use_ramp_rate:1; + bool use_step_increment:1; + bool use_fade_time:1; + bool use_priority:1; + float target_level; + float ramp_rate; + float step_increment; + uint32_t fade_time; + uint8_t priority; +} BACNET_LIGHTING_COMMAND; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int lighting_command_encode( + uint8_t * apdu, + BACNET_LIGHTING_COMMAND * data); + int lighting_command_encode_context( + uint8_t * apdu, + uint8_t tag_number, + BACNET_LIGHTING_COMMAND * value); + int lighting_command_decode( + uint8_t * apdu, + unsigned apdu_max_len, + BACNET_LIGHTING_COMMAND * data); + bool lighting_command_copy( + BACNET_LIGHTING_COMMAND * dst, + BACNET_LIGHTING_COMMAND * src); + bool lighting_command_same( + BACNET_LIGHTING_COMMAND * dst, + BACNET_LIGHTING_COMMAND * src); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/lib/Makefile b/bacnet-stack/lib/Makefile index 5e0ca7f7..992999e0 100644 --- a/bacnet-stack/lib/Makefile +++ b/bacnet-stack/lib/Makefile @@ -72,6 +72,7 @@ CORE_SRC = \ $(BACNET_CORE)/get_alarm_sum.c \ $(BACNET_CORE)/readrange.c \ $(BACNET_CORE)/timestamp.c \ + $(BACNET_CORE)/lighting.c \ $(BACNET_CORE)/version.c HANDLER_SRC = \ diff --git a/bacnet-stack/src/bacapp.c b/bacnet-stack/src/bacapp.c index 804ed545..4e7cc812 100644 --- a/bacnet-stack/src/bacapp.c +++ b/bacnet-stack/src/bacapp.c @@ -46,6 +46,7 @@ #include "bactext.h" #include "datetime.h" #include "bacstr.h" +#include "lighting.h" /** @file bacapp.c Utilities for the BACnet_Application_Data_Value */ @@ -145,6 +146,13 @@ int bacapp_encode_application_data( (int) value->type.Object_Id.type, value->type.Object_Id.instance); break; +#endif +#if defined (BACAPP_LIGHTING_COMMAND) + case BACNET_APPLICATION_TAG_LIGHTING_COMMAND: + apdu_len = + lighting_command_encode(&apdu[0], + &value->type.Lighting_Command); + break; #endif default: break; @@ -258,6 +266,14 @@ int bacapp_decode_data( value->type.Object_Id.instance = instance; } break; +#endif +#if defined (BACAPP_LIGHTING_COMMAND) + case BACNET_APPLICATION_TAG_LIGHTING_COMMAND: + len = + lighting_command_decode( + &apdu[0], len_value_type, + &value->type.Lighting_Command); + break; #endif default: break; @@ -536,6 +552,14 @@ int bacapp_encode_context_data_value( (int) value->type.Object_Id.type, value->type.Object_Id.instance); break; +#endif +#if defined (BACAPP_LIGHTING_COMMAND) + case BACNET_APPLICATION_TAG_LIGHTING_COMMAND: + apdu_len = + lighting_command_encode_context( + &apdu[0], context_tag_number, + &value->type.Lighting_Command); + break; #endif default: break; @@ -879,6 +903,13 @@ bool bacapp_copy( dest_value->type.Object_Id.instance = src_value->type.Object_Id.instance; break; +#endif +#if defined (BACAPP_LIGHTING_COMMAND) + case BACNET_APPLICATION_TAG_LIGHTING_COMMAND: + status = lighting_command_copy( + &dest_value->type.Lighting_Command, + &src_value->type.Lighting_Command); + break; #endif default: status = false; @@ -1299,6 +1330,21 @@ int bacapp_snprintf_value( /* bytes were written. */ ret_val = str_len - rem_str_len; break; + case BACNET_APPLICATION_TAG_LIGHTING_COMMAND: + if (!append_str(&p_str, &rem_str_len, "(")) + break; + if (!append_str(&p_str, &rem_str_len, + bactext_lighting_operation_name(value->type. + Lighting_Command.operation))) { + break; + } + /* FIXME: add the Lighting Command optional values */ + if (!append_str(&p_str, &rem_str_len, ")")) + break; + /* If we get here, then everything is OK. Indicate how many */ + /* bytes were written. */ + ret_val = str_len - rem_str_len; + break; default: ret_val = 0; break; @@ -1466,6 +1512,9 @@ bool bacapp_parse_application_data( status = false; } break; + case BACNET_APPLICATION_TAG_LIGHTING_COMMAND: + /* FIXME: add parsing for lighting command */ + break; default: break; } @@ -1586,7 +1635,13 @@ bool bacapp_same_value( &test_value->type.Bit_String); break; #endif - +#if defined (BACAPP_LIGHTING_COMMAND) + case BACNET_APPLICATION_TAG_BIT_STRING: + status = lighting_command_same( + &value->type.Lighting_Command, + &test_value->type.Lighting_Command); + break; +#endif default: status = false; break; diff --git a/bacnet-stack/src/bactext.c b/bacnet-stack/src/bactext.c index 56884d3b..78c2aab6 100644 --- a/bacnet-stack/src/bactext.c +++ b/bacnet-stack/src/bactext.c @@ -1005,7 +1005,7 @@ INDTEXT_DATA bacnet_property_names[] = { , {PROP_DEFAULT_STEP_INCREMENT, "default-step-increment"} , - {PROP_EGRESS_TIMER, "egress-timer"} + {PROP_EGRESS_TIME, "egress-time"} , {PROP_IN_PROGRESS, "in-progress"} , @@ -2221,3 +2221,41 @@ const char *bactext_network_layer_msg_name( else return "Invalid Network Layer Message"; } + +INDTEXT_DATA bacnet_lighting_operation_names[] = { + {BACNET_LIGHTS_NONE, "none"} + , + {BACNET_LIGHTS_FADE_TO, "fade-to"} + , + {BACNET_LIGHTS_RAMP_TO, "ramp-to"} + , + {BACNET_LIGHTS_STEP_UP, "step-up"} + , + {BACNET_LIGHTS_STEP_DOWN, "step-down"} + , + {BACNET_LIGHTS_STEP_ON, "step-on"} + , + {BACNET_LIGHTS_STEP_OFF, "step-off"} + , + {BACNET_LIGHTS_WARN, "warn"} + , + {BACNET_LIGHTS_WARN_OFF, "warn-off"} + , + {BACNET_LIGHTS_WARN_RELINQUISH, "warn-relinquish"} + , + {BACNET_LIGHTS_STOP, "stop"} + , + {0, NULL} +}; + +const char *bactext_lighting_operation_name( + unsigned index) +{ + if (index < BACNET_LIGHTS_PROPRIETARY_FIRST) + return indtext_by_index_default(network_layer_msg_names, index, + ASHRAE_Reserved_String); + else if (index <= BACNET_LIGHTS_PROPRIETARY_LAST) + return Vendor_Proprietary_String; + else + return "Invalid BACnetLightingOperation"; +} diff --git a/bacnet-stack/src/lighting.c b/bacnet-stack/src/lighting.c new file mode 100644 index 00000000..4db9f3a2 --- /dev/null +++ b/bacnet-stack/src/lighting.c @@ -0,0 +1,343 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2013 Steve Karg + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include +#include +#include +#include "lighting.h" +#include "bacdcode.h" + +/** @file lighting.c Manipulate BACnet lighting command values */ + + +/** + * Encodes into bytes from the lighting-command structure + * + * @param apdu - buffer to hold the bytes + * @param value - lighting command value to encode + * + * @return number of bytes encoded, or 0 if unable to encode. + */ +int lighting_command_encode( + uint8_t * apdu, + BACNET_LIGHTING_COMMAND * data) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + int len = 0; /* total length of the apdu, return value */ + + if (apdu) { + len = encode_context_enumerated(&apdu[apdu_len], 0, + data->operation); + apdu_len += len; + /* optional target-level */ + if (data->use_target_level) { + len = encode_context_real(&apdu[apdu_len], 1, + data->target_level); + apdu_len += len; + } + /* optional ramp-rate */ + if (data->use_ramp_rate) { + len = encode_context_real(&apdu[apdu_len], 2, + data->ramp_rate); + apdu_len += len; + } + /* optional step increment */ + if (data->use_step_increment) { + len = encode_context_real(&apdu[apdu_len], 3, + data->step_increment); + apdu_len += len; + } + /* optional fade time */ + if (data->use_fade_time) { + len = encode_context_unsigned(&apdu[apdu_len], 4, + data->fade_time); + apdu_len += len; + } + /* optional priority */ + if (data->use_priority) { + len = encode_context_unsigned(&apdu[apdu_len], 5, + data->priority); + apdu_len += len; + } + } + + return apdu_len; +} + +/** + * Encodes into bytes from the lighting-command structure + * a context tagged chunk (opening and closing tag) + * + * @param apdu - buffer to hold the bytes + * @param tag_number - tag number to encode this chunk + * @param value - lighting command value to encode + * + * @return number of bytes encoded, or 0 if unable to encode. + */ +int lighting_command_encode_context( + uint8_t * apdu, + uint8_t tag_number, + BACNET_LIGHTING_COMMAND * value) +{ + int apdu_len = 0; + + apdu_len += encode_opening_tag(&apdu[apdu_len], tag_number); + apdu_len += lighting_command_encode(&apdu[apdu_len], value); + apdu_len += encode_closing_tag(&apdu[apdu_len], tag_number); + + return apdu_len; +} + +/** + * Decodes from bytes into the lighting-command structure + * + * @param apdu - buffer to hold the bytes + * @param apdu_max_len - number of bytes in the buffer to decode + * @param value - lighting command value to place the decoded values + * + * @return number of bytes encoded + */ +int lighting_command_decode( + uint8_t * apdu, + unsigned apdu_max_len, + BACNET_LIGHTING_COMMAND * data) +{ + int len = 0; + int apdu_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + uint32_t unsigned_value = 0; + float real_value = 0.0; + + apdu_max_len = apdu_max_len; + /* check for value pointers */ + if (apdu_len && data) { + /* Tag 0: operation */ + if (!decode_is_context_tag(&apdu[apdu_len], 0)) + return BACNET_STATUS_ERROR; + len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, + &len_value_type); + apdu_len += len; + len = + decode_enumerated(&apdu[apdu_len], len_value_type, &unsigned_value); + if (len > 0) { + data->operation = unsigned_value; + } + apdu_len += len; + /* Tag 1: target-level - OPTIONAL */ + if (decode_is_context_tag(&apdu[apdu_len], 1)) { + len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, + &len_value_type); + apdu_len += len; + len = decode_real(&apdu[apdu_len], &real_value); + data->target_level = real_value; + apdu_len += len; + data->use_target_level = true; + } else { + data->use_target_level = false; + } + /* Tag 2: ramp-rate - OPTIONAL */ + if (decode_is_context_tag(&apdu[apdu_len], 2)) { + len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, + &len_value_type); + apdu_len += len; + len = decode_real(&apdu[apdu_len], &real_value); + data->ramp_rate = real_value; + data->use_ramp_rate = true; + } else { + data->use_ramp_rate = false; + } + /* Tag 3: step-increment - OPTIONAL */ + if (decode_is_context_tag(&apdu[apdu_len], 3)) { + len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, + &len_value_type); + apdu_len += len; + len = decode_real(&apdu[apdu_len], &real_value); + data->step_increment = real_value; + data->use_step_increment = true; + } else { + data->use_step_increment = false; + } + /* Tag 4: fade-time - OPTIONAL */ + if (decode_is_context_tag(&apdu[apdu_len], 4)) { + len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, + &len_value_type); + apdu_len += len; + len = decode_unsigned(&apdu[apdu_len], len_value_type, + &unsigned_value); + data->fade_time = unsigned_value; + data->use_fade_time = true; + } else { + data->use_fade_time = false; + } + /* Tag 5: priority - OPTIONAL */ + if (decode_is_context_tag(&apdu[apdu_len], 4)) { + len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, + &len_value_type); + apdu_len += len; + len = decode_unsigned(&apdu[apdu_len], len_value_type, + &unsigned_value); + data->priority = unsigned_value; + data->use_priority = true; + } else { + data->use_priority = false; + } + } + + return len; +} + +/** + * Copies one lighting-command structure to another + * + * @param dst - lighting command value target + * @param src - lighting command value source + * + * @return true if copy succeeded + */ +bool lighting_command_copy( + BACNET_LIGHTING_COMMAND * dst, + BACNET_LIGHTING_COMMAND * src) +{ + bool status = false; + + if (dst && src) { + dst->operation = src->operation; + dst->use_target_level = src->use_target_level; + dst->use_ramp_rate = src->use_ramp_rate; + dst->use_step_increment = src->use_step_increment; + dst->use_fade_time = src->use_fade_time; + dst->use_priority = src->use_priority; + dst->target_level = src->target_level; + dst->ramp_rate = src->ramp_rate; + dst->step_increment = src->step_increment; + dst->fade_time = src->fade_time; + dst->priority = src->priority; + status = true; + } + + return status; +} + +/** + * Compare one lighting-command structure to another + * + * @param dst - lighting command value target + * @param src - lighting command value source + * + * @return true if lighting-commands are the same for values in-use + */ +bool lighting_command_same( + BACNET_LIGHTING_COMMAND * dst, + BACNET_LIGHTING_COMMAND * src) +{ + bool status = false; + + if (dst && src) { + if ((dst->operation == src->operation) && + (dst->use_target_level == src->use_target_level) && + (dst->use_ramp_rate == src->use_ramp_rate) && + (dst->use_step_increment == src->use_step_increment) && + (dst->use_fade_time == src->use_fade_time) && + (dst->use_priority = src->use_priority)) { + status = true; + if ((dst->use_target_level) && + (dst->target_level != src->target_level)) { + status = false; + } + if ((dst->use_ramp_rate) && + (dst->ramp_rate != src->ramp_rate)) { + status = false; + } + if ((dst->use_step_increment) && + (dst->step_increment != src->step_increment)) { + status = false; + } + if ((dst->use_fade_time) && + (dst->fade_time != src->fade_time)) { + status = false; + } + if ((dst->use_priority) && + (dst->priority != src->priority)) { + status = false; + } + } + } + + return status; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testBACnetLightingCommand( + Test * pTest) +{ + bool status = false; + + ct_test(pTest, status == true); +} + +#ifdef TEST_LIGHTING_COMMAND +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Lighting Command", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testBACnetLightingCommand); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} + +#endif +#endif /* TEST */