diff --git a/bacnet-stack/demo/object/device-client.c b/bacnet-stack/demo/object/device-client.c index 3c564552..d0968faa 100644 --- a/bacnet-stack/demo/object/device-client.c +++ b/bacnet-stack/demo/object/device-client.c @@ -1,945 +1,945 @@ -/************************************************************************** -* -* 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. -* -*********************************************************************/ - -/** @file device-client.c Lightweight base "class" for handling all - * BACnet objects belonging to a BACnet device, as well as - * Device-specific properties. This Device instance is designed to - * meet minimal functionality for simple clients. */ - -#include -#include -#include /* for memmove */ -#include /* for timezone, localtime */ -#include "bacdef.h" -#include "bacdcode.h" -#include "bacenum.h" -#include "bacapp.h" -#include "config.h" /* the custom stuff */ -#include "apdu.h" -#include "rp.h" /* ReadProperty handling */ -#include "version.h" -#include "handlers.h" -#include "datalink.h" -#include "address.h" -/* include the device object */ -#include "device.h" /* me */ - -/* 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 = 260001; -static BACNET_CHARACTER_STRING My_Object_Name; -static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; -static char *Vendor_Name = BACNET_VENDOR_NAME; -static uint16_t Vendor_Identifier = BACNET_VENDOR_ID; -static char *Model_Name = "GNU"; -static char *Application_Software_Version = "1.0"; -static char *Location = "USA"; -static char *Description = "command line client"; -/* static uint8_t Protocol_Version = 1; - constant, not settable */ -/* static uint8_t Protocol_Revision = 4; - constant, not settable */ -/* Protocol_Services_Supported - dynamically generated */ -/* Protocol_Object_Types_Supported - in RP encoding */ -/* Object_List - dynamically generated */ -/* static BACNET_SEGMENTATION Segmentation_Supported = SEGMENTATION_NONE; */ -/* static uint8_t Max_Segments_Accepted = 0; */ -/* VT_Classes_Supported */ -/* Active_VT_Sessions */ -/* static BACNET_TIME Local_Time; rely on OS, if there is one */ -/* static BACNET_DATE Local_Date; rely on OS, if there is one */ -/* NOTE: BACnet UTC Offset is inverse of common practice. - If your UTC offset is -5hours of GMT, - then BACnet UTC offset is +5hours. - BACnet UTC offset is expressed in minutes. */ -/* static int32_t UTC_Offset = 5 * 60; */ -/* static bool Daylight_Savings_Status = false; rely on OS */ -/* List_Of_Session_Keys */ -/* Time_Synchronization_Recipients */ -/* Max_Master - rely on MS/TP subsystem, if there is one */ -/* Max_Info_Frames - rely on MS/TP subsystem, if there is one */ -/* Device_Address_Binding - required, but relies on binding cache */ -static uint32_t Database_Revision = 0; -/* Configuration_Files */ -/* Last_Restore_Time */ -/* Backup_Failure_Timeout */ -/* Active_COV_Subscriptions */ -/* Slave_Proxy_Enable */ -/* Manual_Slave_Address_Binding */ -/* Auto_Slave_Discovery */ -/* Slave_Address_Binding */ -/* Profile_Name */ - -/* local forward (semi-private) and external prototypes */ -int Device_Read_Property_Local( - BACNET_READ_PROPERTY_DATA * rpdata); -extern int Routed_Device_Read_Property_Local( - BACNET_READ_PROPERTY_DATA * rpdata); -extern bool Routed_Device_Write_Property_Local( - BACNET_WRITE_PROPERTY_DATA * wp_data); - -/* All included BACnet objects */ -static object_functions_t Object_Table[] = { - {OBJECT_DEVICE, - NULL /* Init - don't init Device or it will recourse! */, - Device_Count, - Device_Index_To_Instance, - Device_Valid_Object_Instance_Number, - Device_Object_Name, - Device_Read_Property_Local, - NULL /* Write_Property */, - NULL /* Property_Lists */, - NULL /* ReadRangeInfo */, - NULL /* Iterator */, - NULL /* Value_Lists */, - NULL /* COV */, - NULL /* COV Clear */, - NULL /* Intrinsic Reporting */}, - {MAX_BACNET_OBJECT_TYPE, - NULL /* Init */, - NULL /* Count */, - NULL /* Index_To_Instance */, - NULL /* Valid_Instance */, - NULL /* Object_Name */, - NULL /* Read_Property */, - NULL /* Write_Property */, - NULL /* Property_Lists */, - NULL /* ReadRangeInfo */, - NULL /* Iterator */, - NULL /* Value_Lists */, - NULL /* COV */, - NULL /* COV Clear */, - NULL /* Intrinsic Reporting */} -}; - -/** Glue function to let the Device object, when called by a handler, - * lookup which Object type needs to be invoked. - * @ingroup ObjHelpers - * @param Object_Type [in] The type of BACnet Object the handler wants to access. - * @return Pointer to the group of object helper functions that implement this - * type of Object. - */ -static struct object_functions *Device_Objects_Find_Functions( - BACNET_OBJECT_TYPE Object_Type) -{ - struct 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); -} - -unsigned Device_Count( - void) -{ - return 1; -} - -uint32_t Device_Index_To_Instance( - unsigned index) -{ - index = index; - return Object_Instance_Number; -} - -/* methods to manipulate the data */ - -/** Return the Object Instance number for our (single) Device Object. - * This is a key function, widely invoked by the handler code, since - * it provides "our" (ie, local) address. - * @ingroup ObjIntf - * @return The Instance number used in the BACNET_OBJECT_ID for the Device. - */ -uint32_t Device_Object_Instance_Number( - void) -{ -#ifdef BAC_ROUTING - return Routed_Device_Object_Instance_Number(); -#else - return Object_Instance_Number; -#endif -} - -bool Device_Set_Object_Instance_Number( - uint32_t object_id) -{ - bool status = true; /* return value */ - - if (object_id <= BACNET_MAX_INSTANCE) { - /* Make the change and update the database revision */ - Object_Instance_Number = object_id; - Device_Inc_Database_Revision(); - } else - status = false; - - return status; -} - -bool Device_Valid_Object_Instance_Number( - uint32_t object_id) -{ - /* BACnet allows for a wildcard instance number */ - return ((Object_Instance_Number == object_id) || - (object_id == BACNET_MAX_INSTANCE)); -} - -bool Device_Object_Name( - uint32_t object_instance, - BACNET_CHARACTER_STRING * object_name) -{ - bool status = false; - - if (object_instance == Object_Instance_Number) { - status = characterstring_copy(object_name, &My_Object_Name); - } - - return status; -} - -bool Device_Set_Object_Name( - BACNET_CHARACTER_STRING * object_name) -{ - bool status = false; /*return value */ - - if (!characterstring_same(&My_Object_Name, object_name)) { - /* Make the change and update the database revision */ - status = characterstring_copy(&My_Object_Name, object_name); - Device_Inc_Database_Revision(); - } - - return status; -} - -BACNET_DEVICE_STATUS Device_System_Status( - void) -{ - return System_Status; -} - -int Device_Set_System_Status( - BACNET_DEVICE_STATUS status, - bool local) -{ - int result = 0; /*return value - 0 = ok, -1 = bad value, -2 = not allowed */ - - /* We limit the options available depending on whether the source is - * internal or external. */ - if (local) { - switch (status) { - case STATUS_OPERATIONAL: - case STATUS_OPERATIONAL_READ_ONLY: - case STATUS_DOWNLOAD_REQUIRED: - case STATUS_DOWNLOAD_IN_PROGRESS: - case STATUS_NON_OPERATIONAL: - System_Status = status; - break; - - /* Don't support backup at present so don't allow setting */ - case STATUS_BACKUP_IN_PROGRESS: - result = -2; - break; - - default: - result = -1; - break; - } - } else { - switch (status) { - /* Allow these for the moment as a way to easily alter - * overall device operation. The lack of password protection - * or other authentication makes allowing writes to this - * property a risky facility to provide. - */ - case STATUS_OPERATIONAL: - case STATUS_OPERATIONAL_READ_ONLY: - case STATUS_NON_OPERATIONAL: - System_Status = status; - break; - - /* Don't allow outsider set this - it should probably - * be set if the device config is incomplete or - * corrupted or perhaps after some sort of operator - * wipe operation. - */ - case STATUS_DOWNLOAD_REQUIRED: - /* Don't allow outsider set this - it should be set - * internally at the start of a multi packet download - * perhaps indirectly via PT or WF to a config file. - */ - case STATUS_DOWNLOAD_IN_PROGRESS: - /* Don't support backup at present so don't allow setting */ - case STATUS_BACKUP_IN_PROGRESS: - result = -2; - break; - - default: - result = -1; - break; - } - } - - return (result); -} - -const char *Device_Vendor_Name( - void) -{ - return Vendor_Name; -} - -/** Returns the Vendor ID for this Device. - * See the assignments at http://www.bacnet.org/VendorID/BACnet%20Vendor%20IDs.htm - * @return The Vendor ID of this Device. - */ -uint16_t Device_Vendor_Identifier( - void) -{ - return Vendor_Identifier; -} - -void Device_Set_Vendor_Identifier( - uint16_t vendor_id) -{ - Vendor_Identifier = vendor_id; -} - -const char *Device_Model_Name( - void) -{ - return Model_Name; -} - -bool Device_Set_Model_Name( - const char *name, - size_t length) -{ - bool status = false; /*return value */ - - if (length < sizeof(Model_Name)) { - memmove(Model_Name, name, length); - Model_Name[length] = 0; - status = true; - } - - return status; -} - -const char *Device_Firmware_Revision( - void) -{ - return BACnet_Version; -} - -const char *Device_Application_Software_Version( - void) -{ - return Application_Software_Version; -} - -bool Device_Set_Application_Software_Version( - const char *name, - size_t length) -{ - bool status = false; /*return value */ - - if (length < sizeof(Application_Software_Version)) { - memmove(Application_Software_Version, name, length); - Application_Software_Version[length] = 0; - status = true; - } - - return status; -} - -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; -} - -BACNET_SEGMENTATION Device_Segmentation_Supported( - void) -{ - return SEGMENTATION_NONE; -} - -uint32_t Device_Database_Revision( - void) -{ - return Database_Revision; -} - -void Device_Set_Database_Revision( - uint32_t revision) -{ - Database_Revision = revision; -} - -/* - * Shortcut for incrementing database revision as this is potentially - * the most common operation if changing object names and ids is - * implemented. - */ -void Device_Inc_Database_Revision( - void) -{ - Database_Revision++; -} - -/** Get the total count of objects supported by this Device Object. - * @note Since many network clients depend on the object list - * for discovery, it must be consistent! - * @return The count of objects, for all supported Object types. - */ -unsigned Device_Object_List_Count( - void) -{ - unsigned count = 0; /* number of objects */ - struct 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; -} - -/** Lookup the Object at the given array index in the Device's Object List. - * Even though we don't keep a single linear array of objects in the Device, - * this method acts as though we do and works through a virtual, concatenated - * array of all of our object type arrays. - * - * @param array_index [in] The desired array index (1 to N) - * @param object_type [out] The object's type, if found. - * @param instance [out] The object's instance number, if found. - * @return True if found, else false. - */ -bool Device_Object_List_Identifier( - unsigned array_index, - int *object_type, - uint32_t * instance) -{ - bool status = false; - unsigned count = 0; - unsigned object_index = 0; - unsigned temp_index = 0; - struct 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) { - object_index -= count; - count = pObject->Object_Count(); - if (object_index < count) { - /* Use the iterator function if available otherwise - * look for the index to instance to get the ID */ - if (pObject->Object_Iterator) { - /* First find the first object */ - temp_index = pObject->Object_Iterator(~(unsigned) 0); - /* Then step through the objects to find the nth */ - while (object_index != 0) { - temp_index = pObject->Object_Iterator(temp_index); - object_index--; - } - /* set the object_index up before falling through to next bit */ - object_index = temp_index; - } - if (pObject->Object_Index_To_Instance) { - *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( - BACNET_CHARACTER_STRING * object_name1, - 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; - BACNET_CHARACTER_STRING object_name2; - struct object_functions *pObject = 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) { - pObject = Device_Objects_Find_Functions(type); - if ((pObject != NULL) && (pObject->Object_Name != NULL) && - (pObject->Object_Name(instance, &object_name2) && - characterstring_same(object_name1, &object_name2))) { - found = true; - if (object_type) { - *object_type = type; - } - if (object_instance) { - *object_instance = instance; - } - break; - } - } - } - - return found; -} - -/** Determine if we have an object of this type and instance number. - * @param object_type [in] The desired BACNET_OBJECT_TYPE - * @param object_instance [in] The object instance number to be looked up. - * @return True if found, else False if no such Object in this device. - */ -bool Device_Valid_Object_Id( - int object_type, - uint32_t object_instance) -{ - bool status = false; /* return value */ - struct object_functions *pObject = NULL; - - pObject = Device_Objects_Find_Functions(object_type); - if ((pObject != NULL) && (pObject->Object_Valid_Instance != NULL)) { - status = pObject->Object_Valid_Instance(object_instance); - } - - return status; -} - -/** Copy a child object's object_name value, given its ID. - * @param object_type [in] The BACNET_OBJECT_TYPE of the child Object. - * @param object_instance [in] The object instance number of the child Object. - * @param object_name [out] The Object Name found for this child Object. - * @return True on success or else False if not found. - */ -bool Device_Object_Name_Copy( - BACNET_OBJECT_TYPE object_type, - uint32_t object_instance, - BACNET_CHARACTER_STRING * object_name) -{ - struct object_functions *pObject = NULL; - bool found = false; - - pObject = Device_Objects_Find_Functions(object_type); - if ((pObject != NULL) && (pObject->Object_Name != NULL)) { - found = pObject->Object_Name(object_instance, object_name); - } - - return found; -} - -/* return the length of the apdu encoded or BACNET_STATUS_ERROR for error or - BACNET_STATUS_ABORT for abort message */ -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 object_functions *pObject = NULL; - bool found = false; - - 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_DEVICE, - Object_Instance_Number); - break; - case PROP_OBJECT_NAME: - apdu_len = - encode_application_character_string(&apdu[0], &My_Object_Name); - break; - case PROP_OBJECT_TYPE: - apdu_len = encode_application_enumerated(&apdu[0], OBJECT_DEVICE); - break; - 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], System_Status); - break; - case PROP_VENDOR_NAME: - characterstring_init_ansi(&char_string, Vendor_Name); - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; - case PROP_VENDOR_IDENTIFIER: - apdu_len = - encode_application_unsigned(&apdu[0], Vendor_Identifier); - 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], - Device_Protocol_Version()); - break; - case PROP_PROTOCOL_REVISION: - apdu_len = - encode_application_unsigned(&apdu[0], - Device_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++) { - /* 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 */ - - pObject = &Object_Table[0]; - 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++) { - found = - Device_Object_List_Identifier(i, &object_type, - &instance); - if (found) { - 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? Don't check for last entry */ - if ((i != count) && (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: the real max apdu remaining should be passed into function */ - apdu_len = address_list_encode(&apdu[0], MAX_APDU); - break; - case PROP_DATABASE_REVISION: - apdu_len = - encode_application_unsigned(&apdu[0], Database_Revision); - break; -#if defined(BACDL_MSTP) - case PROP_MAX_INFO_FRAMES: - apdu_len = - encode_application_unsigned(&apdu[0], - dlmstp_max_info_frames()); - break; - case PROP_MAX_MASTER: - apdu_len = - encode_application_unsigned(&apdu[0], dlmstp_max_master()); - break; -#endif - case PROP_ACTIVE_COV_SUBSCRIPTIONS: - 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 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 != NULL) { - if (pObject->Object_Valid_Instance && - pObject->Object_Valid_Instance(rpdata->object_instance)) { - if (pObject->Object_Read_Property) { - apdu_len = pObject->Object_Read_Property(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_UNSUPPORTED_OBJECT_TYPE; - } - - return apdu_len; -} - -/** The device-client version of this function just returns an error, - * since we don't need to allow writing to our objects. - * - * Note: it was necessary to include this trivial implementation of this function - * to avoid pulling in the device.c module from libbacnet.a for the - * handler_write_property() support, which resulted in hard-to-trace - * duplicate function errors. - * - * @param wp_data [in,out] Structure with the desired Object and Property info - * and new Value on entry, and APDU message on return. - * @return Always False because it is an error. - */ -bool Device_Write_Property( - BACNET_WRITE_PROPERTY_DATA * wp_data) -{ - bool status = false; - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; - return (status); -} -/* Ditto for the local version; always returns false */ -bool Device_Write_Property_Local( - BACNET_WRITE_PROPERTY_DATA * wp_data) -{ - bool status = false; - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; - return (status); -} - - -/** Initialize the Device Object. - Initialize the group of object helper functions for any supported Object. - Initialize each of the Device Object child Object instances. - * @ingroup ObjIntf - * @param object_table [in,out] array of structure with object functions. - * Each Child Object must provide some implementation of each of these - * functions in order to properly support the default handlers. - */ -void Device_Init( - object_functions_t * object_table) -{ - struct object_functions *pObject = NULL; - - characterstring_init_ansi(&My_Object_Name, "SimpleClient"); - /* we don't use the object table passed in */ - (void) object_table; - pObject = &Object_Table[0]; - while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { - if (pObject->Object_Init) { - pObject->Object_Init(); - } - pObject++; - } -} - - +/************************************************************************** +* +* 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. +* +*********************************************************************/ + +/** @file device-client.c Lightweight base "class" for handling all + * BACnet objects belonging to a BACnet device, as well as + * Device-specific properties. This Device instance is designed to + * meet minimal functionality for simple clients. */ + +#include +#include +#include /* for memmove */ +#include /* for timezone, localtime */ +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "apdu.h" +#include "rp.h" /* ReadProperty handling */ +#include "version.h" +#include "handlers.h" +#include "datalink.h" +#include "address.h" +/* include the device object */ +#include "device.h" /* me */ + +/* 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 = 260001; +static BACNET_CHARACTER_STRING My_Object_Name; +static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; +static char *Vendor_Name = BACNET_VENDOR_NAME; +static uint16_t Vendor_Identifier = BACNET_VENDOR_ID; +static char *Model_Name = "GNU"; +static char *Application_Software_Version = "1.0"; +static char *Location = "USA"; +static char *Description = "command line client"; +/* static uint8_t Protocol_Version = 1; - constant, not settable */ +/* static uint8_t Protocol_Revision = 4; - constant, not settable */ +/* Protocol_Services_Supported - dynamically generated */ +/* Protocol_Object_Types_Supported - in RP encoding */ +/* Object_List - dynamically generated */ +/* static BACNET_SEGMENTATION Segmentation_Supported = SEGMENTATION_NONE; */ +/* static uint8_t Max_Segments_Accepted = 0; */ +/* VT_Classes_Supported */ +/* Active_VT_Sessions */ +/* static BACNET_TIME Local_Time; rely on OS, if there is one */ +/* static BACNET_DATE Local_Date; rely on OS, if there is one */ +/* NOTE: BACnet UTC Offset is inverse of common practice. + If your UTC offset is -5hours of GMT, + then BACnet UTC offset is +5hours. + BACnet UTC offset is expressed in minutes. */ +/* static int32_t UTC_Offset = 5 * 60; */ +/* static bool Daylight_Savings_Status = false; rely on OS */ +/* List_Of_Session_Keys */ +/* Time_Synchronization_Recipients */ +/* Max_Master - rely on MS/TP subsystem, if there is one */ +/* Max_Info_Frames - rely on MS/TP subsystem, if there is one */ +/* Device_Address_Binding - required, but relies on binding cache */ +static uint32_t Database_Revision = 0; +/* Configuration_Files */ +/* Last_Restore_Time */ +/* Backup_Failure_Timeout */ +/* Active_COV_Subscriptions */ +/* Slave_Proxy_Enable */ +/* Manual_Slave_Address_Binding */ +/* Auto_Slave_Discovery */ +/* Slave_Address_Binding */ +/* Profile_Name */ + +/* local forward (semi-private) and external prototypes */ +int Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata); +extern int Routed_Device_Read_Property_Local( + BACNET_READ_PROPERTY_DATA * rpdata); +extern bool Routed_Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data); + +/* All included BACnet objects */ +static object_functions_t Object_Table[] = { + {OBJECT_DEVICE, + NULL /* Init - don't init Device or it will recourse! */, + Device_Count, + Device_Index_To_Instance, + Device_Valid_Object_Instance_Number, + Device_Object_Name, + Device_Read_Property_Local, + NULL /* Write_Property */, + NULL /* Property_Lists */, + NULL /* ReadRangeInfo */, + NULL /* Iterator */, + NULL /* Value_Lists */, + NULL /* COV */, + NULL /* COV Clear */, + NULL /* Intrinsic Reporting */}, + {MAX_BACNET_OBJECT_TYPE, + NULL /* Init */, + NULL /* Count */, + NULL /* Index_To_Instance */, + NULL /* Valid_Instance */, + NULL /* Object_Name */, + NULL /* Read_Property */, + NULL /* Write_Property */, + NULL /* Property_Lists */, + NULL /* ReadRangeInfo */, + NULL /* Iterator */, + NULL /* Value_Lists */, + NULL /* COV */, + NULL /* COV Clear */, + NULL /* Intrinsic Reporting */} +}; + +/** Glue function to let the Device object, when called by a handler, + * lookup which Object type needs to be invoked. + * @ingroup ObjHelpers + * @param Object_Type [in] The type of BACnet Object the handler wants to access. + * @return Pointer to the group of object helper functions that implement this + * type of Object. + */ +static struct object_functions *Device_Objects_Find_Functions( + BACNET_OBJECT_TYPE Object_Type) +{ + struct 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); +} + +unsigned Device_Count( + void) +{ + return 1; +} + +uint32_t Device_Index_To_Instance( + unsigned index) +{ + index = index; + return Object_Instance_Number; +} + +/* methods to manipulate the data */ + +/** Return the Object Instance number for our (single) Device Object. + * This is a key function, widely invoked by the handler code, since + * it provides "our" (ie, local) address. + * @ingroup ObjIntf + * @return The Instance number used in the BACNET_OBJECT_ID for the Device. + */ +uint32_t Device_Object_Instance_Number( + void) +{ +#ifdef BAC_ROUTING + return Routed_Device_Object_Instance_Number(); +#else + return Object_Instance_Number; +#endif +} + +bool Device_Set_Object_Instance_Number( + uint32_t object_id) +{ + bool status = true; /* return value */ + + if (object_id <= BACNET_MAX_INSTANCE) { + /* Make the change and update the database revision */ + Object_Instance_Number = object_id; + Device_Inc_Database_Revision(); + } else + status = false; + + return status; +} + +bool Device_Valid_Object_Instance_Number( + uint32_t object_id) +{ + /* BACnet allows for a wildcard instance number */ + return ((Object_Instance_Number == object_id) || + (object_id == BACNET_MAX_INSTANCE)); +} + +bool Device_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + bool status = false; + + if (object_instance == Object_Instance_Number) { + status = characterstring_copy(object_name, &My_Object_Name); + } + + return status; +} + +bool Device_Set_Object_Name( + BACNET_CHARACTER_STRING * object_name) +{ + bool status = false; /*return value */ + + if (!characterstring_same(&My_Object_Name, object_name)) { + /* Make the change and update the database revision */ + status = characterstring_copy(&My_Object_Name, object_name); + Device_Inc_Database_Revision(); + } + + return status; +} + +BACNET_DEVICE_STATUS Device_System_Status( + void) +{ + return System_Status; +} + +int Device_Set_System_Status( + BACNET_DEVICE_STATUS status, + bool local) +{ + int result = 0; /*return value - 0 = ok, -1 = bad value, -2 = not allowed */ + + /* We limit the options available depending on whether the source is + * internal or external. */ + if (local) { + switch (status) { + case STATUS_OPERATIONAL: + case STATUS_OPERATIONAL_READ_ONLY: + case STATUS_DOWNLOAD_REQUIRED: + case STATUS_DOWNLOAD_IN_PROGRESS: + case STATUS_NON_OPERATIONAL: + System_Status = status; + break; + + /* Don't support backup at present so don't allow setting */ + case STATUS_BACKUP_IN_PROGRESS: + result = -2; + break; + + default: + result = -1; + break; + } + } else { + switch (status) { + /* Allow these for the moment as a way to easily alter + * overall device operation. The lack of password protection + * or other authentication makes allowing writes to this + * property a risky facility to provide. + */ + case STATUS_OPERATIONAL: + case STATUS_OPERATIONAL_READ_ONLY: + case STATUS_NON_OPERATIONAL: + System_Status = status; + break; + + /* Don't allow outsider set this - it should probably + * be set if the device config is incomplete or + * corrupted or perhaps after some sort of operator + * wipe operation. + */ + case STATUS_DOWNLOAD_REQUIRED: + /* Don't allow outsider set this - it should be set + * internally at the start of a multi packet download + * perhaps indirectly via PT or WF to a config file. + */ + case STATUS_DOWNLOAD_IN_PROGRESS: + /* Don't support backup at present so don't allow setting */ + case STATUS_BACKUP_IN_PROGRESS: + result = -2; + break; + + default: + result = -1; + break; + } + } + + return (result); +} + +const char *Device_Vendor_Name( + void) +{ + return Vendor_Name; +} + +/** Returns the Vendor ID for this Device. + * See the assignments at http://www.bacnet.org/VendorID/BACnet%20Vendor%20IDs.htm + * @return The Vendor ID of this Device. + */ +uint16_t Device_Vendor_Identifier( + void) +{ + return Vendor_Identifier; +} + +void Device_Set_Vendor_Identifier( + uint16_t vendor_id) +{ + Vendor_Identifier = vendor_id; +} + +const char *Device_Model_Name( + void) +{ + return Model_Name; +} + +bool Device_Set_Model_Name( + const char *name, + size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Model_Name)) { + memmove(Model_Name, name, length); + Model_Name[length] = 0; + status = true; + } + + return status; +} + +const char *Device_Firmware_Revision( + void) +{ + return BACnet_Version; +} + +const char *Device_Application_Software_Version( + void) +{ + return Application_Software_Version; +} + +bool Device_Set_Application_Software_Version( + const char *name, + size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Application_Software_Version)) { + memmove(Application_Software_Version, name, length); + Application_Software_Version[length] = 0; + status = true; + } + + return status; +} + +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; +} + +BACNET_SEGMENTATION Device_Segmentation_Supported( + void) +{ + return SEGMENTATION_NONE; +} + +uint32_t Device_Database_Revision( + void) +{ + return Database_Revision; +} + +void Device_Set_Database_Revision( + uint32_t revision) +{ + Database_Revision = revision; +} + +/* + * Shortcut for incrementing database revision as this is potentially + * the most common operation if changing object names and ids is + * implemented. + */ +void Device_Inc_Database_Revision( + void) +{ + Database_Revision++; +} + +/** Get the total count of objects supported by this Device Object. + * @note Since many network clients depend on the object list + * for discovery, it must be consistent! + * @return The count of objects, for all supported Object types. + */ +unsigned Device_Object_List_Count( + void) +{ + unsigned count = 0; /* number of objects */ + struct 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; +} + +/** Lookup the Object at the given array index in the Device's Object List. + * Even though we don't keep a single linear array of objects in the Device, + * this method acts as though we do and works through a virtual, concatenated + * array of all of our object type arrays. + * + * @param array_index [in] The desired array index (1 to N) + * @param object_type [out] The object's type, if found. + * @param instance [out] The object's instance number, if found. + * @return True if found, else false. + */ +bool Device_Object_List_Identifier( + unsigned array_index, + int *object_type, + uint32_t * instance) +{ + bool status = false; + unsigned count = 0; + unsigned object_index = 0; + unsigned temp_index = 0; + struct 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) { + object_index -= count; + count = pObject->Object_Count(); + if (object_index < count) { + /* Use the iterator function if available otherwise + * look for the index to instance to get the ID */ + if (pObject->Object_Iterator) { + /* First find the first object */ + temp_index = pObject->Object_Iterator(~(unsigned) 0); + /* Then step through the objects to find the nth */ + while (object_index != 0) { + temp_index = pObject->Object_Iterator(temp_index); + object_index--; + } + /* set the object_index up before falling through to next bit */ + object_index = temp_index; + } + if (pObject->Object_Index_To_Instance) { + *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( + BACNET_CHARACTER_STRING * object_name1, + 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; + BACNET_CHARACTER_STRING object_name2; + struct object_functions *pObject = 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) { + pObject = Device_Objects_Find_Functions(type); + if ((pObject != NULL) && (pObject->Object_Name != NULL) && + (pObject->Object_Name(instance, &object_name2) && + characterstring_same(object_name1, &object_name2))) { + found = true; + if (object_type) { + *object_type = type; + } + if (object_instance) { + *object_instance = instance; + } + break; + } + } + } + + return found; +} + +/** Determine if we have an object of this type and instance number. + * @param object_type [in] The desired BACNET_OBJECT_TYPE + * @param object_instance [in] The object instance number to be looked up. + * @return True if found, else False if no such Object in this device. + */ +bool Device_Valid_Object_Id( + int object_type, + uint32_t object_instance) +{ + bool status = false; /* return value */ + struct object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_Valid_Instance != NULL)) { + status = pObject->Object_Valid_Instance(object_instance); + } + + return status; +} + +/** Copy a child object's object_name value, given its ID. + * @param object_type [in] The BACNET_OBJECT_TYPE of the child Object. + * @param object_instance [in] The object instance number of the child Object. + * @param object_name [out] The Object Name found for this child Object. + * @return True on success or else False if not found. + */ +bool Device_Object_Name_Copy( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + struct object_functions *pObject = NULL; + bool found = false; + + pObject = Device_Objects_Find_Functions(object_type); + if ((pObject != NULL) && (pObject->Object_Name != NULL)) { + found = pObject->Object_Name(object_instance, object_name); + } + + return found; +} + +/* return the length of the apdu encoded or BACNET_STATUS_ERROR for error or + BACNET_STATUS_ABORT for abort message */ +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 object_functions *pObject = NULL; + bool found = false; + + 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_DEVICE, + Object_Instance_Number); + break; + case PROP_OBJECT_NAME: + apdu_len = + encode_application_character_string(&apdu[0], &My_Object_Name); + break; + case PROP_OBJECT_TYPE: + apdu_len = encode_application_enumerated(&apdu[0], OBJECT_DEVICE); + break; + 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], System_Status); + break; + case PROP_VENDOR_NAME: + characterstring_init_ansi(&char_string, Vendor_Name); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = + encode_application_unsigned(&apdu[0], Vendor_Identifier); + 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], + Device_Protocol_Version()); + break; + case PROP_PROTOCOL_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_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++) { + /* 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 */ + + pObject = &Object_Table[0]; + 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++) { + found = + Device_Object_List_Identifier(i, &object_type, + &instance); + if (found) { + 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? Don't check for last entry */ + if ((i != count) && (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: the real max apdu remaining should be passed into function */ + apdu_len = address_list_encode(&apdu[0], MAX_APDU); + break; + case PROP_DATABASE_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], Database_Revision); + break; +#if defined(BACDL_MSTP) + case PROP_MAX_INFO_FRAMES: + apdu_len = + encode_application_unsigned(&apdu[0], + dlmstp_max_info_frames()); + break; + case PROP_MAX_MASTER: + apdu_len = + encode_application_unsigned(&apdu[0], dlmstp_max_master()); + break; +#endif + case PROP_ACTIVE_COV_SUBSCRIPTIONS: + 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 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 != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(rpdata->object_instance)) { + if (pObject->Object_Read_Property) { + apdu_len = pObject->Object_Read_Property(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; +} + +/** The device-client version of this function just returns an error, + * since we don't need to allow writing to our objects. + * + * Note: it was necessary to include this trivial implementation of this function + * to avoid pulling in the device.c module from libbacnet.a for the + * handler_write_property() support, which resulted in hard-to-trace + * duplicate function errors. + * + * @param wp_data [in,out] Structure with the desired Object and Property info + * and new Value on entry, and APDU message on return. + * @return Always False because it is an error. + */ +bool Device_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + return (status); +} +/* Ditto for the local version; always returns false */ +bool Device_Write_Property_Local( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + return (status); +} + + +/** Initialize the Device Object. + Initialize the group of object helper functions for any supported Object. + Initialize each of the Device Object child Object instances. + * @ingroup ObjIntf + * @param object_table [in,out] array of structure with object functions. + * Each Child Object must provide some implementation of each of these + * functions in order to properly support the default handlers. + */ +void Device_Init( + object_functions_t * object_table) +{ + struct object_functions *pObject = NULL; + + characterstring_init_ansi(&My_Object_Name, "SimpleClient"); + /* we don't use the object table passed in */ + (void) object_table; + pObject = &Object_Table[0]; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + if (pObject->Object_Init) { + pObject->Object_Init(); + } + pObject++; + } +} + + diff --git a/bacnet-stack/demo/object/device.c b/bacnet-stack/demo/object/device.c index 6d10a862..75c14b36 100644 --- a/bacnet-stack/demo/object/device.c +++ b/bacnet-stack/demo/object/device.c @@ -1352,7 +1352,7 @@ int Device_Read_Property( } } else { rpdata->error_class = ERROR_CLASS_OBJECT; - rpdata->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; } return apdu_len; @@ -1568,7 +1568,7 @@ bool Device_Write_Property( } } else { wp_data->error_class = ERROR_CLASS_OBJECT; - wp_data->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; } return (status); diff --git a/bacnet-stack/ports/at91sam7s/device.c b/bacnet-stack/ports/at91sam7s/device.c index 02c33e76..a21c24c9 100644 --- a/bacnet-stack/ports/at91sam7s/device.c +++ b/bacnet-stack/ports/at91sam7s/device.c @@ -244,7 +244,7 @@ int Device_Read_Property( } } else { rpdata->error_class = ERROR_CLASS_OBJECT; - rpdata->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; } return apdu_len; @@ -273,7 +273,7 @@ bool Device_Write_Property( } } else { wp_data->error_class = ERROR_CLASS_OBJECT; - wp_data->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; } return status; diff --git a/bacnet-stack/ports/atmega168/h_rp.c b/bacnet-stack/ports/atmega168/h_rp.c index 494891fb..dbb0289e 100644 --- a/bacnet-stack/ports/atmega168/h_rp.c +++ b/bacnet-stack/ports/atmega168/h_rp.c @@ -79,7 +79,7 @@ int Encode_Property_APDU( break; default: *error_class = ERROR_CLASS_OBJECT; - *error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; break; } diff --git a/bacnet-stack/ports/atmega8/h_rp.c b/bacnet-stack/ports/atmega8/h_rp.c index 52c0d325..6b36dd02 100644 --- a/bacnet-stack/ports/atmega8/h_rp.c +++ b/bacnet-stack/ports/atmega8/h_rp.c @@ -87,7 +87,7 @@ int Encode_Property_APDU( #endif default: *error_class = ERROR_CLASS_OBJECT; - *error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; break; } diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/device.c b/bacnet-stack/ports/bdk-atxx4-mstp/device.c index 4be8427f..7b589d27 100644 --- a/bacnet-stack/ports/bdk-atxx4-mstp/device.c +++ b/bacnet-stack/ports/bdk-atxx4-mstp/device.c @@ -246,7 +246,7 @@ int Device_Read_Property( } } else { rpdata->error_class = ERROR_CLASS_OBJECT; - rpdata->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; } return apdu_len; @@ -275,7 +275,7 @@ bool Device_Write_Property( } } else { wp_data->error_class = ERROR_CLASS_OBJECT; - wp_data->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; } return status; diff --git a/bacnet-stack/ports/rx62n/device.c b/bacnet-stack/ports/rx62n/device.c index e0f81fc7..76db5bc2 100644 --- a/bacnet-stack/ports/rx62n/device.c +++ b/bacnet-stack/ports/rx62n/device.c @@ -800,7 +800,7 @@ int Device_Read_Property( } } else { rpdata->error_class = ERROR_CLASS_OBJECT; - rpdata->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; } return apdu_len; @@ -939,7 +939,7 @@ bool Device_Write_Property( } } else { wp_data->error_class = ERROR_CLASS_OBJECT; - wp_data->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; } return status; diff --git a/bacnet-stack/ports/stm32f10x/device.c b/bacnet-stack/ports/stm32f10x/device.c index 2a8f0a5c..ba68765a 100644 --- a/bacnet-stack/ports/stm32f10x/device.c +++ b/bacnet-stack/ports/stm32f10x/device.c @@ -227,7 +227,7 @@ int Device_Read_Property( } } else { rpdata->error_class = ERROR_CLASS_OBJECT; - rpdata->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; } return apdu_len; @@ -256,7 +256,7 @@ bool Device_Write_Property( } } else { wp_data->error_class = ERROR_CLASS_OBJECT; - wp_data->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; } return status;