/************************************************************************** * * Copyright (C) 2011 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. * *********************************************************************/ #include #include #include /* for memmove */ #include "bacdef.h" #include "bacdcode.h" #include "bacstr.h" #include "bacenum.h" #include "bacapp.h" #include "config.h" /* the custom stuff */ #include "apdu.h" #include "wp.h" /* WriteProperty handling */ #include "rp.h" /* ReadProperty handling */ #include "dcc.h" /* DeviceCommunicationControl handling */ #include "version.h" #include "device.h" /* me */ #include "handlers.h" #include "datalink.h" #include "address.h" /* os specfic includes */ #include "timer.h" /* objects */ #include "device.h" #include "bo.h" /* forward prototype */ int Device_Read_Property_Local( BACNET_READ_PROPERTY_DATA * rpdata); bool Device_Write_Property_Local( BACNET_WRITE_PROPERTY_DATA * wp_data); static struct my_object_functions { BACNET_OBJECT_TYPE Object_Type; object_init_function Object_Init; object_count_function Object_Count; object_index_to_instance_function Object_Index_To_Instance; object_valid_instance_function Object_Valid_Instance; object_name_function Object_Name; read_property_function Object_Read_Property; write_property_function Object_Write_Property; rpm_property_lists_function Object_RPM_List; } Object_Table[] = { { OBJECT_DEVICE, NULL, /* don't init - recursive! */ Device_Count, Device_Index_To_Instance, Device_Valid_Object_Instance_Number, Device_Name, Device_Read_Property_Local, Device_Write_Property_Local, Device_Property_Lists}, { OBJECT_BINARY_OUTPUT, Binary_Output_Init, Binary_Output_Count, Binary_Output_Index_To_Instance, Binary_Output_Valid_Instance, Binary_Output_Name, Binary_Output_Read_Property, Binary_Output_Write_Property, Binary_Output_Property_Lists}, { MAX_BACNET_OBJECT_TYPE, NULL, NULL, NULL, NULL, NULL, NULL, NULL} }; /* note: you really only need to define variables for properties that are writable or that may change. The properties that are constant can be hard coded into the read-property encoding. */ static uint32_t Object_Instance_Number = 12345; static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; static BACNET_REINITIALIZED_STATE Reinitialize_State = BACNET_REINIT_IDLE; static char My_Object_Name[MAX_DEV_NAME_LEN + 1] = "SimpleServer"; static char Model_Name[MAX_DEV_MOD_LEN + 1] = "RX62N"; static char Application_Software_Version[MAX_DEV_VER_LEN + 1] = "1.0"; static char Location[MAX_DEV_LOC_LEN + 1] = "USA"; static char Description[MAX_DEV_DESC_LEN + 1] = "Renesas Rulz!"; static uint32_t Database_Revision = 0; /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Device_Properties_Required[] = { PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_SYSTEM_STATUS, PROP_VENDOR_NAME, PROP_VENDOR_IDENTIFIER, PROP_MODEL_NAME, PROP_FIRMWARE_REVISION, PROP_APPLICATION_SOFTWARE_VERSION, PROP_PROTOCOL_VERSION, PROP_PROTOCOL_REVISION, PROP_PROTOCOL_SERVICES_SUPPORTED, PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, PROP_OBJECT_LIST, PROP_MAX_APDU_LENGTH_ACCEPTED, PROP_SEGMENTATION_SUPPORTED, PROP_APDU_TIMEOUT, PROP_NUMBER_OF_APDU_RETRIES, PROP_MAX_MASTER, PROP_MAX_INFO_FRAMES, PROP_DEVICE_ADDRESS_BINDING, PROP_DATABASE_REVISION, -1 }; static const int Device_Properties_Optional[] = { PROP_DESCRIPTION, -1 }; static const int Device_Properties_Proprietary[] = { -1 }; static struct my_object_functions *Device_Objects_Find_Functions( BACNET_OBJECT_TYPE Object_Type) { struct my_object_functions *pObject = NULL; pObject = &Object_Table[0]; while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { /* handle each object type */ if (pObject->Object_Type == Object_Type) { return (pObject); } pObject++; } return (NULL); } static int Read_Property_Common( struct my_object_functions *pObject, BACNET_READ_PROPERTY_DATA * rpdata) { int apdu_len = BACNET_STATUS_ERROR; BACNET_CHARACTER_STRING char_string; char *pString = ""; uint8_t *apdu = NULL; if ((rpdata->application_data == NULL) || (rpdata->application_data_len == 0)) { return 0; } apdu = rpdata->application_data; switch (rpdata->object_property) { case PROP_OBJECT_IDENTIFIER: /* Device Object exception: requested instance may not match our instance if a wildcard */ if (rpdata->object_type == OBJECT_DEVICE) { rpdata->object_instance = Object_Instance_Number; } apdu_len = encode_application_object_id(&apdu[0], rpdata->object_type, rpdata->object_instance); break; case PROP_OBJECT_NAME: if (pObject->Object_Name) { pString = pObject->Object_Name(rpdata->object_instance); } characterstring_init_ansi(&char_string, pString); apdu_len = encode_application_character_string(&apdu[0], &char_string); break; case PROP_OBJECT_TYPE: apdu_len = encode_application_enumerated(&apdu[0], rpdata->object_type); break; default: if (pObject->Object_Read_Property) { apdu_len = pObject->Object_Read_Property(rpdata); } break; } return apdu_len; } static unsigned property_list_count( const int *pList) { unsigned property_count = 0; if (pList) { while (*pList != -1) { property_count++; pList++; } } return property_count; } /** For a given object type, returns the special property list. * This function is used for ReadPropertyMultiple calls which want * just Required, just Optional, or All properties. * @ingroup ObjIntf * * @param object_type [in] The desired BACNET_OBJECT_TYPE whose properties * are to be listed. * @param pPropertyList [out] Reference to the structure which will, on return, * list, separately, the Required, Optional, and Proprietary object * properties with their counts. */ void Device_Objects_Property_List( BACNET_OBJECT_TYPE object_type, struct special_property_list_t *pPropertyList) { struct my_object_functions *pObject = NULL; pPropertyList->Required.pList = NULL; pPropertyList->Optional.pList = NULL; pPropertyList->Proprietary.pList = NULL; /* If we can find an entry for the required object type * and there is an Object_List_RPM fn ptr then call it * to populate the pointers to the individual list counters. */ pObject = Device_Objects_Find_Functions(object_type); if ((pObject != NULL) && (pObject->Object_RPM_List != NULL)) { pObject->Object_RPM_List(&pPropertyList->Required.pList, &pPropertyList->Optional.pList, &pPropertyList->Proprietary.pList); } /* Fetch the counts if available otherwise zero them */ pPropertyList->Required.count = pPropertyList->Required.pList == NULL ? 0 : property_list_count(pPropertyList->Required.pList); pPropertyList->Optional.count = pPropertyList->Optional.pList == NULL ? 0 : property_list_count(pPropertyList->Optional.pList); pPropertyList->Proprietary.count = pPropertyList->Proprietary.pList == NULL ? 0 : property_list_count(pPropertyList->Proprietary.pList); return; } void Device_Property_Lists( const int **pRequired, const int **pOptional, const int **pProprietary) { if (pRequired) *pRequired = Device_Properties_Required; if (pOptional) *pOptional = Device_Properties_Optional; if (pProprietary) *pProprietary = Device_Properties_Proprietary; return; } unsigned Device_Count( void) { return 1; } uint32_t Device_Index_To_Instance( unsigned index) { index = index; return Object_Instance_Number; } bool Device_Reinitialize( BACNET_REINITIALIZE_DEVICE_DATA * rd_data) { bool status = false; if (characterstring_ansi_same(&rd_data->password, "rehmite")) { Reinitialize_State = rd_data->state; dcc_set_status_duration(COMMUNICATION_ENABLE, 0); /* Note: you could use a mix of state and password to multiple things */ /* note: you probably want to restart *after* the simple ack has been sent from the return handler so just set a flag from here */ status = true; } else { rd_data->error_class = ERROR_CLASS_SECURITY; rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE; } return status; } BACNET_REINITIALIZED_STATE Device_Reinitialized_State( void) { return Reinitialize_State; } /* methods to manipulate the data */ uint32_t Device_Object_Instance_Number( void) { return Object_Instance_Number; } bool Device_Set_Object_Instance_Number( uint32_t object_id) { bool status = true; /* return value */ if (object_id <= BACNET_MAX_INSTANCE) { Object_Instance_Number = object_id; } else status = false; return status; } bool Device_Valid_Object_Instance_Number( uint32_t object_id) { return (Object_Instance_Number == object_id); } char *Device_Name( uint32_t object_instance) { if (object_instance == Object_Instance_Number) { return My_Object_Name; } return NULL; } const char *Device_Object_Name( void) { return My_Object_Name; } bool Device_Set_Object_Name( const char *name, size_t length) { bool status = false; /*return value */ /* FIXME: All the object names in a device must be unique. Disallow setting the Device Object Name to any objects in the device. */ if (length < sizeof(My_Object_Name)) { /* Make the change and update the database revision */ memmove(My_Object_Name, name, length); My_Object_Name[length] = 0; Device_Inc_Database_Revision(); status = true; } return status; } BACNET_DEVICE_STATUS Device_System_Status( void) { return System_Status; } int Device_Set_System_Status( BACNET_DEVICE_STATUS status, bool local) { /*return value - 0 = ok, -1 = bad value, -2 = not allowed */ int result = -1; if (status < MAX_DEVICE_STATUS) { System_Status = status; result = 0; } return result; } const char *Device_Description( void) { return Description; } bool Device_Set_Description( const char *name, size_t length) { bool status = false; /*return value */ if (length < sizeof(Description)) { memmove(Description, name, length); Description[length] = 0; status = true; } return status; } const char *Device_Location( void) { return Location; } bool Device_Set_Location( const char *name, size_t length) { bool status = false; /*return value */ if (length < sizeof(Location)) { memmove(Location, name, length); Location[length] = 0; status = true; } return status; } uint8_t Device_Protocol_Version( void) { return BACNET_PROTOCOL_VERSION; } uint8_t Device_Protocol_Revision( void) { return BACNET_PROTOCOL_REVISION; } uint16_t Device_Vendor_Identifier( void) { return BACNET_VENDOR_ID; } BACNET_SEGMENTATION Device_Segmentation_Supported( void) { return SEGMENTATION_NONE; } uint32_t Device_Database_Revision( void) { return Database_Revision; } void Device_Inc_Database_Revision( void) { Database_Revision++; } /* Since many network clients depend on the object list */ /* for discovery, it must be consistent! */ unsigned Device_Object_List_Count( void) { unsigned count = 0; /* number of objects */ struct my_object_functions *pObject = NULL; /* initialize the default return values */ pObject = &Object_Table[0]; while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { if (pObject->Object_Count) { count += pObject->Object_Count(); } pObject++; } return count; } bool Device_Object_List_Identifier( unsigned array_index, int *object_type, uint32_t * instance) { bool status = false; unsigned count = 0; unsigned object_index = 0; struct my_object_functions *pObject = NULL; /* array index zero is length - so invalid */ if (array_index == 0) { return status; } object_index = array_index - 1; /* initialize the default return values */ pObject = &Object_Table[0]; while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { if (pObject->Object_Count && pObject->Object_Index_To_Instance) { object_index -= count; count = pObject->Object_Count(); if (object_index < count) { *object_type = pObject->Object_Type; *instance = pObject->Object_Index_To_Instance(object_index); status = true; break; } } pObject++; } return status; } /** Determine if we have an object with the given object_name. * If the object_type and object_instance pointers are not null, * and the lookup succeeds, they will be given the resulting values. * @param object_name [in] The desired Object Name to look for. * @param object_type [out] The BACNET_OBJECT_TYPE of the matching Object. * @param object_instance [out] The object instance number of the matching Object. * @return True on success or else False if not found. */ bool Device_Valid_Object_Name( const char *object_name, int *object_type, uint32_t * object_instance) { bool found = false; int type = 0; uint32_t instance; unsigned max_objects = 0, i = 0; bool check_id = false; char *name = NULL; max_objects = Device_Object_List_Count(); for (i = 0; i < max_objects; i++) { check_id = Device_Object_List_Identifier(i, &type, &instance); if (check_id) { name = Device_Valid_Object_Id(type, instance); if (strcmp(name, object_name) == 0) { found = true; if (object_type) { *object_type = type; } if (object_instance) { *object_instance = instance; } break; } } } return found; } /* returns the name or NULL if not found */ char *Device_Valid_Object_Id( int object_type, uint32_t object_instance) { char *name = NULL; /* return value */ struct my_object_functions *pObject = NULL; pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE) object_type); if ((pObject) && (pObject->Object_Name)) { name = pObject->Object_Name(object_instance); } return name; } /* return the length of the apdu encoded or BACNET_STATUS_ERROR for error */ int Device_Read_Property_Local( BACNET_READ_PROPERTY_DATA * rpdata) { int apdu_len = 0; /* return value */ int len = 0; /* apdu len intermediate value */ BACNET_BIT_STRING bit_string; BACNET_CHARACTER_STRING char_string; unsigned i = 0; int object_type = 0; uint32_t instance = 0; unsigned count = 0; uint8_t *apdu = NULL; struct my_object_functions *pObject = NULL; bool found = false; if ((rpdata->application_data == NULL) || (rpdata->application_data_len == 0)) { return 0; } apdu = rpdata->application_data; switch (rpdata->object_property) { case PROP_DESCRIPTION: characterstring_init_ansi(&char_string, Description); apdu_len = encode_application_character_string(&apdu[0], &char_string); break; case PROP_SYSTEM_STATUS: apdu_len = encode_application_enumerated(&apdu[0], Device_System_Status()); break; case PROP_VENDOR_NAME: characterstring_init_ansi(&char_string, BACNET_VENDOR_NAME); apdu_len = encode_application_character_string(&apdu[0], &char_string); break; case PROP_VENDOR_IDENTIFIER: apdu_len = encode_application_unsigned(&apdu[0], BACNET_VENDOR_ID); break; case PROP_MODEL_NAME: characterstring_init_ansi(&char_string, Model_Name); apdu_len = encode_application_character_string(&apdu[0], &char_string); break; case PROP_FIRMWARE_REVISION: characterstring_init_ansi(&char_string, BACnet_Version); apdu_len = encode_application_character_string(&apdu[0], &char_string); break; case PROP_APPLICATION_SOFTWARE_VERSION: characterstring_init_ansi(&char_string, Application_Software_Version); apdu_len = encode_application_character_string(&apdu[0], &char_string); break; case PROP_LOCATION: characterstring_init_ansi(&char_string, Location); apdu_len = encode_application_character_string(&apdu[0], &char_string); break; case PROP_PROTOCOL_VERSION: apdu_len = encode_application_unsigned(&apdu[0], BACNET_PROTOCOL_VERSION); break; case PROP_PROTOCOL_REVISION: apdu_len = encode_application_unsigned(&apdu[0], BACNET_PROTOCOL_REVISION); break; case PROP_PROTOCOL_SERVICES_SUPPORTED: /* Note: list of services that are executed, not initiated. */ bitstring_init(&bit_string); for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { /* automatic lookup based on handlers set */ bitstring_set_bit(&bit_string, (uint8_t) i, apdu_service_supported((BACNET_SERVICES_SUPPORTED) i)); } apdu_len = encode_application_bitstring(&apdu[0], &bit_string); break; case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: /* Note: this is the list of objects that can be in this device, not a list of objects that this device can access */ bitstring_init(&bit_string); for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { /* FIXME: if ReadProperty used an array of Functions... */ /* initialize all the object types to not-supported */ bitstring_set_bit(&bit_string, (uint8_t) i, false); } /* set the object types with objects to supported */ i = 0; pObject = &Object_Table[i]; while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { if ((pObject->Object_Count) && (pObject->Object_Count() > 0)) { bitstring_set_bit(&bit_string, pObject->Object_Type, true); } pObject++; } apdu_len = encode_application_bitstring(&apdu[0], &bit_string); break; case PROP_OBJECT_LIST: count = Device_Object_List_Count(); /* Array element zero is the number of objects in the list */ if (rpdata->array_index == 0) apdu_len = encode_application_unsigned(&apdu[0], count); /* if no index was specified, then try to encode the entire list */ /* into one packet. Note that more than likely you will have */ /* to return an error if the number of encoded objects exceeds */ /* your maximum APDU size. */ else if (rpdata->array_index == BACNET_ARRAY_ALL) { for (i = 1; i <= count; i++) { if (Device_Object_List_Identifier(i, &object_type, &instance)) { len = encode_application_object_id(&apdu[apdu_len], object_type, instance); apdu_len += len; /* assume next one is the same size as this one */ /* can we all fit into the APDU? */ if ((apdu_len + len) >= MAX_APDU) { /* Abort response */ rpdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; apdu_len = BACNET_STATUS_ABORT; break; } } else { /* error: internal error? */ rpdata->error_class = ERROR_CLASS_SERVICES; rpdata->error_code = ERROR_CODE_OTHER; apdu_len = BACNET_STATUS_ERROR; break; } } } else { found = Device_Object_List_Identifier(rpdata->array_index, &object_type, &instance); if (found) { apdu_len = encode_application_object_id(&apdu[0], object_type, instance); } else { rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; apdu_len = BACNET_STATUS_ERROR; } } break; case PROP_MAX_APDU_LENGTH_ACCEPTED: apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU); break; case PROP_SEGMENTATION_SUPPORTED: apdu_len = encode_application_enumerated(&apdu[0], Device_Segmentation_Supported()); break; case PROP_APDU_TIMEOUT: apdu_len = encode_application_unsigned(&apdu[0], apdu_timeout()); break; case PROP_NUMBER_OF_APDU_RETRIES: apdu_len = encode_application_unsigned(&apdu[0], apdu_retries()); break; case PROP_DEVICE_ADDRESS_BINDING: /* FIXME: encode the list here, if it exists */ break; case PROP_DATABASE_REVISION: apdu_len = encode_application_unsigned(&apdu[0], Database_Revision); 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_OBJECT_LIST) && (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; } /** Looks up the requested Object and Property, and encodes its Value in an APDU. * @ingroup ObjIntf * If the Object or Property can't be found, sets the error class and code. * * @param rpdata [in,out] Structure with the desired Object and Property info * on entry, and APDU message on return. * @return The length of the APDU on success, else BACNET_STATUS_ERROR */ int Device_Read_Property( BACNET_READ_PROPERTY_DATA * rpdata) { int apdu_len = BACNET_STATUS_ERROR; struct my_object_functions *pObject = NULL; /* initialize the default return values */ rpdata->error_class = ERROR_CLASS_OBJECT; rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; pObject = Device_Objects_Find_Functions(rpdata->object_type); if (pObject) { if (pObject->Object_Valid_Instance && pObject->Object_Valid_Instance(rpdata->object_instance)) { apdu_len = Read_Property_Common(pObject, rpdata); } else { rpdata->error_class = ERROR_CLASS_OBJECT; rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; } } else { rpdata->error_class = ERROR_CLASS_OBJECT; rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; } return apdu_len; } /* returns true if successful */ bool Device_Write_Property_Local( BACNET_WRITE_PROPERTY_DATA * wp_data) { bool status = false; /* return value */ int len = 0; BACNET_APPLICATION_DATA_VALUE value; int temp; /* 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_OBJECT_LIST) && (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_OBJECT_IDENTIFIER: status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_OBJECT_ID, &wp_data->error_class, &wp_data->error_code); if (status) { if ((value.type.Object_Id.type == OBJECT_DEVICE) && (Device_Set_Object_Instance_Number(value.type.Object_Id. instance))) { /* we could send an I-Am broadcast to let the world know */ status = true; } else { status = false; wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; } } break; case PROP_SYSTEM_STATUS: status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, &wp_data->error_class, &wp_data->error_code); if (status) { temp = Device_Set_System_Status((BACNET_DEVICE_STATUS) value.type.Enumerated, false); if (temp != 0) { status = false; wp_data->error_class = ERROR_CLASS_PROPERTY; if (temp == -1) { wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; } else { wp_data->error_code = ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED; } } } break; case PROP_OBJECT_NAME: status = WPValidateString(&value, MAX_DEV_NAME_LEN, false, &wp_data->error_class, &wp_data->error_code); if (status) { Device_Set_Object_Name(characterstring_value(&value.type. Character_String), characterstring_length(&value.type.Character_String)); } break; case PROP_LOCATION: status = WPValidateString(&value, MAX_DEV_LOC_LEN, true, &wp_data->error_class, &wp_data->error_code); if (status) { Device_Set_Location(characterstring_value(&value.type. Character_String), characterstring_length(&value.type.Character_String)); } break; case PROP_DESCRIPTION: status = WPValidateString(&value, MAX_DEV_DESC_LEN, true, &wp_data->error_class, &wp_data->error_code); if (status) { Device_Set_Description(characterstring_value(&value.type. Character_String), characterstring_length(&value.type.Character_String)); } break; case PROP_OBJECT_TYPE: case PROP_VENDOR_NAME: case PROP_VENDOR_IDENTIFIER: case PROP_MODEL_NAME: case PROP_FIRMWARE_REVISION: case PROP_APPLICATION_SOFTWARE_VERSION: case PROP_PROTOCOL_VERSION: case PROP_PROTOCOL_REVISION: case PROP_PROTOCOL_SERVICES_SUPPORTED: case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: case PROP_OBJECT_LIST: case PROP_MAX_APDU_LENGTH_ACCEPTED: case PROP_SEGMENTATION_SUPPORTED: case PROP_DEVICE_ADDRESS_BINDING: case PROP_ACTIVE_COV_SUBSCRIPTIONS: case PROP_DATABASE_REVISION: 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; } /* not using len at this time */ len = len; return status; } /** Looks up the requested Object and Property, and set the new Value in it, * if allowed. * If the Object or Property can't be found, sets the error class and code. * @ingroup ObjIntf * * @param wp_data [in,out] Structure with the desired Object and Property info * and new Value on entry, and APDU message on return. * @return True on success, else False if there is an error. */ bool Device_Write_Property( BACNET_WRITE_PROPERTY_DATA * wp_data) { bool status = false; struct my_object_functions *pObject = NULL; /* initialize the default return values */ wp_data->error_class = ERROR_CLASS_OBJECT; wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; pObject = Device_Objects_Find_Functions(wp_data->object_type); if (pObject != NULL) { if (pObject->Object_Valid_Instance && pObject->Object_Valid_Instance(wp_data->object_instance)) { if (pObject->Object_Write_Property) { status = pObject->Object_Write_Property(wp_data); } else { wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; } } else { wp_data->error_class = ERROR_CLASS_OBJECT; wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; } } else { wp_data->error_class = ERROR_CLASS_OBJECT; wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; } return status; } /** Initialize the Device Object and each of its child Object instances. * @ingroup ObjIntf */ void Device_Init( object_functions_t * object_table) { struct my_object_functions *pObject = NULL; /* not using the standard table - using our own */ (void) object_table; pObject = &Object_Table[0]; while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { if (pObject->Object_Init) { pObject->Object_Init(); } pObject++; } dcc_set_status_duration(COMMUNICATION_ENABLE, 0); }