From e242b30a47244aa0f08dbf5e51558cbb0d790b19 Mon Sep 17 00:00:00 2001 From: skarg Date: Wed, 4 Nov 2015 17:35:03 +0000 Subject: [PATCH] =?UTF-8?q?Added=20example=20octet-string=20value=20object?= =?UTF-8?q?.=20Thank=20you,=20Nikola=20Jeli=C4=87!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bacnet-stack/demo/object/osv.c | 447 +++++++++++++++++++++++++++++++++ bacnet-stack/demo/object/osv.h | 113 +++++++++ 2 files changed, 560 insertions(+) create mode 100644 bacnet-stack/demo/object/osv.c create mode 100644 bacnet-stack/demo/object/osv.h diff --git a/bacnet-stack/demo/object/osv.c b/bacnet-stack/demo/object/osv.c new file mode 100644 index 00000000..4d997d76 --- /dev/null +++ b/bacnet-stack/demo/object/osv.c @@ -0,0 +1,447 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* 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. +* +*********************************************************************/ + +/* Octetstring Value Objects - customize for your use */ + +#include +#include +#include +#include + +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "bactext.h" +#include "config.h" /* the custom stuff */ +#include "device.h" +#include "handlers.h" +#include "osv.h" + + +#ifndef MAX_OCTETSTRING_VALUES +#define MAX_OCTETSTRING_VALUES 4 +#endif + +OCTETSTRING_VALUE_DESCR AV_Descr[MAX_OCTETSTRING_VALUES]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Octetstring_Value_Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + -1 +}; + +static const int Octetstring_Value_Properties_Optional[] = { + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_DESCRIPTION, + -1 +}; + +static const int Octetstring_Value_Properties_Proprietary[] = { + -1 +}; + +void Octetstring_Value_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Octetstring_Value_Properties_Required; + if (pOptional) + *pOptional = Octetstring_Value_Properties_Optional; + if (pProprietary) + *pProprietary = Octetstring_Value_Properties_Proprietary; + + return; +} + +void Octetstring_Value_Init( + void) +{ + unsigned i; + + for (i = 0; i < MAX_OCTETSTRING_VALUES; i++) { + memset(&AV_Descr[i], 0x00, sizeof(OCTETSTRING_VALUE_DESCR)); + octetstring_init (&AV_Descr[i].Present_Value, NULL, 0); + } +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Octetstring_Value_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_OCTETSTRING_VALUES) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Octetstring_Value_Count( + void) +{ + return MAX_OCTETSTRING_VALUES; +} + +/* 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 Octetstring_Value_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Octetstring_Value_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_OCTETSTRING_VALUES; + + if (object_instance < MAX_OCTETSTRING_VALUES) + index = object_instance; + + return index; +} + +/** + * 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 - octetstring value + * @param priority - priority 1..16 + * + * @return true if values are within range and present-value is set. + */ +bool Octetstring_Value_Present_Value_Set( + uint32_t object_instance, + BACNET_OCTET_STRING * value, + uint8_t priority) +{ + unsigned index = 0; + bool status = false; + + index = Octetstring_Value_Instance_To_Index(object_instance); + if (index < MAX_OCTETSTRING_VALUES) { + octetstring_copy (&AV_Descr[index].Present_Value, value); + status = true; + } + return status; +} + +BACNET_OCTET_STRING * Octetstring_Value_Present_Value( + uint32_t object_instance) +{ + BACNET_OCTET_STRING * value = NULL; + unsigned index = 0; + + index = Octetstring_Value_Instance_To_Index(object_instance); + if (index < MAX_OCTETSTRING_VALUES) { + value = &AV_Descr[index].Present_Value; + } + + return value; +} + +/* note: the object name must be unique within this device */ +bool Octetstring_Value_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_OCTETSTRING_VALUES) { + sprintf(text_string, "OCTETSTRING VALUE %lu", + (unsigned long) object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int Octetstring_Value_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + BACNET_OCTET_STRING * real_value = NULL; + unsigned object_index = 0; + bool state = false; + uint8_t *apdu = NULL; + OCTETSTRING_VALUE_DESCR *CurrentAV; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + + apdu = rpdata->application_data; + + object_index = Octetstring_Value_Instance_To_Index(rpdata->object_instance); + if (object_index < MAX_OCTETSTRING_VALUES) + CurrentAV = &AV_Descr[object_index]; + else + return BACNET_STATUS_ERROR; + + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_OCTETSTRING_VALUE, + rpdata->object_instance); + break; + + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + Octetstring_Value_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_OCTETSTRING_VALUE); + break; + + case PROP_PRESENT_VALUE: + real_value = Octetstring_Value_Present_Value(rpdata->object_instance); + apdu_len = encode_application_octet_string(&apdu[0], real_value); + break; + + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + CurrentAV->Out_Of_Service); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + + case PROP_OUT_OF_SERVICE: + state = CurrentAV->Out_Of_Service; + 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_PRIORITY_ARRAY) && + (rpdata->object_property != PROP_EVENT_TIME_STAMPS) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Octetstring_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + OCTETSTRING_VALUE_DESCR *CurrentAV; + + /* 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->object_property != PROP_EVENT_TIME_STAMPS) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + object_index = Octetstring_Value_Instance_To_Index(wp_data->object_instance); + if (object_index < MAX_OCTETSTRING_VALUES) + CurrentAV = &AV_Descr[object_index]; + else + return false; + + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_OCTET_STRING) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (Octetstring_Value_Present_Value_Set(wp_data->object_instance, + &value.type.Octet_String, wp_data->priority)) { + status = true; + } else if (wp_data->priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + status = false; + 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) { + CurrentAV->Out_Of_Service = 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_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; +} + + +void Octetstring_Value_Intrinsic_Reporting( + uint32_t object_instance) +{ +} + +#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 testOctetstring_Value( + Test * pTest) +{ + BACNET_READ_PROPERTY_DATA rpdata; + 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; + + Octetstring_Value_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_OCTETSTRING_VALUE; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Octetstring_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_OCTETSTRING_VALUE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Octetstring Value", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testOctetstring_Value); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_OCTETSTRING_VALUE */ +#endif /* TEST */ diff --git a/bacnet-stack/demo/object/osv.h b/bacnet-stack/demo/object/osv.h new file mode 100644 index 00000000..50b6e198 --- /dev/null +++ b/bacnet-stack/demo/object/osv.h @@ -0,0 +1,113 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* 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 OSV_H +#define OSV_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "wp.h" +#include "rp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + typedef struct octetstring_value_descr { + unsigned Event_State:3; + bool Out_Of_Service; + BACNET_OCTET_STRING Present_Value; + } OCTETSTRING_VALUE_DESCR; + + + void Octetstring_Value_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool Octetstring_Value_Valid_Instance( + uint32_t object_instance); + unsigned Octetstring_Value_Count( + void); + uint32_t Octetstring_Value_Index_To_Instance( + unsigned index); + unsigned Octetstring_Value_Instance_To_Index( + uint32_t object_instance); + + bool Octetstring_Value_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + + int Octetstring_Value_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + + bool Octetstring_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + bool Octetstring_Value_Present_Value_Set( + uint32_t object_instance, + BACNET_OCTET_STRING * value, + uint8_t priority); + BACNET_OCTET_STRING * Octetstring_Value_Present_Value( + uint32_t object_instance); + + bool Octetstring_Value_Change_Of_Value( + uint32_t instance); + void Octetstring_Value_Change_Of_Value_Clear( + uint32_t instance); + bool Octetstring_Value_Encode_Value_List( + uint32_t object_instance, + BACNET_PROPERTY_VALUE * value_list); + + char *Octetstring_Value_Description( + uint32_t instance); + bool Octetstring_Value_Description_Set( + uint32_t instance, + char *new_name); + + bool Octetstring_Value_Out_Of_Service( + uint32_t instance); + void Octetstring_Value_Out_Of_Service_Set( + uint32_t instance, + bool oos_flag); + + /* note: header of Intrinsic_Reporting function is required + even when INTRINSIC_REPORTING is not defined */ + void Octetstring_Value_Intrinsic_Reporting( + uint32_t object_instance); + + void Octetstring_Value_Init( + void); + +#ifdef TEST +#include "ctest.h" + void testOctetstring_Value( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif