From a8280dda95661b4feb7f0e0aacbd724df35bbb56 Mon Sep 17 00:00:00 2001 From: skarg Date: Thu, 22 Mar 2012 18:22:42 +0000 Subject: [PATCH] Added CharacterString Value object example and unit tests, and added to demo/server example application. Added Multi-state Value object example and unit tests, and added to demo/server example application. Fixed Analog Input object, Analog Output object, Analog Value object, Device object, Multi-state Input object, Multi-state Output object examples to return correct error codes for WriteProperty service (per BACnet 135.1-2009-9.22.2.6). Fixed top level ReadProperty and WriteProperty handlers in Device object to return correct error codes (per BACnet 135.1-2009-9.22.2.5) --- bacnet-stack/demo/object/ai.c | 22 +- bacnet-stack/demo/object/ao.c | 14 +- bacnet-stack/demo/object/av.c | 17 +- bacnet-stack/demo/object/csv.c | 508 ++++++++++++++++++++++ bacnet-stack/demo/object/csv.h | 94 +++++ bacnet-stack/demo/object/csv.mak | 40 ++ bacnet-stack/demo/object/device.c | 52 ++- bacnet-stack/demo/object/ms-input.c | 77 +++- bacnet-stack/demo/object/ms-input.h | 9 +- bacnet-stack/demo/object/mso.c | 14 +- bacnet-stack/demo/object/msv.c | 628 ++++++++++++++++++++++++++++ bacnet-stack/demo/object/msv.h | 107 +++++ bacnet-stack/demo/object/msv.mak | 40 ++ bacnet-stack/include/wp.h | 7 +- bacnet-stack/lib/Makefile | 2 + bacnet-stack/lib/makefile.b32 | 4 +- bacnet-stack/src/bacapp.c | 22 +- bacnet-stack/src/bactext.c | 18 +- bacnet-stack/test.mak | 17 +- 19 files changed, 1637 insertions(+), 55 deletions(-) create mode 100644 bacnet-stack/demo/object/csv.c create mode 100644 bacnet-stack/demo/object/csv.h create mode 100644 bacnet-stack/demo/object/csv.mak create mode 100644 bacnet-stack/demo/object/msv.c create mode 100644 bacnet-stack/demo/object/msv.h create mode 100644 bacnet-stack/demo/object/msv.mak diff --git a/bacnet-stack/demo/object/ai.c b/bacnet-stack/demo/object/ai.c index f873a76f..653d2782 100644 --- a/bacnet-stack/demo/object/ai.c +++ b/bacnet-stack/demo/object/ai.c @@ -504,7 +504,7 @@ bool Analog_Input_Write_Property( else return false; - switch (wp_data->object_property) { + switch ((int)wp_data->object_property) { case PROP_PRESENT_VALUE: status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL, @@ -640,11 +640,27 @@ bool Analog_Input_Write_Property( } break; #endif - - default: + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_DESCRIPTION: + case PROP_RELIABILITY: +#if defined(INTRINSIC_REPORTING) + case PROP_ACKED_TRANSITIONS: + case PROP_EVENT_TIME_STAMPS: +#endif + case 9997: + case 9998: + case 9999: 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; diff --git a/bacnet-stack/demo/object/ao.c b/bacnet-stack/demo/object/ao.c index 5fc3abed..ce69f817 100644 --- a/bacnet-stack/demo/object/ao.c +++ b/bacnet-stack/demo/object/ao.c @@ -470,10 +470,22 @@ bool Analog_Output_Write_Property( value.type.Boolean; } break; - default: + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_UNITS: + case PROP_PRIORITY_ARRAY: + case PROP_RELINQUISH_DEFAULT: + case PROP_DESCRIPTION: 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; diff --git a/bacnet-stack/demo/object/av.c b/bacnet-stack/demo/object/av.c index a72ce4bc..017902f1 100644 --- a/bacnet-stack/demo/object/av.c +++ b/bacnet-stack/demo/object/av.c @@ -738,11 +738,24 @@ bool Analog_Value_Write_Property( } break; #endif - - default: + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_DESCRIPTION: + case PROP_PRIORITY_ARRAY: +#if defined(INTRINSIC_REPORTING) + case PROP_ACKED_TRANSITIONS: + case PROP_EVENT_TIME_STAMPS: +#endif wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; } return status; diff --git a/bacnet-stack/demo/object/csv.c b/bacnet-stack/demo/object/csv.c new file mode 100644 index 00000000..c50d602f --- /dev/null +++ b/bacnet-stack/demo/object/csv.c @@ -0,0 +1,508 @@ +/************************************************************************** +* +* 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. +* +*********************************************************************/ + +/* CharacterString Value Objects */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "rp.h" +#include "wp.h" +#include "csv.h" +#include "handlers.h" + +/* number of demo objects */ +#ifndef MAX_CHARACTERSTRING_VALUES +#define MAX_CHARACTERSTRING_VALUES 1 +#endif + +/* Here is our Present Value */ +static BACNET_CHARACTER_STRING Present_Value[MAX_CHARACTERSTRING_VALUES]; +/* Writable out-of-service allows others to manipulate our Present Value */ +static bool Out_Of_Service[MAX_CHARACTERSTRING_VALUES]; +static char Object_Name[MAX_CHARACTERSTRING_VALUES][64]; +static char Object_Description[MAX_CHARACTERSTRING_VALUES][64]; + +/* 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, + -1 +}; + +static const int Properties_Optional[] = { + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + -1 +}; + +static const int Properties_Proprietary[] = { + -1 +}; + +void CharacterString_Value_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; +} + +void CharacterString_Value_Init( + void) +{ + unsigned i; + + /* initialize all Present Values */ + for (i = 0; i < MAX_CHARACTERSTRING_VALUES; i++) { + characterstring_init_ansi(&Present_Value[i], ""); + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned CharacterString_Value_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_CHARACTERSTRING_VALUES; + + if (object_instance < MAX_CHARACTERSTRING_VALUES) { + index = object_instance; + } + + return index; +} + +/* 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 */ +uint32_t CharacterString_Value_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned CharacterString_Value_Count( + void) +{ + return MAX_CHARACTERSTRING_VALUES; +} + +bool CharacterString_Value_Valid_Instance( + uint32_t object_instance) +{ + unsigned index = 0; /* offset from instance lookup */ + + index = CharacterString_Value_Instance_To_Index(object_instance); + if (index < MAX_CHARACTERSTRING_VALUES) { + return true; + } + + return false; +} + +bool CharacterString_Value_Present_Value( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + bool status = false; + unsigned index = 0; /* offset from instance lookup */ + + index = CharacterString_Value_Instance_To_Index(object_instance); + if (object_name && (index < MAX_CHARACTERSTRING_VALUES)) { + status = characterstring_copy( + object_name, + &Present_Value[index]); + } + + return status; +} + +bool CharacterString_Value_Present_Value_Set( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + bool status = false; + unsigned index = 0; /* offset from instance lookup */ + + index = CharacterString_Value_Instance_To_Index(object_instance); + if (index < MAX_CHARACTERSTRING_VALUES) { + status = characterstring_copy( + &Present_Value[index], + object_name); + } + + return status; +} + +bool CharacterString_Value_Out_Of_Service( + uint32_t object_instance) +{ + bool value = false; + unsigned index = 0; + + index = CharacterString_Value_Instance_To_Index(object_instance); + if (index < MAX_CHARACTERSTRING_VALUES) { + value = Out_Of_Service[index]; + } + + return value; +} + +static void CharacterString_Value_Out_Of_Service_Set( + uint32_t object_instance, + bool value) +{ + unsigned index = 0; + + index = CharacterString_Value_Instance_To_Index(object_instance); + if (index < MAX_CHARACTERSTRING_VALUES) { + Out_Of_Service[index] = value; + } + + return; +} + +static char *CharacterString_Value_Description( + uint32_t object_instance) +{ + unsigned index = 0; /* offset from instance lookup */ + char *pName = NULL; /* return value */ + + index = CharacterString_Value_Instance_To_Index(object_instance); + if (index < MAX_CHARACTERSTRING_VALUES) { + pName = Object_Description[index]; + } + + return pName; +} + +bool CharacterString_Value_Description_Set( + uint32_t object_instance, + char *new_name) +{ + unsigned index = 0; /* offset from instance lookup */ + size_t i = 0; /* loop counter */ + bool status = false; /* return value */ + + index = CharacterString_Value_Instance_To_Index(object_instance); + if (index < MAX_CHARACTERSTRING_VALUES) { + status = true; + if (new_name) { + for (i = 0; i < sizeof(Object_Description[index]); i++) { + Object_Description[index][i] = new_name[i]; + if (new_name[i] == 0) { + break; + } + } + } else { + for (i = 0; i < sizeof(Object_Description[index]); i++) { + Object_Description[index][i] = 0; + } + } + } + + return status; +} + +bool CharacterString_Value_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + unsigned index = 0; /* offset from instance lookup */ + bool status = false; + + index = CharacterString_Value_Instance_To_Index(object_instance); + if (index < MAX_CHARACTERSTRING_VALUES) { + status = characterstring_init_ansi(object_name, Object_Name[index]); + } + + return status; +} + +/* note: the object name must be unique within this device */ +bool CharacterString_Value_Name_Set( + uint32_t object_instance, + char *new_name) +{ + unsigned index = 0; /* offset from instance lookup */ + size_t i = 0; /* loop counter */ + bool status = false; /* return value */ + + index = CharacterString_Value_Instance_To_Index(object_instance); + if (index < MAX_CHARACTERSTRING_VALUES) { + status = true; + /* FIXME: check to see if there is a matching name */ + if (new_name) { + for (i = 0; i < sizeof(Object_Name[index]); i++) { + Object_Name[index][i] = new_name[i]; + if (new_name[i] == 0) { + break; + } + } + } else { + for (i = 0; i < sizeof(Object_Name[index]); i++) { + Object_Name[index][i] = 0; + } + } + } + + return status; +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int CharacterString_Value_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + unsigned object_index = 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_CHARACTERSTRING_VALUE, rpdata->object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different */ + case PROP_OBJECT_NAME: + CharacterString_Value_Object_Name(rpdata->object_instance, + &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + CharacterString_Value_Description(rpdata->object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], + OBJECT_CHARACTERSTRING_VALUE); + break; + case PROP_PRESENT_VALUE: + CharacterString_Value_Present_Value(rpdata->object_instance, + &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + if (CharacterString_Value_Out_Of_Service(rpdata->object_instance)) { + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + true); + } else { + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + false); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + object_index = + CharacterString_Value_Instance_To_Index(rpdata->object_instance); + state = Out_Of_Service[object_index]; + apdu_len = encode_application_boolean(&apdu[0], state); + 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_STATE_TEXT) && + (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; +} + +/* returns true if successful */ +bool CharacterString_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* 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; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + status = + WPValidateArgType(&value, + BACNET_APPLICATION_TAG_CHARACTER_STRING, + &wp_data->error_class, &wp_data->error_code); + if (status) { + status = + CharacterString_Value_Present_Value_Set + (wp_data->object_instance, + &value.type.Character_String); + if (!status) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + CharacterString_Value_Out_Of_Service_Set( + wp_data->object_instance, value.type.Boolean); + } + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testCharacterStringValue( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint16_t decoded_type = 0; + uint32_t decoded_instance = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + CharacterString_Value_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_CHARACTERSTRING_VALUE; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = CharacterString_Value_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_CHARACTERSTRING_VALUE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet CharacterString Value", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testCharacterStringValue); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif +#endif /* TEST */ diff --git a/bacnet-stack/demo/object/csv.h b/bacnet-stack/demo/object/csv.h new file mode 100644 index 00000000..92587bb7 --- /dev/null +++ b/bacnet-stack/demo/object/csv.h @@ -0,0 +1,94 @@ +/************************************************************************** +* +* 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 CSV_H +#define CSV_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "rp.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void CharacterString_Value_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + + bool CharacterString_Value_Valid_Instance( + uint32_t object_instance); + unsigned CharacterString_Value_Count( + void); + uint32_t CharacterString_Value_Index_To_Instance( + unsigned index); + unsigned CharacterString_Value_Instance_To_Index( + uint32_t instance); + + int CharacterString_Value_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + + bool CharacterString_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + /* optional API */ + bool CharacterString_Value_Object_Instance_Add( + uint32_t instance); + + bool CharacterString_Value_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool CharacterString_Value_Name_Set( + uint32_t object_instance, + char *new_name); + + bool CharacterString_Value_Present_Value( + uint32_t object_instance, + BACNET_CHARACTER_STRING * value); + bool CharacterString_Value_Present_Value_Set( + uint32_t object_instance, + BACNET_CHARACTER_STRING * value); + bool CharacterString_Value_Description_Set( + uint32_t object_instance, + char *text_string); + + void CharacterString_Value_Init( + void); + + +#ifdef TEST +#include "ctest.h" + void testCharacterStringValue( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/bacnet-stack/demo/object/csv.mak b/bacnet-stack/demo/object/csv.mak new file mode 100644 index 00000000..107f469c --- /dev/null +++ b/bacnet-stack/demo/object/csv.mak @@ -0,0 +1,40 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_CHARACTERSTRING_VALUE + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = csv.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = characterstring_value + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/bacnet-stack/demo/object/device.c b/bacnet-stack/demo/object/device.c index 8096f9fa..b9eace06 100644 --- a/bacnet-stack/demo/object/device.c +++ b/bacnet-stack/demo/object/device.c @@ -54,10 +54,12 @@ #include "bi.h" #include "bo.h" #include "bv.h" +#include "csv.h" #include "lc.h" #include "lsp.h" -#include "mso.h" #include "ms-input.h" +#include "mso.h" +#include "msv.h" #include "trendlog.h" #if defined(INTRINSIC_REPORTING) #include "nc.h" @@ -162,6 +164,21 @@ static object_functions_t My_Object_Table[] = { NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */}, + {OBJECT_CHARACTERSTRING_VALUE, + CharacterString_Value_Init, + CharacterString_Value_Count, + CharacterString_Value_Index_To_Instance, + CharacterString_Value_Valid_Instance, + CharacterString_Value_Object_Name, + CharacterString_Value_Read_Property, + CharacterString_Value_Write_Property, + CharacterString_Value_Property_Lists, + NULL /* ReadRangeInfo */, + NULL /* Iterator */, + NULL /* Value_Lists */, + NULL /* COV */, + NULL /* COV Clear */, + NULL /* Intrinsic Reporting */}, #if defined(INTRINSIC_REPORTING) {OBJECT_NOTIFICATION_CLASS, Notification_Class_Init, @@ -209,6 +226,21 @@ static object_functions_t My_Object_Table[] = { NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */}, + {OBJECT_MULTI_STATE_INPUT, + Multistate_Input_Init, + Multistate_Input_Count, + Multistate_Input_Index_To_Instance, + Multistate_Input_Valid_Instance, + Multistate_Input_Object_Name, + Multistate_Input_Read_Property, + Multistate_Input_Write_Property, + Multistate_Input_Property_Lists, + NULL /* ReadRangeInfo */, + NULL /* Iterator */, + NULL /* Value_Lists */, + NULL /* COV */, + NULL /* COV Clear */, + NULL /* Intrinsic Reporting */}, {OBJECT_MULTI_STATE_OUTPUT, Multistate_Output_Init, Multistate_Output_Count, @@ -224,15 +256,15 @@ static object_functions_t My_Object_Table[] = { NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */}, - {OBJECT_MULTI_STATE_INPUT, - Multistate_Input_Init, - Multistate_Input_Count, - Multistate_Input_Index_To_Instance, - Multistate_Input_Valid_Instance, - Multistate_Input_Object_Name, - Multistate_Input_Read_Property, - Multistate_Input_Write_Property, - Multistate_Input_Property_Lists, + {OBJECT_MULTI_STATE_VALUE, + Multistate_Value_Init, + Multistate_Value_Count, + Multistate_Value_Index_To_Instance, + Multistate_Value_Valid_Instance, + Multistate_Value_Object_Name, + Multistate_Value_Read_Property, + Multistate_Value_Write_Property, + Multistate_Value_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, diff --git a/bacnet-stack/demo/object/ms-input.c b/bacnet-stack/demo/object/ms-input.c index 12f70239..cd146cf4 100644 --- a/bacnet-stack/demo/object/ms-input.c +++ b/bacnet-stack/demo/object/ms-input.c @@ -183,6 +183,34 @@ bool Multistate_Input_Present_Value_Set( return status; } +bool Multistate_Input_Out_Of_Service( + uint32_t object_instance) +{ + bool value = false; + unsigned index = 0; + + index = Multistate_Input_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_INPUTS) { + value = Out_Of_Service[index]; + } + + return value; +} + +void Multistate_Input_Out_Of_Service_Set( + uint32_t object_instance, + bool value) +{ + unsigned index = 0; + + index = Multistate_Input_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_INPUTS) { + Out_Of_Service[index] = value; + } + + return; +} + static char *Multistate_Input_Description( uint32_t object_instance) { @@ -329,8 +357,8 @@ int Multistate_Input_Read_Property( BACNET_BIT_STRING bit_string; BACNET_CHARACTER_STRING char_string; uint32_t present_value = 0; - unsigned object_index = 0; unsigned i = 0; + unsigned int object_index = 0; bool state = false; uint8_t *apdu = NULL; @@ -375,7 +403,13 @@ int Multistate_Input_Read_Property( 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); + if (Multistate_Input_Out_Of_Service(rpdata->object_instance)) { + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + true); + } else { + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + false); + } apdu_len = encode_application_bitstring(&apdu[0], &bit_string); break; case PROP_EVENT_STATE: @@ -384,9 +418,7 @@ int Multistate_Input_Read_Property( encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); break; case PROP_OUT_OF_SERVICE: - object_index = - Multistate_Input_Instance_To_Index(rpdata->object_instance); - state = Out_Of_Service[object_index]; + state = Multistate_Input_Out_Of_Service(rpdata->object_instance); apdu_len = encode_application_boolean(&apdu[0], state); break; case PROP_NUMBER_OF_STATES: @@ -485,18 +517,12 @@ bool Multistate_Input_Write_Property( WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, &wp_data->error_class, &wp_data->error_code); if (status) { - if (Out_Of_Service[object_index]) { - status = - Multistate_Input_Present_Value_Set - (wp_data->object_instance, value.type.Unsigned_Int); - if (!status) { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } - } else { - status = false; + status = + Multistate_Input_Present_Value_Set + (wp_data->object_instance, value.type.Unsigned_Int); + if (!status) { wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; } } break; @@ -505,15 +531,24 @@ bool Multistate_Input_Write_Property( WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, &wp_data->error_class, &wp_data->error_code); if (status) { - object_index = - Multistate_Input_Instance_To_Index - (wp_data->object_instance); - Out_Of_Service[object_index] = value.type.Boolean; + Multistate_Input_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_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_NUMBER_OF_STATES: + case PROP_DESCRIPTION: + case PROP_STATE_TEXT: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; default: wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; break; } diff --git a/bacnet-stack/demo/object/ms-input.h b/bacnet-stack/demo/object/ms-input.h index 9f71311b..85c7aeeb 100644 --- a/bacnet-stack/demo/object/ms-input.h +++ b/bacnet-stack/demo/object/ms-input.h @@ -72,6 +72,13 @@ extern "C" { bool Multistate_Input_Present_Value_Set( uint32_t object_instance, uint32_t value); + + bool Multistate_Input_Out_Of_Service( + uint32_t object_instance); + void Multistate_Input_Out_Of_Service_Set( + uint32_t object_instance, + bool value); + bool Multistate_Input_Description_Set( uint32_t object_instance, char *text_string); @@ -89,7 +96,7 @@ extern "C" { #ifdef TEST #include "ctest.h" - void testMultistateOutput( + void testMultistateInput( Test * pTest); #endif diff --git a/bacnet-stack/demo/object/mso.c b/bacnet-stack/demo/object/mso.c index 0ff12186..979f7a05 100644 --- a/bacnet-stack/demo/object/mso.c +++ b/bacnet-stack/demo/object/mso.c @@ -438,10 +438,22 @@ bool Multistate_Output_Write_Property( value.type.Boolean; } break; - default: + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_NUMBER_OF_STATES: + case PROP_DESCRIPTION: + case PROP_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_UNKNOWN_PROPERTY; + break; } return status; diff --git a/bacnet-stack/demo/object/msv.c b/bacnet-stack/demo/object/msv.c new file mode 100644 index 00000000..8e1d59e7 --- /dev/null +++ b/bacnet-stack/demo/object/msv.c @@ -0,0 +1,628 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Multi-state Value Objects */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "rp.h" +#include "wp.h" +#include "msv.h" +#include "handlers.h" + +/* number of demo objects */ +#ifndef MAX_MULTISTATE_VALUES +#define MAX_MULTISTATE_VALUES 1 +#endif + +/* how many states? 1 to 254 states - 0 is not allowed. */ +#ifndef MULTISTATE_NUMBER_OF_STATES +#define MULTISTATE_NUMBER_OF_STATES (254) +#endif + +/* Here is our Present Value */ +static uint8_t Present_Value[MAX_MULTISTATE_VALUES]; +/* Writable out-of-service allows others to manipulate our Present Value */ +static bool Out_Of_Service[MAX_MULTISTATE_VALUES]; +static char Object_Name[MAX_MULTISTATE_VALUES][64]; +static char Object_Description[MAX_MULTISTATE_VALUES][64]; +static char State_Text[MAX_MULTISTATE_VALUES][MULTISTATE_NUMBER_OF_STATES][64]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_NUMBER_OF_STATES, + -1 +}; + +static const int Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_STATE_TEXT, + -1 +}; + +static const int Properties_Proprietary[] = { + -1 +}; + +void Multistate_Value_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; +} + +void Multistate_Value_Init( + void) +{ + unsigned i; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_MULTISTATE_VALUES; i++) { + Present_Value[i] = 1; + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Multistate_Value_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_MULTISTATE_VALUES; + + if (object_instance < MAX_MULTISTATE_VALUES) + index = object_instance; + + return index; +} + +/* 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 */ +uint32_t Multistate_Value_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Multistate_Value_Count( + void) +{ + return MAX_MULTISTATE_VALUES; +} + +bool Multistate_Value_Valid_Instance( + uint32_t object_instance) +{ + unsigned index = 0; /* offset from instance lookup */ + + index = Multistate_Value_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_VALUES) { + return true; + } + + return false; +} + +uint32_t Multistate_Value_Present_Value( + uint32_t object_instance) +{ + uint32_t value = 1; + unsigned index = 0; /* offset from instance lookup */ + + index = Multistate_Value_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_VALUES) { + value = Present_Value[index]; + } + + return value; +} + +bool Multistate_Value_Present_Value_Set( + uint32_t object_instance, + uint32_t value) +{ + bool status = false; + unsigned index = 0; /* offset from instance lookup */ + + index = Multistate_Value_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_VALUES) { + if ((value > 0) && (value <= MULTISTATE_NUMBER_OF_STATES)) { + Present_Value[index] = (uint8_t) value; + status = true; + } + } + + return status; +} + +bool Multistate_Value_Out_Of_Service( + uint32_t object_instance) +{ + bool value = false; + unsigned index = 0; + + index = Multistate_Value_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_VALUES) { + value = Out_Of_Service[index]; + } + + return value; +} + +void Multistate_Value_Out_Of_Service_Set( + uint32_t object_instance, + bool value) +{ + unsigned index = 0; + + index = Multistate_Value_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_VALUES) { + Out_Of_Service[index] = value; + } + + return; +} + +static char *Multistate_Value_Description( + uint32_t object_instance) +{ + unsigned index = 0; /* offset from instance lookup */ + char *pName = NULL; /* return value */ + + index = Multistate_Value_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_VALUES) { + pName = Object_Description[index]; + } + + return pName; +} + +bool Multistate_Value_Description_Set( + uint32_t object_instance, + char *new_name) +{ + unsigned index = 0; /* offset from instance lookup */ + size_t i = 0; /* loop counter */ + bool status = false; /* return value */ + + index = Multistate_Value_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_VALUES) { + status = true; + if (new_name) { + for (i = 0; i < sizeof(Object_Description[index]); i++) { + Object_Description[index][i] = new_name[i]; + if (new_name[i] == 0) { + break; + } + } + } else { + for (i = 0; i < sizeof(Object_Description[index]); i++) { + Object_Description[index][i] = 0; + } + } + } + + return status; +} + +bool Multistate_Value_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + unsigned index = 0; /* offset from instance lookup */ + bool status = false; + + index = Multistate_Value_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_VALUES) { + status = characterstring_init_ansi(object_name, Object_Name[index]); + } + + return status; +} + +/* note: the object name must be unique within this device */ +bool Multistate_Value_Name_Set( + uint32_t object_instance, + char *new_name) +{ + unsigned index = 0; /* offset from instance lookup */ + size_t i = 0; /* loop counter */ + bool status = false; /* return value */ + + index = Multistate_Value_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_VALUES) { + status = true; + /* FIXME: check to see if there is a matching name */ + if (new_name) { + for (i = 0; i < sizeof(Object_Name[index]); i++) { + Object_Name[index][i] = new_name[i]; + if (new_name[i] == 0) { + break; + } + } + } else { + for (i = 0; i < sizeof(Object_Name[index]); i++) { + Object_Name[index][i] = 0; + } + } + } + + return status; +} + +static char *Multistate_Value_State_Text( + uint32_t object_instance, + uint32_t state_index) +{ + unsigned index = 0; /* offset from instance lookup */ + char *pName = NULL; /* return value */ + + index = Multistate_Value_Instance_To_Index(object_instance); + if ((index < MAX_MULTISTATE_VALUES) && (state_index > 0) && + (state_index <= MULTISTATE_NUMBER_OF_STATES)) { + state_index--; + pName = State_Text[index][state_index]; + } + + return pName; +} + +/* note: the object name must be unique within this device */ +bool Multistate_Value_State_Text_Set( + uint32_t object_instance, + uint32_t state_index, + char *new_name) +{ + unsigned index = 0; /* offset from instance lookup */ + size_t i = 0; /* loop counter */ + bool status = false; /* return value */ + + index = Multistate_Value_Instance_To_Index(object_instance); + if ((index < MAX_MULTISTATE_VALUES) && (state_index > 0) && + (state_index <= MULTISTATE_NUMBER_OF_STATES)) { + state_index--; + status = true; + if (new_name) { + for (i = 0; i < sizeof(State_Text[index][state_index]); i++) { + State_Text[index][state_index][i] = new_name[i]; + if (new_name[i] == 0) { + break; + } + } + } else { + for (i = 0; i < sizeof(State_Text[index][state_index]); i++) { + State_Text[index][state_index][i] = 0; + } + } + } + + return status;; +} + + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int Multistate_Value_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + uint32_t present_value = 0; + unsigned object_index = 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_MULTI_STATE_VALUE, rpdata->object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different */ + case PROP_OBJECT_NAME: + Multistate_Value_Object_Name(rpdata->object_instance, + &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Multistate_Value_Description(rpdata->object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], + OBJECT_MULTI_STATE_VALUE); + break; + case PROP_PRESENT_VALUE: + present_value = + Multistate_Value_Present_Value(rpdata->object_instance); + apdu_len = encode_application_unsigned(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + if (Multistate_Value_Out_Of_Service(rpdata->object_instance)) { + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + true); + } else { + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + false); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + object_index = + Multistate_Value_Instance_To_Index(rpdata->object_instance); + state = Out_Of_Service[object_index]; + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_NUMBER_OF_STATES: + apdu_len = + encode_application_unsigned(&apdu[apdu_len], + MULTISTATE_NUMBER_OF_STATES); + break; + case PROP_STATE_TEXT: + if (rpdata->array_index == 0) { + /* Array element zero is the number of elements in the array */ + apdu_len = + encode_application_unsigned(&apdu[0], + MULTISTATE_NUMBER_OF_STATES); + } else if (rpdata->array_index == BACNET_ARRAY_ALL) { + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + object_index = + Multistate_Value_Instance_To_Index + (rpdata->object_instance); + for (i = 1; i <= MULTISTATE_NUMBER_OF_STATES; i++) { + characterstring_init_ansi(&char_string, + Multistate_Value_State_Text(rpdata->object_instance, + i)); + /* FIXME: this might go beyond MAX_APDU length! */ + len = + encode_application_character_string(&apdu[apdu_len], + &char_string); + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) { + apdu_len += len; + } else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + object_index = + Multistate_Value_Instance_To_Index + (rpdata->object_instance); + if (rpdata->array_index <= MULTISTATE_NUMBER_OF_STATES) { + characterstring_init_ansi(&char_string, + Multistate_Value_State_Text(rpdata->object_instance, + rpdata->array_index)); + apdu_len = + encode_application_character_string(&apdu[0], + &char_string); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + 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_STATE_TEXT) && + (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; +} + +/* returns true if successful */ +bool Multistate_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* 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; + } + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + status = + Multistate_Value_Present_Value_Set + (wp_data->object_instance, value.type.Unsigned_Int); + if (!status) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Multistate_Value_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_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_NUMBER_OF_STATES: + case PROP_DESCRIPTION: + case PROP_STATE_TEXT: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testMultistateInput( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint16_t decoded_type = 0; + uint32_t decoded_instance = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + Multistate_Value_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_MULTI_STATE_VALUE; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Multistate_Value_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_MULTISTATE_VALUE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Multi-state Input", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testMultistateInput); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif +#endif /* TEST */ diff --git a/bacnet-stack/demo/object/msv.h b/bacnet-stack/demo/object/msv.h new file mode 100644 index 00000000..28a1f8cb --- /dev/null +++ b/bacnet-stack/demo/object/msv.h @@ -0,0 +1,107 @@ +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef MULTISTATE_VALUE_H +#define MULTISTATE_VALUE_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "rp.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void Multistate_Value_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + + bool Multistate_Value_Valid_Instance( + uint32_t object_instance); + unsigned Multistate_Value_Count( + void); + uint32_t Multistate_Value_Index_To_Instance( + unsigned index); + unsigned Multistate_Value_Instance_To_Index( + uint32_t instance); + + int Multistate_Value_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + + bool Multistate_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + /* optional API */ + bool Multistate_Value_Object_Instance_Add( + uint32_t instance); + + bool Multistate_Value_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool Multistate_Value_Name_Set( + uint32_t object_instance, + char *new_name); + + uint32_t Multistate_Value_Present_Value( + uint32_t object_instance); + bool Multistate_Value_Present_Value_Set( + uint32_t object_instance, + uint32_t value); + + bool Multistate_Value_Out_Of_Service( + uint32_t object_instance); + void Multistate_Value_Out_Of_Service_Set( + uint32_t object_instance, + bool value); + + bool Multistate_Value_Description_Set( + uint32_t object_instance, + char *text_string); + bool Multistate_Value_State_Text_Set( + uint32_t object_instance, + uint32_t state_index, + char *new_name); + bool Multistate_Value_Max_States_Set( + uint32_t instance, + uint32_t max_states_requested); + + void Multistate_Value_Init( + void); + + +#ifdef TEST +#include "ctest.h" + void testMultistateValue( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/bacnet-stack/demo/object/msv.mak b/bacnet-stack/demo/object/msv.mak new file mode 100644 index 00000000..fe0df4ba --- /dev/null +++ b/bacnet-stack/demo/object/msv.mak @@ -0,0 +1,40 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_MULTISTATE_VALUE + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = msv.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = multistate_value + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/bacnet-stack/include/wp.h b/bacnet-stack/include/wp.h index 9fdcdd2c..32ff1d14 100644 --- a/bacnet-stack/include/wp.h +++ b/bacnet-stack/include/wp.h @@ -93,10 +93,11 @@ extern "C" { uint8_t * invoke_id, BACNET_WRITE_PROPERTY_DATA * wp_data); - void test_ReadProperty( - Test * pTest); - void test_ReadPropertyAck( + void testWriteProperty( Test * pTest); + void testWritePropertyTag( + Test * pTest, + BACNET_APPLICATION_DATA_VALUE * value); #endif #ifdef __cplusplus diff --git a/bacnet-stack/lib/Makefile b/bacnet-stack/lib/Makefile index d04f53c4..1f104d8d 100644 --- a/bacnet-stack/lib/Makefile +++ b/bacnet-stack/lib/Makefile @@ -120,10 +120,12 @@ OBJECT_SRC = \ $(BACNET_OBJECT)/bi.c \ $(BACNET_OBJECT)/bo.c \ $(BACNET_OBJECT)/bv.c \ + $(BACNET_OBJECT)/csv.c \ $(BACNET_OBJECT)/lc.c \ $(BACNET_OBJECT)/lsp.c \ $(BACNET_OBJECT)/ms-input.c \ $(BACNET_OBJECT)/mso.c \ + $(BACNET_OBJECT)/msv.c \ $(BACNET_OBJECT)/nc.c \ $(BACNET_OBJECT)/trendlog.c \ $(BACNET_OBJECT)/bacfile.c diff --git a/bacnet-stack/lib/makefile.b32 b/bacnet-stack/lib/makefile.b32 index 16dfb3ce..90711d4a 100644 --- a/bacnet-stack/lib/makefile.b32 +++ b/bacnet-stack/lib/makefile.b32 @@ -139,10 +139,12 @@ OBJECT_SRC = $(BACNET_OBJECT)\device.c \ $(BACNET_OBJECT)\bi.c \ $(BACNET_OBJECT)\bo.c \ $(BACNET_OBJECT)\bv.c \ + $(BACNET_OBJECT)\csv.c \ $(BACNET_OBJECT)\lc.c \ $(BACNET_OBJECT)\lsp.c \ - $(BACNET_OBJECT)\mso.c \ $(BACNET_OBJECT)\ms-input.c \ + $(BACNET_OBJECT)\mso.c \ + $(BACNET_OBJECT)\msv.c \ $(BACNET_OBJECT)\trendlog.c \ $(BACNET_OBJECT)\bacfile.c diff --git a/bacnet-stack/src/bacapp.c b/bacnet-stack/src/bacapp.c index a6ffac89..fde2c9c3 100644 --- a/bacnet-stack/src/bacapp.c +++ b/bacnet-stack/src/bacapp.c @@ -648,7 +648,25 @@ BACNET_APPLICATION_TAG bacapp_context_tag_type( break; } break; - + case PROP_ACTIVE_COV_SUBSCRIPTIONS: + /* BACnetCOVSubscription */ + switch (tag_number) { + case 0: /* BACnetRecipientProcess */ + case 1: /* BACnetObjectPropertyReference */ + break; + case 2: /* issueConfirmedNotifications */ + tag = BACNET_APPLICATION_TAG_BOOLEAN; + break; + case 3: /* timeRemaining */ + tag = BACNET_APPLICATION_TAG_UNSIGNED_INT; + break; + case 4: /* covIncrement */ + tag = BACNET_APPLICATION_TAG_REAL; + break; + default: + break; + } + break; default: break; } @@ -717,7 +735,7 @@ int bacapp_decode_context_data( } } else if ( tag_len == 1 ) /* and is a Closing tag */ - apdu_len = 0; /* Don't advance over that closing tag. */ + apdu_len = 0; /* Don't advance over that closing tag. */ } return apdu_len; diff --git a/bacnet-stack/src/bactext.c b/bacnet-stack/src/bactext.c index 3ccf3e9b..9483f05d 100644 --- a/bacnet-stack/src/bactext.c +++ b/bacnet-stack/src/bactext.c @@ -187,9 +187,9 @@ INDTEXT_DATA bacnet_object_type_names[] = { , {OBJECT_LOOP, "Loop"} , - {OBJECT_MULTI_STATE_INPUT, "Multi-State Input"} + {OBJECT_MULTI_STATE_INPUT, "Multi-state Input"} , - {OBJECT_MULTI_STATE_OUTPUT, "Multi-State Output"} + {OBJECT_MULTI_STATE_OUTPUT, "Multi-state Output"} , {OBJECT_NOTIFICATION_CLASS, "Notification Class"} , @@ -199,9 +199,9 @@ INDTEXT_DATA bacnet_object_type_names[] = { , {OBJECT_AVERAGING, "Averaging"} , - {OBJECT_MULTI_STATE_VALUE, "Multi-State Value"} + {OBJECT_MULTI_STATE_VALUE, "Multi-state Value"} , - {OBJECT_TRENDLOG, "Trendlog"} + {OBJECT_TRENDLOG, "TrendLog"} , {OBJECT_LIFE_SAFETY_POINT, "Life Safety Point"} , @@ -239,23 +239,23 @@ INDTEXT_DATA bacnet_object_type_names[] = { , {OBJECT_NETWORK_SECURITY, "Network Security"} , - {OBJECT_BITSTRING_VALUE, "Bitstring Value"} + {OBJECT_BITSTRING_VALUE, "BitString Value"} , - {OBJECT_CHARACTERSTRING_VALUE, "Characterstring Value"} + {OBJECT_CHARACTERSTRING_VALUE, "CharacterString Value"} , {OBJECT_DATE_PATTERN_VALUE, "Date Pattern Value"} , {OBJECT_DATE_VALUE, "Date Value"} , - {OBJECT_DATETIME_PATTERN_VALUE, "Datetime Pattern Value"} + {OBJECT_DATETIME_PATTERN_VALUE, "DateTime Pattern Value"} , - {OBJECT_DATETIME_VALUE, "Datetime Value"} + {OBJECT_DATETIME_VALUE, "DateTime Value"} , {OBJECT_INTEGER_VALUE, "Integer Value"} , {OBJECT_LARGE_ANALOG_VALUE, "Large Analog Value"} , - {OBJECT_OCTETSTRING_VALUE, "Octetstring Value"} + {OBJECT_OCTETSTRING_VALUE, "OctetString Value"} , {OBJECT_POSITIVE_INTEGER_VALUE, "Positive Integer Value"} , diff --git a/bacnet-stack/test.mak b/bacnet-stack/test.mak index 0e9a1acd..374aa71e 100644 --- a/bacnet-stack/test.mak +++ b/bacnet-stack/test.mak @@ -187,7 +187,7 @@ wp: logfile test/wp.mak ( ./test/wp >> ${LOGFILE} ) make -C test -f wp.mak clean -objects: ai ao av bi bo bv lc lo lso lsp mso +objects: ai ao av bi bo bv csv lc lo lso lsp mso msv ms-input ai: logfile demo/object/ai.mak make -C demo/object -f ai.mak clean all @@ -218,6 +218,11 @@ bv: logfile demo/object/bv.mak ( ./demo/object/binary_value >> ${LOGFILE} ) make -C demo/object -f bv.mak clean +csv: logfile demo/object/csv.mak + make -C demo/object -f csv.mak clean all + ( ./demo/object/characterstring_value >> ${LOGFILE} ) + make -C demo/object -f csv.mak clean + device: logfile demo/object/device.mak make -C demo/object -f device.mak clean all ( ./demo/object/device >> ${LOGFILE} ) @@ -243,7 +248,17 @@ lsp: logfile demo/object/lsp.mak ( ./demo/object/life_safety_point >> ${LOGFILE} ) make -C demo/object -f lsp.mak clean +ms-input: logfile demo/object/ms-input.mak + make -C demo/object -f ms-input.mak clean all + ( ./demo/object/multistate_input >> ${LOGFILE} ) + make -C demo/object -f ms-input.mak clean + mso: logfile demo/object/mso.mak make -C demo/object -f mso.mak clean all ( ./demo/object/multistate_output >> ${LOGFILE} ) make -C demo/object -f mso.mak clean + +msv: logfile demo/object/msv.mak + make -C demo/object -f msv.mak clean all + ( ./demo/object/multistate_value >> ${LOGFILE} ) + make -C demo/object -f msv.mak clean