Edouard@2020: /************************************************************************** Edouard@2020: * Edouard@2020: * Copyright (C) 2005,2006,2009 Steve Karg Edouard@2020: * Copyright (C) 2017 Mario de Sousa Edouard@2020: * Edouard@2020: * Permission is hereby granted, free of charge, to any person obtaining Edouard@2020: * a copy of this software and associated documentation files (the Edouard@2020: * "Software"), to deal in the Software without restriction, including Edouard@2020: * without limitation the rights to use, copy, modify, merge, publish, Edouard@2020: * distribute, sublicense, and/or sell copies of the Software, and to Edouard@2020: * permit persons to whom the Software is furnished to do so, subject to Edouard@2020: * the following conditions: Edouard@2020: * Edouard@2020: * The above copyright notice and this permission notice shall be included Edouard@2020: * in all copies or substantial portions of the Software. Edouard@2020: * Edouard@2020: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, Edouard@2020: * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF Edouard@2020: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. Edouard@2020: * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY Edouard@2020: * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, Edouard@2020: * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE Edouard@2020: * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Edouard@2020: * Edouard@2020: *********************************************************************/ Edouard@2020: Edouard@2020: /** Base "class" for handling all BACnet objects belonging Edouard@2020: * to a BACnet device, as well as Device-specific properties. */ Edouard@2020: Edouard@2020: #include Edouard@2020: #include Edouard@2020: #include /* for memmove */ Edouard@2020: #include /* for timezone, localtime */ Edouard@2020: Edouard@2020: #include "config_bacnet_for_beremiz_%(locstr)s.h" /* the custom configuration for beremiz plugin */ Edouard@2020: #include "bacdef.h" Edouard@2020: #include "bacdcode.h" Edouard@2020: #include "bacenum.h" Edouard@2020: #include "bacapp.h" Edouard@2020: #include "apdu.h" Edouard@2020: #include "wp.h" /* WriteProperty handling */ Edouard@2020: #include "rp.h" /* ReadProperty handling */ Edouard@2020: #include "dcc.h" /* DeviceCommunicationControl handling */ Edouard@2020: #include "version.h" Edouard@2020: #include "device_%(locstr)s.h" /* me */ Edouard@2020: #include "handlers.h" Edouard@2020: #include "datalink.h" Edouard@2020: #include "address.h" Edouard@2020: /* os specfic includes */ Edouard@2020: #include "timer.h" Edouard@2020: /* include the device object */ Edouard@2020: #include "device_%(locstr)s.h" Edouard@2020: #include "ai_%(locstr)s.h" Edouard@2020: #include "ao_%(locstr)s.h" Edouard@2020: #include "av_%(locstr)s.h" Edouard@2020: #include "bi_%(locstr)s.h" Edouard@2020: #include "bo_%(locstr)s.h" Edouard@2020: #include "bv_%(locstr)s.h" Edouard@2020: #include "msi_%(locstr)s.h" Edouard@2020: #include "mso_%(locstr)s.h" Edouard@2020: #include "msv_%(locstr)s.h" Edouard@2020: Edouard@2020: Edouard@2020: #if defined(__BORLANDC__) || defined(_WIN32) Edouard@2020: /* Not included in time.h as specified by The Open Group */ Edouard@2020: /* Difference from UTC and local standard time */ Edouard@2020: long int timezone; Edouard@2020: #endif Edouard@2020: Edouard@2020: /* local forward (semi-private) and external prototypes */ Edouard@2020: int Device_Read_Property_Local( Edouard@2020: BACNET_READ_PROPERTY_DATA * rpdata); Edouard@2020: bool Device_Write_Property_Local( Edouard@2020: BACNET_WRITE_PROPERTY_DATA * wp_data); Edouard@2020: extern int Routed_Device_Read_Property_Local( Edouard@2020: BACNET_READ_PROPERTY_DATA * rpdata); Edouard@2020: extern bool Routed_Device_Write_Property_Local( Edouard@2020: BACNET_WRITE_PROPERTY_DATA * wp_data); Edouard@2020: Edouard@2020: /* may be overridden by outside table */ Edouard@2020: static object_functions_t *Object_Table; Edouard@2020: Edouard@2020: static object_functions_t My_Object_Table[] = { Edouard@2020: {OBJECT_DEVICE, Edouard@2020: NULL /* Init - don't init Device or it will recurse! */ , Edouard@2020: Device_Count, Edouard@2020: Device_Index_To_Instance, Edouard@2020: Device_Valid_Object_Instance_Number, Edouard@2020: Device_Object_Name, Edouard@2020: Device_Read_Property_Local, Edouard@2020: Device_Write_Property_Local, Edouard@2020: Device_Property_Lists, Edouard@2020: DeviceGetRRInfo, Edouard@2020: NULL /* Iterator */ , Edouard@2020: NULL /* Value_Lists */ , Edouard@2020: NULL /* COV */ , Edouard@2020: NULL /* COV Clear */ , Edouard@2020: NULL /* Intrinsic Reporting */ }, Edouard@2020: {OBJECT_ANALOG_INPUT, Edouard@2020: Analog_Input_Init, Edouard@2020: Analog_Input_Count, Edouard@2020: Analog_Input_Index_To_Instance, Edouard@2020: Analog_Input_Valid_Instance, Edouard@2020: Analog_Input_Object_Name, Edouard@2020: Analog_Input_Read_Property, Edouard@2020: Analog_Input_Write_Property, Edouard@2020: Analog_Input_Property_Lists, Edouard@2020: NULL /* ReadRangeInfo */ , Edouard@2020: NULL /* Iterator */ , Edouard@2020: NULL /* Value_Lists */ , Edouard@2020: NULL /* COV */ , Edouard@2020: NULL /* COV Clear */ , Edouard@2020: NULL /* Intrinsic Reporting */ }, Edouard@2020: {OBJECT_ANALOG_OUTPUT, Edouard@2020: Analog_Output_Init, Edouard@2020: Analog_Output_Count, Edouard@2020: Analog_Output_Index_To_Instance, Edouard@2020: Analog_Output_Valid_Instance, Edouard@2020: Analog_Output_Object_Name, Edouard@2020: Analog_Output_Read_Property, Edouard@2020: Analog_Output_Write_Property, Edouard@2020: Analog_Output_Property_Lists, Edouard@2020: NULL /* ReadRangeInfo */ , Edouard@2020: NULL /* Iterator */ , Edouard@2020: NULL /* Value_Lists */ , Edouard@2020: NULL /* COV */ , Edouard@2020: NULL /* COV Clear */ , Edouard@2020: NULL /* Intrinsic Reporting */ }, Edouard@2020: {OBJECT_ANALOG_VALUE, Edouard@2020: Analog_Value_Init, Edouard@2020: Analog_Value_Count, Edouard@2020: Analog_Value_Index_To_Instance, Edouard@2020: Analog_Value_Valid_Instance, Edouard@2020: Analog_Value_Object_Name, Edouard@2020: Analog_Value_Read_Property, Edouard@2020: Analog_Value_Write_Property, Edouard@2020: Analog_Value_Property_Lists, Edouard@2020: NULL /* ReadRangeInfo */ , Edouard@2020: NULL /* Iterator */ , Edouard@2020: NULL /* Value_Lists */ , Edouard@2020: NULL /* COV */ , Edouard@2020: NULL /* COV Clear */ , Edouard@2020: NULL /* Intrinsic Reporting */ }, Edouard@2020: {OBJECT_BINARY_INPUT, Edouard@2020: Binary_Input_Init, Edouard@2020: Binary_Input_Count, Edouard@2020: Binary_Input_Index_To_Instance, Edouard@2020: Binary_Input_Valid_Instance, Edouard@2020: Binary_Input_Object_Name, Edouard@2020: Binary_Input_Read_Property, Edouard@2020: Binary_Input_Write_Property, Edouard@2020: Binary_Input_Property_Lists, Edouard@2020: NULL /* ReadRangeInfo */ , Edouard@2020: NULL /* Iterator */ , Edouard@2020: NULL /* Value_Lists */ , Edouard@2020: NULL /* COV */ , Edouard@2020: NULL /* COV Clear */ , Edouard@2020: NULL /* Intrinsic Reporting */ }, Edouard@2020: {OBJECT_BINARY_OUTPUT, Edouard@2020: Binary_Output_Init, Edouard@2020: Binary_Output_Count, Edouard@2020: Binary_Output_Index_To_Instance, Edouard@2020: Binary_Output_Valid_Instance, Edouard@2020: Binary_Output_Object_Name, Edouard@2020: Binary_Output_Read_Property, Edouard@2020: Binary_Output_Write_Property, Edouard@2020: Binary_Output_Property_Lists, Edouard@2020: NULL /* ReadRangeInfo */ , Edouard@2020: NULL /* Iterator */ , Edouard@2020: NULL /* Value_Lists */ , Edouard@2020: NULL /* COV */ , Edouard@2020: NULL /* COV Clear */ , Edouard@2020: NULL /* Intrinsic Reporting */ }, Edouard@2020: {OBJECT_BINARY_VALUE, Edouard@2020: Binary_Value_Init, Edouard@2020: Binary_Value_Count, Edouard@2020: Binary_Value_Index_To_Instance, Edouard@2020: Binary_Value_Valid_Instance, Edouard@2020: Binary_Value_Object_Name, Edouard@2020: Binary_Value_Read_Property, Edouard@2020: Binary_Value_Write_Property, Edouard@2020: Binary_Value_Property_Lists, Edouard@2020: NULL /* ReadRangeInfo */ , Edouard@2020: NULL /* Iterator */ , Edouard@2020: NULL /* Value_Lists */ , Edouard@2020: NULL /* COV */ , Edouard@2020: NULL /* COV Clear */ , Edouard@2020: NULL /* Intrinsic Reporting */ }, Edouard@2020: {OBJECT_MULTI_STATE_INPUT, Edouard@2020: Multistate_Input_Init, Edouard@2020: Multistate_Input_Count, Edouard@2020: Multistate_Input_Index_To_Instance, Edouard@2020: Multistate_Input_Valid_Instance, Edouard@2020: Multistate_Input_Object_Name, Edouard@2020: Multistate_Input_Read_Property, Edouard@2020: Multistate_Input_Write_Property, Edouard@2020: Multistate_Input_Property_Lists, Edouard@2020: NULL /* ReadRangeInfo */ , Edouard@2020: NULL /* Iterator */ , Edouard@2020: NULL /* Value_Lists */ , Edouard@2020: NULL /* COV */ , Edouard@2020: NULL /* COV Clear */ , Edouard@2020: NULL /* Intrinsic Reporting */ }, Edouard@2020: {OBJECT_MULTI_STATE_OUTPUT, Edouard@2020: Multistate_Output_Init, Edouard@2020: Multistate_Output_Count, Edouard@2020: Multistate_Output_Index_To_Instance, Edouard@2020: Multistate_Output_Valid_Instance, Edouard@2020: Multistate_Output_Object_Name, Edouard@2020: Multistate_Output_Read_Property, Edouard@2020: Multistate_Output_Write_Property, Edouard@2020: Multistate_Output_Property_Lists, Edouard@2020: NULL /* ReadRangeInfo */ , Edouard@2020: NULL /* Iterator */ , Edouard@2020: NULL /* Value_Lists */ , Edouard@2020: NULL /* COV */ , Edouard@2020: NULL /* COV Clear */ , Edouard@2020: NULL /* Intrinsic Reporting */ }, Edouard@2020: {OBJECT_MULTI_STATE_VALUE, Edouard@2020: Multistate_Value_Init, Edouard@2020: Multistate_Value_Count, Edouard@2020: Multistate_Value_Index_To_Instance, Edouard@2020: Multistate_Value_Valid_Instance, Edouard@2020: Multistate_Value_Object_Name, Edouard@2020: Multistate_Value_Read_Property, Edouard@2020: Multistate_Value_Write_Property, Edouard@2020: Multistate_Value_Property_Lists, Edouard@2020: NULL /* ReadRangeInfo */ , Edouard@2020: NULL /* Iterator */ , Edouard@2020: NULL /* Value_Lists */ , Edouard@2020: NULL /* COV */ , Edouard@2020: NULL /* COV Clear */ , Edouard@2020: NULL /* Intrinsic Reporting */ }, Edouard@2020: {MAX_BACNET_OBJECT_TYPE, Edouard@2020: NULL /* Init */ , Edouard@2020: NULL /* Count */ , Edouard@2020: NULL /* Index_To_Instance */ , Edouard@2020: NULL /* Valid_Instance */ , Edouard@2020: NULL /* Object_Name */ , Edouard@2020: NULL /* Read_Property */ , Edouard@2020: NULL /* Write_Property */ , Edouard@2020: NULL /* Property_Lists */ , Edouard@2020: NULL /* ReadRangeInfo */ , Edouard@2020: NULL /* Iterator */ , Edouard@2020: NULL /* Value_Lists */ , Edouard@2020: NULL /* COV */ , Edouard@2020: NULL /* COV Clear */ , Edouard@2020: NULL /* Intrinsic Reporting */ } Edouard@2020: }; Edouard@2020: Edouard@2020: /** Glue function to let the Device object, when called by a handler, Edouard@2020: * lookup which Object type needs to be invoked. Edouard@2020: * param: Object_Type [in] The type of BACnet Object the handler wants to access. Edouard@2020: * return: Pointer to the group of object helper functions that implement this Edouard@2020: * type of Object. Edouard@2020: */ Edouard@2020: static struct object_functions *Device_Objects_Find_Functions( Edouard@2020: BACNET_OBJECT_TYPE Object_Type) Edouard@2020: { Edouard@2020: struct object_functions *pObject = NULL; Edouard@2020: Edouard@2020: pObject = Object_Table; Edouard@2020: while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { Edouard@2020: /* handle each object type */ Edouard@2020: if (pObject->Object_Type == Object_Type) { Edouard@2020: return (pObject); Edouard@2020: } Edouard@2020: pObject++; Edouard@2020: } Edouard@2020: Edouard@2020: return (NULL); Edouard@2020: } Edouard@2020: Edouard@2020: /** Try to find a rr_info_function helper function for the requested object type. Edouard@2020: * Edouard@2020: * param: object_type [in] The type of BACnet Object the handler wants to access. Edouard@2020: * return: Pointer to the object helper function that implements the Edouard@2020: * ReadRangeInfo function, Object_RR_Info, for this type of Object on Edouard@2020: * success, else a NULL pointer if the type of Object isn't supported Edouard@2020: * or doesn't have a ReadRangeInfo function. Edouard@2020: */ Edouard@2020: rr_info_function Device_Objects_RR_Info( Edouard@2020: BACNET_OBJECT_TYPE object_type) Edouard@2020: { Edouard@2020: struct object_functions *pObject = NULL; Edouard@2020: Edouard@2020: pObject = Device_Objects_Find_Functions(object_type); Edouard@2020: return (pObject != NULL ? pObject->Object_RR_Info : NULL); Edouard@2020: } Edouard@2020: Edouard@2020: /** For a given object type, returns the special property list. Edouard@2020: * This function is used for ReadPropertyMultiple calls which want Edouard@2020: * just Required, just Optional, or All properties. Edouard@2020: * Edouard@2020: * param: object_type [in] The desired BACNET_OBJECT_TYPE whose properties Edouard@2020: * are to be listed. Edouard@2020: * param: pPropertyList [out] Reference to the structure which will, on return, Edouard@2020: * list, separately, the Required, Optional, and Proprietary object Edouard@2020: * properties with their counts. Edouard@2020: */ Edouard@2020: void Device_Objects_Property_List( Edouard@2020: BACNET_OBJECT_TYPE object_type, Edouard@2020: struct special_property_list_t *pPropertyList) Edouard@2020: { Edouard@2020: struct object_functions *pObject = NULL; Edouard@2020: Edouard@2020: pPropertyList->Required.pList = NULL; Edouard@2020: pPropertyList->Optional.pList = NULL; Edouard@2020: pPropertyList->Proprietary.pList = NULL; Edouard@2020: Edouard@2020: /* If we can find an entry for the required object type Edouard@2020: * and there is an Object_List_RPM fn ptr then call it Edouard@2020: * to populate the pointers to the individual list counters. Edouard@2020: */ Edouard@2020: Edouard@2020: pObject = Device_Objects_Find_Functions(object_type); Edouard@2020: if ((pObject != NULL) && (pObject->Object_RPM_List != NULL)) { Edouard@2020: pObject->Object_RPM_List(&pPropertyList->Required.pList, Edouard@2020: &pPropertyList->Optional.pList, &pPropertyList->Proprietary.pList); Edouard@2020: } Edouard@2020: Edouard@2020: /* Fetch the counts if available otherwise zero them */ Edouard@2020: pPropertyList->Required.count = Edouard@2020: pPropertyList->Required.pList == Edouard@2020: NULL ? 0 : property_list_count(pPropertyList->Required.pList); Edouard@2020: Edouard@2020: pPropertyList->Optional.count = Edouard@2020: pPropertyList->Optional.pList == Edouard@2020: NULL ? 0 : property_list_count(pPropertyList->Optional.pList); Edouard@2020: Edouard@2020: pPropertyList->Proprietary.count = Edouard@2020: pPropertyList->Proprietary.pList == Edouard@2020: NULL ? 0 : property_list_count(pPropertyList->Proprietary.pList); Edouard@2020: Edouard@2020: return; Edouard@2020: } Edouard@2020: Edouard@2020: /** Commands a Device re-initialization, to a given state. Edouard@2020: * The request's password must match for the operation to succeed. Edouard@2020: * This implementation provides a framework, but doesn't Edouard@2020: * actually *DO* anything. Edouard@2020: * @note You could use a mix of states and passwords to multiple outcomes. Edouard@2020: * @note You probably want to restart *after* the simple ack has been sent Edouard@2020: * from the return handler, so just set a local flag here. Edouard@2020: * Edouard@2020: * param: rd_data [in,out] The information from the RD request. Edouard@2020: * On failure, the error class and code will be set. Edouard@2020: * return: True if succeeds (password is correct), else False. Edouard@2020: */ Edouard@2020: bool Device_Reinitialize( Edouard@2020: BACNET_REINITIALIZE_DEVICE_DATA * rd_data) Edouard@2020: { Edouard@2020: bool status = false; Edouard@2020: Edouard@2020: if (characterstring_ansi_same(&rd_data->password, "Jesus")) { Edouard@2020: switch (rd_data->state) { Edouard@2020: case BACNET_REINIT_COLDSTART: Edouard@2020: case BACNET_REINIT_WARMSTART: Edouard@2020: dcc_set_status_duration(COMMUNICATION_ENABLE, 0); Edouard@2020: break; Edouard@2020: case BACNET_REINIT_STARTBACKUP: Edouard@2020: break; Edouard@2020: case BACNET_REINIT_ENDBACKUP: Edouard@2020: break; Edouard@2020: case BACNET_REINIT_STARTRESTORE: Edouard@2020: break; Edouard@2020: case BACNET_REINIT_ENDRESTORE: Edouard@2020: break; Edouard@2020: case BACNET_REINIT_ABORTRESTORE: Edouard@2020: break; Edouard@2020: default: Edouard@2020: break; Edouard@2020: } Edouard@2020: /* Note: you could use a mix of state Edouard@2020: and password to multiple things */ Edouard@2020: /* note: you probably want to restart *after* the Edouard@2020: simple ack has been sent from the return handler Edouard@2020: so just set a flag from here */ Edouard@2020: status = true; Edouard@2020: } else { Edouard@2020: rd_data->error_class = ERROR_CLASS_SECURITY; Edouard@2020: rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE; Edouard@2020: } Edouard@2020: Edouard@2020: return status; Edouard@2020: } Edouard@2020: Edouard@2020: /* These three arrays are used by the ReadPropertyMultiple handler, Edouard@2020: * as well as to initialize the XXX_Property_List used by the Edouard@2020: * Property List (PROP_PROPERTY_LIST) property. Edouard@2020: */ Edouard@2020: static const int Device_Properties_Required[] = { Edouard@2020: /* (1) Currently Supported */ Edouard@2020: /* (2) Required by standard ASHRAE 135-2016 */ Edouard@2020: /*(1)(2) */ Edouard@2020: PROP_OBJECT_IDENTIFIER, /* W R ( 75) */ Edouard@2020: PROP_OBJECT_NAME, /* W R ( 77) */ Edouard@2020: PROP_OBJECT_TYPE, /* R R ( 79) */ Edouard@2020: PROP_SYSTEM_STATUS, /* R R (112) */ Edouard@2020: PROP_VENDOR_NAME, /* R R (121) */ Edouard@2020: PROP_VENDOR_IDENTIFIER, /* W R (120) */ Edouard@2020: PROP_MODEL_NAME, /* W R ( 70) */ Edouard@2020: PROP_FIRMWARE_REVISION, /* R R ( 44) */ Edouard@2020: PROP_APPLICATION_SOFTWARE_VERSION, /* R R ( 12) */ Edouard@2020: PROP_PROTOCOL_VERSION, /* R R ( 98) */ Edouard@2020: PROP_PROTOCOL_REVISION, /* R R (139) */ Edouard@2020: PROP_PROTOCOL_SERVICES_SUPPORTED, /* R R ( 97) */ Edouard@2020: PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, /* R R ( 96) */ Edouard@2020: PROP_OBJECT_LIST, /* R R ( 76) */ Edouard@2020: PROP_MAX_APDU_LENGTH_ACCEPTED, /* R R ( 62) */ Edouard@2020: PROP_SEGMENTATION_SUPPORTED, /* R R (107) */ Edouard@2020: PROP_APDU_TIMEOUT, /* W R ( 11) */ Edouard@2020: PROP_NUMBER_OF_APDU_RETRIES, /* W R ( 73) */ Edouard@2020: PROP_DEVICE_ADDRESS_BINDING, /* R R ( 30) */ Edouard@2020: PROP_DATABASE_REVISION, /* R R (155) */ Edouard@2020: // PROP_PROPERTY_LIST, /* R R (371) */ Edouard@2020: -1 Edouard@2020: }; Edouard@2020: Edouard@2020: static const int Device_Properties_Optional[] = { Edouard@2020: PROP_DESCRIPTION, /* W O ( 28) */ Edouard@2020: PROP_LOCAL_TIME, /* R O ( 57) */ Edouard@2020: PROP_UTC_OFFSET, /* R O (119) */ Edouard@2020: PROP_LOCAL_DATE, /* R O ( 56) */ Edouard@2020: PROP_DAYLIGHT_SAVINGS_STATUS, /* R O ( 24) */ Edouard@2020: PROP_LOCATION, /* W O ( 58) */ Edouard@2020: -1 Edouard@2020: }; Edouard@2020: Edouard@2020: static const int Device_Properties_Proprietary[] = { Edouard@2020: -1 Edouard@2020: }; Edouard@2020: Edouard@2020: /* This array stores the PROPERTY_LIST which may be read by clients. Edouard@2020: * End of list is marked by following the last element with the value '-1' Edouard@2020: * Edouard@2020: * It is initialized by Binary_Value_Init() based off the values Edouard@2020: * stored in Binary_Value_Properties_Required Edouard@2020: * Binary_Value_Properties_Optional Edouard@2020: * Binary_Value_Properties_Proprietary Edouard@2020: */ Edouard@2020: /* TODO: Allocate memory for this array with malloc() at startup */ Edouard@2020: static int Device_Properties_List[64]; Edouard@2020: Edouard@2020: Edouard@2020: void Device_Property_Lists( Edouard@2020: const int **pRequired, Edouard@2020: const int **pOptional, Edouard@2020: const int **pProprietary) Edouard@2020: { Edouard@2020: if (pRequired) Edouard@2020: *pRequired = Device_Properties_Required; Edouard@2020: if (pOptional) Edouard@2020: *pOptional = Device_Properties_Optional; Edouard@2020: if (pProprietary) Edouard@2020: *pProprietary = Device_Properties_Proprietary; Edouard@2020: Edouard@2020: return; Edouard@2020: } Edouard@2020: Edouard@2020: Edouard@2020: static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; Edouard@2020: Edouard@2020: static BACNET_CHARACTER_STRING My_Object_Name; Edouard@2020: static uint32_t Object_Instance_Number = 260001; Edouard@2020: static uint16_t Vendor_Identifier = BACNET_VENDOR_ID; Edouard@2020: static char *Vendor_Name = BACNET_VENDOR_NAME; Edouard@2020: static char *Firmware_Revision = BACNET_FIRMWARE_REVISION; Edouard@2020: static char Model_Name [MAX_DEV_MOD_LEN + 1] = BACNET_DEVICE_MODEL_NAME; Edouard@2020: static char Application_Software_Version[MAX_DEV_VER_LEN + 1] = BACNET_DEVICE_APPSOFT_VER; Edouard@2020: static char Location [MAX_DEV_LOC_LEN + 1] = BACNET_DEVICE_LOCATION; Edouard@2020: static char Description [MAX_DEV_DESC_LEN + 1] = BACNET_DEVICE_DESCRIPTION; Edouard@2020: /* static uint8_t Protocol_Version = 1; - constant, not settable */ Edouard@2020: /* static uint8_t Protocol_Revision = 4; - constant, not settable */ Edouard@2020: /* Protocol_Services_Supported - dynamically generated */ Edouard@2020: /* Protocol_Object_Types_Supported - in RP encoding */ Edouard@2020: /* Object_List - dynamically generated */ Edouard@2020: /* static BACNET_SEGMENTATION Segmentation_Supported = SEGMENTATION_NONE; */ Edouard@2020: /* static uint8_t Max_Segments_Accepted = 0; */ Edouard@2020: /* VT_Classes_Supported */ Edouard@2020: /* Active_VT_Sessions */ Edouard@2020: static BACNET_TIME Local_Time; /* rely on OS, if there is one */ Edouard@2020: static BACNET_DATE Local_Date; /* rely on OS, if there is one */ Edouard@2020: /* NOTE: BACnet UTC Offset is inverse of common practice. Edouard@2020: If your UTC offset is -5hours of GMT, Edouard@2020: then BACnet UTC offset is +5hours. Edouard@2020: BACnet UTC offset is expressed in minutes. */ Edouard@2020: static int32_t UTC_Offset = 5 * 60; Edouard@2020: static bool Daylight_Savings_Status = false; /* rely on OS */ Edouard@2020: /* List_Of_Session_Keys */ Edouard@2020: /* Time_Synchronization_Recipients */ Edouard@2020: /* Max_Master - rely on MS/TP subsystem, if there is one */ Edouard@2020: /* Max_Info_Frames - rely on MS/TP subsystem, if there is one */ Edouard@2020: /* Device_Address_Binding - required, but relies on binding cache */ Edouard@2020: static uint32_t Database_Revision = 0; Edouard@2020: /* Configuration_Files */ Edouard@2020: /* Last_Restore_Time */ Edouard@2020: /* Backup_Failure_Timeout */ Edouard@2020: /* Active_COV_Subscriptions */ Edouard@2020: /* Slave_Proxy_Enable */ Edouard@2020: /* Manual_Slave_Address_Binding */ Edouard@2020: /* Auto_Slave_Discovery */ Edouard@2020: /* Slave_Address_Binding */ Edouard@2020: /* Profile_Name */ Edouard@2020: Edouard@2020: unsigned Device_Count( Edouard@2020: void) Edouard@2020: { Edouard@2020: return 1; Edouard@2020: } Edouard@2020: Edouard@2020: uint32_t Device_Index_To_Instance( Edouard@2020: unsigned index) Edouard@2020: { Edouard@2020: index = index; Edouard@2020: return Object_Instance_Number; Edouard@2020: } Edouard@2020: Edouard@2020: /* methods to manipulate the data */ Edouard@2020: Edouard@2020: /** Return the Object Instance number for our (single) Device Object. Edouard@2020: * This is a key function, widely invoked by the handler code, since Edouard@2020: * it provides "our" (ie, local) address. Edouard@2020: * return: The Instance number used in the BACNET_OBJECT_ID for the Device. Edouard@2020: */ Edouard@2020: uint32_t Device_Object_Instance_Number( Edouard@2020: void) Edouard@2020: { Edouard@2020: return Object_Instance_Number; Edouard@2020: } Edouard@2020: Edouard@2020: bool Device_Set_Object_Instance_Number( Edouard@2020: uint32_t object_id) Edouard@2020: { Edouard@2020: bool status = true; /* return value */ Edouard@2020: Edouard@2020: if (object_id <= BACNET_MAX_INSTANCE) { Edouard@2020: /* Make the change and update the database revision */ Edouard@2020: Object_Instance_Number = object_id; Edouard@2020: Device_Inc_Database_Revision(); Edouard@2020: } else Edouard@2020: status = false; Edouard@2020: Edouard@2020: return status; Edouard@2020: } Edouard@2020: Edouard@2020: bool Device_Valid_Object_Instance_Number( Edouard@2020: uint32_t object_id) Edouard@2020: { Edouard@2020: return (Object_Instance_Number == object_id); Edouard@2020: } Edouard@2020: Edouard@2020: bool Device_Object_Name( Edouard@2020: uint32_t object_instance, Edouard@2020: BACNET_CHARACTER_STRING * object_name) Edouard@2020: { Edouard@2020: bool status = false; Edouard@2020: Edouard@2020: if (object_instance == Object_Instance_Number) { Edouard@2020: status = characterstring_copy(object_name, &My_Object_Name); Edouard@2020: } Edouard@2020: Edouard@2020: return status; Edouard@2020: } Edouard@2020: Edouard@2020: bool Device_Set_Object_Name( Edouard@2020: BACNET_CHARACTER_STRING * object_name) Edouard@2020: { Edouard@2020: bool status = false; /*return value */ Edouard@2020: Edouard@2020: if (!characterstring_same(&My_Object_Name, object_name)) { Edouard@2020: /* Make the change and update the database revision */ Edouard@2020: status = characterstring_copy(&My_Object_Name, object_name); Edouard@2020: Device_Inc_Database_Revision(); Edouard@2020: } Edouard@2020: Edouard@2020: return status; Edouard@2020: } Edouard@2020: Edouard@2020: BACNET_DEVICE_STATUS Device_System_Status( Edouard@2020: void) Edouard@2020: { Edouard@2020: return System_Status; Edouard@2020: } Edouard@2020: Edouard@2020: int Device_Set_System_Status( Edouard@2020: BACNET_DEVICE_STATUS status, Edouard@2020: bool local) Edouard@2020: { Edouard@2020: int result = 0; /*return value - 0 = ok, -1 = bad value, -2 = not allowed */ Edouard@2020: Edouard@2020: /* We limit the options available depending on whether the source is Edouard@2020: * internal or external. */ Edouard@2020: if (local) { Edouard@2020: switch (status) { Edouard@2020: case STATUS_OPERATIONAL: Edouard@2020: case STATUS_OPERATIONAL_READ_ONLY: Edouard@2020: case STATUS_DOWNLOAD_REQUIRED: Edouard@2020: case STATUS_DOWNLOAD_IN_PROGRESS: Edouard@2020: case STATUS_NON_OPERATIONAL: Edouard@2020: System_Status = status; Edouard@2020: break; Edouard@2020: Edouard@2020: /* Don't support backup at present so don't allow setting */ Edouard@2020: case STATUS_BACKUP_IN_PROGRESS: Edouard@2020: result = -2; Edouard@2020: break; Edouard@2020: Edouard@2020: default: Edouard@2020: result = -1; Edouard@2020: break; Edouard@2020: } Edouard@2020: } else { Edouard@2020: switch (status) { Edouard@2020: /* Allow these for the moment as a way to easily alter Edouard@2020: * overall device operation. The lack of password protection Edouard@2020: * or other authentication makes allowing writes to this Edouard@2020: * property a risky facility to provide. Edouard@2020: */ Edouard@2020: case STATUS_OPERATIONAL: Edouard@2020: case STATUS_OPERATIONAL_READ_ONLY: Edouard@2020: case STATUS_NON_OPERATIONAL: Edouard@2020: System_Status = status; Edouard@2020: break; Edouard@2020: Edouard@2020: /* Don't allow outsider set this - it should probably Edouard@2020: * be set if the device config is incomplete or Edouard@2020: * corrupted or perhaps after some sort of operator Edouard@2020: * wipe operation. Edouard@2020: */ Edouard@2020: case STATUS_DOWNLOAD_REQUIRED: Edouard@2020: /* Don't allow outsider set this - it should be set Edouard@2020: * internally at the start of a multi packet download Edouard@2020: * perhaps indirectly via PT or WF to a config file. Edouard@2020: */ Edouard@2020: case STATUS_DOWNLOAD_IN_PROGRESS: Edouard@2020: /* Don't support backup at present so don't allow setting */ Edouard@2020: case STATUS_BACKUP_IN_PROGRESS: Edouard@2020: result = -2; Edouard@2020: break; Edouard@2020: Edouard@2020: default: Edouard@2020: result = -1; Edouard@2020: break; Edouard@2020: } Edouard@2020: } Edouard@2020: Edouard@2020: return (result); Edouard@2020: } Edouard@2020: Edouard@2020: const char *Device_Vendor_Name( Edouard@2020: void) Edouard@2020: { Edouard@2020: return Vendor_Name; Edouard@2020: } Edouard@2020: Edouard@2020: /** Returns the Vendor ID for this Device. Edouard@2020: * See the assignments at http://www.bacnet.org/VendorID/BACnet%%20Vendor%%20IDs.htm Edouard@2020: * return: The Vendor ID of this Device. Edouard@2020: */ Edouard@2020: uint16_t Device_Vendor_Identifier( Edouard@2020: void) Edouard@2020: { Edouard@2020: return Vendor_Identifier; Edouard@2020: } Edouard@2020: Edouard@2020: void Device_Set_Vendor_Identifier( Edouard@2020: uint16_t vendor_id) Edouard@2020: { Edouard@2020: Vendor_Identifier = vendor_id; Edouard@2020: } Edouard@2020: Edouard@2020: const char *Device_Model_Name( Edouard@2020: void) Edouard@2020: { Edouard@2020: return Model_Name; Edouard@2020: } Edouard@2020: Edouard@2020: bool Device_Set_Model_Name( Edouard@2020: const char *name, Edouard@2020: size_t length) Edouard@2020: { Edouard@2020: bool status = false; /*return value */ Edouard@2020: Edouard@2020: if (length < sizeof(Model_Name)) { Edouard@2020: memmove(Model_Name, name, length); Edouard@2020: Model_Name[length] = 0; Edouard@2020: status = true; Edouard@2020: } Edouard@2020: Edouard@2020: return status; Edouard@2020: } Edouard@2020: Edouard@2020: const char *Device_Firmware_Revision( Edouard@2020: void) Edouard@2020: { Edouard@2020: return Firmware_Revision; Edouard@2020: } Edouard@2020: Edouard@2020: const char *Device_Application_Software_Version( Edouard@2020: void) Edouard@2020: { Edouard@2020: return Application_Software_Version; Edouard@2020: } Edouard@2020: Edouard@2020: bool Device_Set_Application_Software_Version( Edouard@2020: const char *name, Edouard@2020: size_t length) Edouard@2020: { Edouard@2020: bool status = false; /*return value */ Edouard@2020: Edouard@2020: if (length < sizeof(Application_Software_Version)) { Edouard@2020: memmove(Application_Software_Version, name, length); Edouard@2020: Application_Software_Version[length] = 0; Edouard@2020: status = true; Edouard@2020: } Edouard@2020: Edouard@2020: return status; Edouard@2020: } Edouard@2020: Edouard@2020: const char *Device_Description( Edouard@2020: void) Edouard@2020: { Edouard@2020: return Description; Edouard@2020: } Edouard@2020: Edouard@2020: bool Device_Set_Description( Edouard@2020: const char *name, Edouard@2020: size_t length) Edouard@2020: { Edouard@2020: bool status = false; /*return value */ Edouard@2020: Edouard@2020: if (length < sizeof(Description)) { Edouard@2020: memmove(Description, name, length); Edouard@2020: Description[length] = 0; Edouard@2020: status = true; Edouard@2020: } Edouard@2020: Edouard@2020: return status; Edouard@2020: } Edouard@2020: Edouard@2020: const char *Device_Location( Edouard@2020: void) Edouard@2020: { Edouard@2020: return Location; Edouard@2020: } Edouard@2020: Edouard@2020: bool Device_Set_Location( Edouard@2020: const char *name, Edouard@2020: size_t length) Edouard@2020: { Edouard@2020: bool status = false; /*return value */ Edouard@2020: Edouard@2020: if (length < sizeof(Location)) { Edouard@2020: memmove(Location, name, length); Edouard@2020: Location[length] = 0; Edouard@2020: status = true; Edouard@2020: } Edouard@2020: Edouard@2020: return status; Edouard@2020: } Edouard@2020: Edouard@2020: uint8_t Device_Protocol_Version( Edouard@2020: void) Edouard@2020: { Edouard@2020: return BACNET_PROTOCOL_VERSION; Edouard@2020: } Edouard@2020: Edouard@2020: uint8_t Device_Protocol_Revision( Edouard@2020: void) Edouard@2020: { Edouard@2020: return BACNET_PROTOCOL_REVISION; Edouard@2020: } Edouard@2020: Edouard@2020: BACNET_SEGMENTATION Device_Segmentation_Supported( Edouard@2020: void) Edouard@2020: { Edouard@2020: return SEGMENTATION_NONE; Edouard@2020: } Edouard@2020: Edouard@2020: uint32_t Device_Database_Revision( Edouard@2020: void) Edouard@2020: { Edouard@2020: return Database_Revision; Edouard@2020: } Edouard@2020: Edouard@2020: void Device_Set_Database_Revision( Edouard@2020: uint32_t revision) Edouard@2020: { Edouard@2020: Database_Revision = revision; Edouard@2020: } Edouard@2020: Edouard@2020: /* Edouard@2020: * Shortcut for incrementing database revision as this is potentially Edouard@2020: * the most common operation if changing object names and ids is Edouard@2020: * implemented. Edouard@2020: */ Edouard@2020: void Device_Inc_Database_Revision( Edouard@2020: void) Edouard@2020: { Edouard@2020: Database_Revision++; Edouard@2020: } Edouard@2020: Edouard@2020: /** Get the total count of objects supported by this Device Object. Edouard@2020: * @note Since many network clients depend on the object list Edouard@2020: * for discovery, it must be consistent! Edouard@2020: * return: The count of objects, for all supported Object types. Edouard@2020: */ Edouard@2020: unsigned Device_Object_List_Count( Edouard@2020: void) Edouard@2020: { Edouard@2020: unsigned count = 0; /* number of objects */ Edouard@2020: struct object_functions *pObject = NULL; Edouard@2020: Edouard@2020: /* initialize the default return values */ Edouard@2020: pObject = Object_Table; Edouard@2020: while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { Edouard@2020: if (pObject->Object_Count) { Edouard@2020: count += pObject->Object_Count(); Edouard@2020: } Edouard@2020: pObject++; Edouard@2020: } Edouard@2020: Edouard@2020: return count; Edouard@2020: } Edouard@2020: Edouard@2020: /** Lookup the Object at the given array index in the Device's Object List. Edouard@2020: * Even though we don't keep a single linear array of objects in the Device, Edouard@2020: * this method acts as though we do and works through a virtual, concatenated Edouard@2020: * array of all of our object type arrays. Edouard@2020: * Edouard@2020: * param: array_index [in] The desired array index (1 to N) Edouard@2020: * param: object_type [out] The object's type, if found. Edouard@2020: * param: instance [out] The object's instance number, if found. Edouard@2020: * return: True if found, else false. Edouard@2020: */ Edouard@2020: bool Device_Object_List_Identifier( Edouard@2020: unsigned array_index, Edouard@2020: int *object_type, Edouard@2020: uint32_t * instance) Edouard@2020: { Edouard@2020: bool status = false; Edouard@2020: unsigned count = 0; Edouard@2020: unsigned object_index = 0; Edouard@2020: unsigned temp_index = 0; Edouard@2020: struct object_functions *pObject = NULL; Edouard@2020: Edouard@2020: /* array index zero is length - so invalid */ Edouard@2020: if (array_index == 0) { Edouard@2020: return status; Edouard@2020: } Edouard@2020: object_index = array_index - 1; Edouard@2020: /* initialize the default return values */ Edouard@2020: pObject = Object_Table; Edouard@2020: while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { Edouard@2020: if (pObject->Object_Count) { Edouard@2020: object_index -= count; Edouard@2020: count = pObject->Object_Count(); Edouard@2020: if (object_index < count) { Edouard@2020: /* Use the iterator function if available otherwise Edouard@2020: * look for the index to instance to get the ID */ Edouard@2020: if (pObject->Object_Iterator) { Edouard@2020: /* First find the first object */ Edouard@2020: temp_index = pObject->Object_Iterator(~(unsigned) 0); Edouard@2020: /* Then step through the objects to find the nth */ Edouard@2020: while (object_index != 0) { Edouard@2020: temp_index = pObject->Object_Iterator(temp_index); Edouard@2020: object_index--; Edouard@2020: } Edouard@2020: /* set the object_index up before falling through to next bit */ Edouard@2020: object_index = temp_index; Edouard@2020: } Edouard@2020: if (pObject->Object_Index_To_Instance) { Edouard@2020: *object_type = pObject->Object_Type; Edouard@2020: *instance = Edouard@2020: pObject->Object_Index_To_Instance(object_index); Edouard@2020: status = true; Edouard@2020: break; Edouard@2020: } Edouard@2020: } Edouard@2020: } Edouard@2020: pObject++; Edouard@2020: } Edouard@2020: Edouard@2020: return status; Edouard@2020: } Edouard@2020: Edouard@2020: /** Determine if we have an object with the given object_name. Edouard@2020: * If the object_type and object_instance pointers are not null, Edouard@2020: * and the lookup succeeds, they will be given the resulting values. Edouard@2020: * param: object_name [in] The desired Object Name to look for. Edouard@2020: * param: object_type [out] The BACNET_OBJECT_TYPE of the matching Object. Edouard@2020: * param: object_instance [out] The object instance number of the matching Object. Edouard@2020: * return: True on success or else False if not found. Edouard@2020: */ Edouard@2020: bool Device_Valid_Object_Name( Edouard@2020: BACNET_CHARACTER_STRING * object_name1, Edouard@2020: int *object_type, Edouard@2020: uint32_t * object_instance) Edouard@2020: { Edouard@2020: bool found = false; Edouard@2020: int type = 0; Edouard@2020: uint32_t instance; Edouard@2020: unsigned max_objects = 0, i = 0; Edouard@2020: bool check_id = false; Edouard@2020: BACNET_CHARACTER_STRING object_name2; Edouard@2020: struct object_functions *pObject = NULL; Edouard@2020: Edouard@2020: max_objects = Device_Object_List_Count(); Edouard@2020: for (i = 1; i <= max_objects; i++) { Edouard@2020: check_id = Device_Object_List_Identifier(i, &type, &instance); Edouard@2020: if (check_id) { Edouard@2020: pObject = Device_Objects_Find_Functions(type); Edouard@2020: if ((pObject != NULL) && (pObject->Object_Name != NULL) && Edouard@2020: (pObject->Object_Name(instance, &object_name2) && Edouard@2020: characterstring_same(object_name1, &object_name2))) { Edouard@2020: found = true; Edouard@2020: if (object_type) { Edouard@2020: *object_type = type; Edouard@2020: } Edouard@2020: if (object_instance) { Edouard@2020: *object_instance = instance; Edouard@2020: } Edouard@2020: break; Edouard@2020: } Edouard@2020: } Edouard@2020: } Edouard@2020: Edouard@2020: return found; Edouard@2020: } Edouard@2020: Edouard@2020: /** Determine if we have an object of this type and instance number. Edouard@2020: * param: object_type [in] The desired BACNET_OBJECT_TYPE Edouard@2020: * param: object_instance [in] The object instance number to be looked up. Edouard@2020: * return: True if found, else False if no such Object in this device. Edouard@2020: */ Edouard@2020: bool Device_Valid_Object_Id( Edouard@2020: int object_type, Edouard@2020: uint32_t object_instance) Edouard@2020: { Edouard@2020: bool status = false; /* return value */ Edouard@2020: struct object_functions *pObject = NULL; Edouard@2020: Edouard@2020: pObject = Device_Objects_Find_Functions(object_type); Edouard@2020: if ((pObject != NULL) && (pObject->Object_Valid_Instance != NULL)) { Edouard@2020: status = pObject->Object_Valid_Instance(object_instance); Edouard@2020: } Edouard@2020: Edouard@2020: return status; Edouard@2020: } Edouard@2020: Edouard@2020: /** Copy a child object's object_name value, given its ID. Edouard@2020: * param: object_type [in] The BACNET_OBJECT_TYPE of the child Object. Edouard@2020: * param: object_instance [in] The object instance number of the child Object. Edouard@2020: * param: object_name [out] The Object Name found for this child Object. Edouard@2020: * return: True on success or else False if not found. Edouard@2020: */ Edouard@2020: bool Device_Object_Name_Copy( Edouard@2020: BACNET_OBJECT_TYPE object_type, Edouard@2020: uint32_t object_instance, Edouard@2020: BACNET_CHARACTER_STRING * object_name) Edouard@2020: { Edouard@2020: struct object_functions *pObject = NULL; Edouard@2020: bool found = false; Edouard@2020: Edouard@2020: pObject = Device_Objects_Find_Functions(object_type); Edouard@2020: if ((pObject != NULL) && (pObject->Object_Name != NULL)) { Edouard@2020: found = pObject->Object_Name(object_instance, object_name); Edouard@2020: } Edouard@2020: Edouard@2020: return found; Edouard@2020: } Edouard@2020: Edouard@2020: static void Device_Update_Current_Time( Edouard@2020: void) Edouard@2020: { Edouard@2020: struct tm *tblock = NULL; Edouard@2020: #if defined(_MSC_VER) Edouard@2020: time_t tTemp; Edouard@2020: #else Edouard@2020: struct timeval tv; Edouard@2020: #endif Edouard@2020: /* Edouard@2020: struct tm Edouard@2020: Edouard@2020: int tm_sec Seconds [0,60]. Edouard@2020: int tm_min Minutes [0,59]. Edouard@2020: int tm_hour Hour [0,23]. Edouard@2020: int tm_mday Day of month [1,31]. Edouard@2020: int tm_mon Month of year [0,11]. Edouard@2020: int tm_year Years since 1900. Edouard@2020: int tm_wday Day of week [0,6] (Sunday =0). Edouard@2020: int tm_yday Day of year [0,365]. Edouard@2020: int tm_isdst Daylight Savings flag. Edouard@2020: */ Edouard@2020: #if defined(_MSC_VER) Edouard@2020: time(&tTemp); Edouard@2020: tblock = localtime(&tTemp); Edouard@2020: #else Edouard@2020: if (gettimeofday(&tv, NULL) == 0) { Edouard@2020: tblock = localtime(&tv.tv_sec); Edouard@2020: } Edouard@2020: #endif Edouard@2020: Edouard@2020: if (tblock) { Edouard@2020: datetime_set_date(&Local_Date, (uint16_t) tblock->tm_year + 1900, Edouard@2020: (uint8_t) tblock->tm_mon + 1, (uint8_t) tblock->tm_mday); Edouard@2020: #if !defined(_MSC_VER) Edouard@2020: datetime_set_time(&Local_Time, (uint8_t) tblock->tm_hour, Edouard@2020: (uint8_t) tblock->tm_min, (uint8_t) tblock->tm_sec, Edouard@2020: (uint8_t) (tv.tv_usec / 10000)); Edouard@2020: #else Edouard@2020: datetime_set_time(&Local_Time, (uint8_t) tblock->tm_hour, Edouard@2020: (uint8_t) tblock->tm_min, (uint8_t) tblock->tm_sec, 0); Edouard@2020: #endif Edouard@2020: if (tblock->tm_isdst) { Edouard@2020: Daylight_Savings_Status = true; Edouard@2020: } else { Edouard@2020: Daylight_Savings_Status = false; Edouard@2020: } Edouard@2020: /* note: timezone is declared in stdlib. */ Edouard@2020: UTC_Offset = timezone / 60; Edouard@2020: } else { Edouard@2020: datetime_date_wildcard_set(&Local_Date); Edouard@2020: datetime_time_wildcard_set(&Local_Time); Edouard@2020: Daylight_Savings_Status = false; Edouard@2020: } Edouard@2020: } Edouard@2020: Edouard@2020: void Device_getCurrentDateTime( Edouard@2020: BACNET_DATE_TIME * DateTime) Edouard@2020: { Edouard@2020: Device_Update_Current_Time(); Edouard@2020: Edouard@2020: DateTime->date = Local_Date; Edouard@2020: DateTime->time = Local_Time; Edouard@2020: } Edouard@2020: Edouard@2020: int32_t Device_UTC_Offset(void) Edouard@2020: { Edouard@2020: Device_Update_Current_Time(); Edouard@2020: Edouard@2020: return UTC_Offset; Edouard@2020: } Edouard@2020: Edouard@2020: bool Device_Daylight_Savings_Status(void) Edouard@2020: { Edouard@2020: return Daylight_Savings_Status; Edouard@2020: } Edouard@2020: Edouard@2020: /* return the length of the apdu encoded or BACNET_STATUS_ERROR for error or Edouard@2020: BACNET_STATUS_ABORT for abort message */ Edouard@2020: int Device_Read_Property_Local( Edouard@2020: BACNET_READ_PROPERTY_DATA * rpdata) Edouard@2020: { Edouard@2020: int apdu_len = 0; /* return value */ Edouard@2020: int len = 0; /* apdu len intermediate value */ Edouard@2020: BACNET_BIT_STRING bit_string = { 0 }; Edouard@2020: BACNET_CHARACTER_STRING char_string = { 0 }; Edouard@2020: unsigned i = 0; Edouard@2020: int object_type = 0; Edouard@2020: uint32_t instance = 0; Edouard@2020: unsigned count = 0; Edouard@2020: uint8_t *apdu = NULL; Edouard@2020: struct object_functions *pObject = NULL; Edouard@2020: bool found = false; Edouard@2020: Edouard@2020: if ((rpdata == NULL) || (rpdata->application_data == NULL) || Edouard@2020: (rpdata->application_data_len == 0)) { Edouard@2020: return 0; Edouard@2020: } Edouard@2020: apdu = rpdata->application_data; Edouard@2020: switch (rpdata->object_property) { Edouard@2020: case PROP_OBJECT_IDENTIFIER: Edouard@2020: apdu_len = Edouard@2020: encode_application_object_id(&apdu[0], OBJECT_DEVICE, Edouard@2020: Object_Instance_Number); Edouard@2020: break; Edouard@2020: case PROP_OBJECT_NAME: Edouard@2020: apdu_len = Edouard@2020: encode_application_character_string(&apdu[0], &My_Object_Name); Edouard@2020: break; Edouard@2020: case PROP_OBJECT_TYPE: Edouard@2020: apdu_len = encode_application_enumerated(&apdu[0], OBJECT_DEVICE); Edouard@2020: break; Edouard@2020: case PROP_DESCRIPTION: Edouard@2020: characterstring_init_ansi(&char_string, Description); Edouard@2020: apdu_len = Edouard@2020: encode_application_character_string(&apdu[0], &char_string); Edouard@2020: break; Edouard@2020: case PROP_SYSTEM_STATUS: Edouard@2020: apdu_len = encode_application_enumerated(&apdu[0], System_Status); Edouard@2020: break; Edouard@2020: case PROP_VENDOR_NAME: Edouard@2020: characterstring_init_ansi(&char_string, Vendor_Name); Edouard@2020: apdu_len = Edouard@2020: encode_application_character_string(&apdu[0], &char_string); Edouard@2020: break; Edouard@2020: case PROP_VENDOR_IDENTIFIER: Edouard@2020: apdu_len = Edouard@2020: encode_application_unsigned(&apdu[0], Vendor_Identifier); Edouard@2020: break; Edouard@2020: case PROP_MODEL_NAME: Edouard@2020: characterstring_init_ansi(&char_string, Model_Name); Edouard@2020: apdu_len = Edouard@2020: encode_application_character_string(&apdu[0], &char_string); Edouard@2020: break; Edouard@2020: case PROP_FIRMWARE_REVISION: Edouard@2020: characterstring_init_ansi(&char_string, Firmware_Revision); Edouard@2020: apdu_len = Edouard@2020: encode_application_character_string(&apdu[0], &char_string); Edouard@2020: break; Edouard@2020: case PROP_APPLICATION_SOFTWARE_VERSION: Edouard@2020: characterstring_init_ansi(&char_string, Edouard@2020: Application_Software_Version); Edouard@2020: apdu_len = Edouard@2020: encode_application_character_string(&apdu[0], &char_string); Edouard@2020: break; Edouard@2020: case PROP_LOCATION: Edouard@2020: characterstring_init_ansi(&char_string, Location); Edouard@2020: apdu_len = Edouard@2020: encode_application_character_string(&apdu[0], &char_string); Edouard@2020: break; Edouard@2020: case PROP_LOCAL_TIME: Edouard@2020: Device_Update_Current_Time(); Edouard@2020: apdu_len = encode_application_time(&apdu[0], &Local_Time); Edouard@2020: break; Edouard@2020: case PROP_UTC_OFFSET: Edouard@2020: Device_Update_Current_Time(); Edouard@2020: apdu_len = encode_application_signed(&apdu[0], UTC_Offset); Edouard@2020: break; Edouard@2020: case PROP_LOCAL_DATE: Edouard@2020: Device_Update_Current_Time(); Edouard@2020: apdu_len = encode_application_date(&apdu[0], &Local_Date); Edouard@2020: break; Edouard@2020: case PROP_DAYLIGHT_SAVINGS_STATUS: Edouard@2020: Device_Update_Current_Time(); Edouard@2020: apdu_len = Edouard@2020: encode_application_boolean(&apdu[0], Daylight_Savings_Status); Edouard@2020: break; Edouard@2020: case PROP_PROTOCOL_VERSION: Edouard@2020: apdu_len = Edouard@2020: encode_application_unsigned(&apdu[0], Edouard@2020: Device_Protocol_Version()); Edouard@2020: break; Edouard@2020: case PROP_PROTOCOL_REVISION: Edouard@2020: apdu_len = Edouard@2020: encode_application_unsigned(&apdu[0], Edouard@2020: Device_Protocol_Revision()); Edouard@2020: break; Edouard@2020: case PROP_PROTOCOL_SERVICES_SUPPORTED: Edouard@2020: /* Note: list of services that are executed, not initiated. */ Edouard@2020: bitstring_init(&bit_string); Edouard@2020: for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { Edouard@2020: /* automatic lookup based on handlers set */ Edouard@2020: bitstring_set_bit(&bit_string, (uint8_t) i, Edouard@2020: apdu_service_supported((BACNET_SERVICES_SUPPORTED) i)); Edouard@2020: } Edouard@2020: apdu_len = encode_application_bitstring(&apdu[0], &bit_string); Edouard@2020: break; Edouard@2020: case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: Edouard@2020: /* Note: this is the list of objects that can be in this device, Edouard@2020: not a list of objects that this device can access */ Edouard@2020: bitstring_init(&bit_string); Edouard@2020: for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { Edouard@2020: /* initialize all the object types to not-supported */ Edouard@2020: bitstring_set_bit(&bit_string, (uint8_t) i, false); Edouard@2020: } Edouard@2020: /* set the object types with objects to supported */ Edouard@2020: Edouard@2020: pObject = Object_Table; Edouard@2020: while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { Edouard@2020: if ((pObject->Object_Count) && (pObject->Object_Count() > 0)) { Edouard@2020: bitstring_set_bit(&bit_string, pObject->Object_Type, true); Edouard@2020: } Edouard@2020: pObject++; Edouard@2020: } Edouard@2020: apdu_len = encode_application_bitstring(&apdu[0], &bit_string); Edouard@2020: break; Edouard@2020: case PROP_OBJECT_LIST: Edouard@2020: count = Device_Object_List_Count(); Edouard@2020: /* Array element zero is the number of objects in the list */ Edouard@2020: if (rpdata->array_index == 0) Edouard@2020: apdu_len = encode_application_unsigned(&apdu[0], count); Edouard@2020: /* if no index was specified, then try to encode the entire list */ Edouard@2020: /* into one packet. Note that more than likely you will have */ Edouard@2020: /* to return an error if the number of encoded objects exceeds */ Edouard@2020: /* your maximum APDU size. */ Edouard@2020: else if (rpdata->array_index == BACNET_ARRAY_ALL) { Edouard@2020: for (i = 1; i <= count; i++) { Edouard@2020: found = Edouard@2020: Device_Object_List_Identifier(i, &object_type, Edouard@2020: &instance); Edouard@2020: if (found) { Edouard@2020: len = Edouard@2020: encode_application_object_id(&apdu[apdu_len], Edouard@2020: object_type, instance); Edouard@2020: apdu_len += len; Edouard@2020: /* assume next one is the same size as this one */ Edouard@2020: /* can we all fit into the APDU? Don't check for last entry */ Edouard@2020: if ((i != count) && (apdu_len + len) >= MAX_APDU) { Edouard@2020: /* Abort response */ Edouard@2020: rpdata->error_code = Edouard@2020: ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; Edouard@2020: apdu_len = BACNET_STATUS_ABORT; Edouard@2020: break; Edouard@2020: } Edouard@2020: } else { Edouard@2020: /* error: internal error? */ Edouard@2020: rpdata->error_class = ERROR_CLASS_SERVICES; Edouard@2020: rpdata->error_code = ERROR_CODE_OTHER; Edouard@2020: apdu_len = BACNET_STATUS_ERROR; Edouard@2020: break; Edouard@2020: } Edouard@2020: } Edouard@2020: } else { Edouard@2020: found = Edouard@2020: Device_Object_List_Identifier(rpdata->array_index, Edouard@2020: &object_type, &instance); Edouard@2020: if (found) { Edouard@2020: apdu_len = Edouard@2020: encode_application_object_id(&apdu[0], object_type, Edouard@2020: instance); Edouard@2020: } else { Edouard@2020: rpdata->error_class = ERROR_CLASS_PROPERTY; Edouard@2020: rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; Edouard@2020: apdu_len = BACNET_STATUS_ERROR; Edouard@2020: } Edouard@2020: } Edouard@2020: break; Edouard@2020: case PROP_MAX_APDU_LENGTH_ACCEPTED: Edouard@2020: apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU); Edouard@2020: break; Edouard@2020: case PROP_SEGMENTATION_SUPPORTED: Edouard@2020: apdu_len = Edouard@2020: encode_application_enumerated(&apdu[0], Edouard@2020: Device_Segmentation_Supported()); Edouard@2020: break; Edouard@2020: case PROP_APDU_TIMEOUT: Edouard@2020: apdu_len = encode_application_unsigned(&apdu[0], apdu_timeout()); Edouard@2020: break; Edouard@2020: case PROP_NUMBER_OF_APDU_RETRIES: Edouard@2020: apdu_len = encode_application_unsigned(&apdu[0], apdu_retries()); Edouard@2020: break; Edouard@2020: case PROP_DEVICE_ADDRESS_BINDING: Edouard@2020: /* FIXME: the real max apdu remaining should be passed into function */ Edouard@2020: apdu_len = address_list_encode(&apdu[0], MAX_APDU); Edouard@2020: break; Edouard@2020: case PROP_DATABASE_REVISION: Edouard@2020: apdu_len = Edouard@2020: encode_application_unsigned(&apdu[0], Database_Revision); Edouard@2020: break; Edouard@2020: // case PROP_PROPERTY_LIST: Edouard@2020: // BACnet_encode_array(Device_Properties_List, Edouard@2020: // property_list_count(Device_Properties_List), Edouard@2020: // retfalse, encode_application_enumerated); Edouard@2020: // break; Edouard@2020: default: Edouard@2020: rpdata->error_class = ERROR_CLASS_PROPERTY; Edouard@2020: rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; Edouard@2020: apdu_len = BACNET_STATUS_ERROR; Edouard@2020: break; Edouard@2020: } Edouard@2020: /* only array properties can have array options */ Edouard@2020: if ((apdu_len >= 0) && (rpdata->object_property != PROP_OBJECT_LIST) && Edouard@2020: // (rpdata->object_property != PROP_PROPERTY_LIST) && Edouard@2020: (rpdata->array_index != BACNET_ARRAY_ALL)) { Edouard@2020: rpdata->error_class = ERROR_CLASS_PROPERTY; Edouard@2020: rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; Edouard@2020: apdu_len = BACNET_STATUS_ERROR; Edouard@2020: } Edouard@2020: Edouard@2020: return apdu_len; Edouard@2020: } Edouard@2020: Edouard@2020: /** Looks up the requested Object and Property, and encodes its Value in an APDU. Edouard@2020: * If the Object or Property can't be found, sets the error class and code. Edouard@2020: * Edouard@2020: * param: rpdata [in,out] Structure with the desired Object and Property info Edouard@2020: * on entry, and APDU message on return. Edouard@2020: * return: The length of the APDU on success, else BACNET_STATUS_ERROR Edouard@2020: */ Edouard@2020: int Device_Read_Property( Edouard@2020: BACNET_READ_PROPERTY_DATA * rpdata) Edouard@2020: { Edouard@2020: int apdu_len = BACNET_STATUS_ERROR; Edouard@2020: struct object_functions *pObject = NULL; Edouard@2020: Edouard@2020: /* initialize the default return values */ Edouard@2020: rpdata->error_class = ERROR_CLASS_OBJECT; Edouard@2020: rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; Edouard@2020: pObject = Device_Objects_Find_Functions(rpdata->object_type); Edouard@2020: if (pObject != NULL) { Edouard@2020: if (pObject->Object_Valid_Instance && Edouard@2020: pObject->Object_Valid_Instance(rpdata->object_instance)) { Edouard@2020: if (pObject->Object_Read_Property) { Edouard@2020: apdu_len = pObject->Object_Read_Property(rpdata); Edouard@2020: } Edouard@2020: } Edouard@2020: } Edouard@2020: Edouard@2020: return apdu_len; Edouard@2020: } Edouard@2020: Edouard@2020: /* returns true if successful */ Edouard@2020: bool Device_Write_Property_Local( Edouard@2020: BACNET_WRITE_PROPERTY_DATA * wp_data) Edouard@2020: { Edouard@2020: bool status = false; /* return value */ Edouard@2020: int len = 0; Edouard@2020: BACNET_APPLICATION_DATA_VALUE value; Edouard@2020: int object_type = 0; Edouard@2020: uint32_t object_instance = 0; Edouard@2020: int temp; Edouard@2020: Edouard@2020: /* decode the some of the request */ Edouard@2020: len = Edouard@2020: bacapp_decode_application_data(wp_data->application_data, Edouard@2020: wp_data->application_data_len, &value); Edouard@2020: if (len < 0) { Edouard@2020: /* error while decoding - a value larger than we can handle */ Edouard@2020: wp_data->error_class = ERROR_CLASS_PROPERTY; Edouard@2020: wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; Edouard@2020: return false; Edouard@2020: } Edouard@2020: if ((wp_data->object_property != PROP_OBJECT_LIST) && Edouard@2020: (wp_data->array_index != BACNET_ARRAY_ALL)) { Edouard@2020: /* only array properties can have array options */ Edouard@2020: wp_data->error_class = ERROR_CLASS_PROPERTY; Edouard@2020: wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; Edouard@2020: return false; Edouard@2020: } Edouard@2020: /* FIXME: len < application_data_len: more data? */ Edouard@2020: switch (wp_data->object_property) { Edouard@2020: case PROP_OBJECT_IDENTIFIER: Edouard@2020: status = Edouard@2020: WPValidateArgType(&value, BACNET_APPLICATION_TAG_OBJECT_ID, Edouard@2020: &wp_data->error_class, &wp_data->error_code); Edouard@2020: if (status) { Edouard@2020: if ((value.type.Object_Id.type == OBJECT_DEVICE) && Edouard@2020: (Device_Set_Object_Instance_Number(value.type. Edouard@2020: Object_Id.instance))) { Edouard@2020: /* FIXME: we could send an I-Am broadcast to let the world know */ Edouard@2020: } else { Edouard@2020: status = false; Edouard@2020: wp_data->error_class = ERROR_CLASS_PROPERTY; Edouard@2020: wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; Edouard@2020: } Edouard@2020: } Edouard@2020: break; Edouard@2020: case PROP_NUMBER_OF_APDU_RETRIES: Edouard@2020: status = Edouard@2020: WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, Edouard@2020: &wp_data->error_class, &wp_data->error_code); Edouard@2020: if (status) { Edouard@2020: /* FIXME: bounds check? */ Edouard@2020: apdu_retries_set((uint8_t) value.type.Unsigned_Int); Edouard@2020: } Edouard@2020: break; Edouard@2020: case PROP_APDU_TIMEOUT: Edouard@2020: status = Edouard@2020: WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, Edouard@2020: &wp_data->error_class, &wp_data->error_code); Edouard@2020: if (status) { Edouard@2020: /* FIXME: bounds check? */ Edouard@2020: apdu_timeout_set((uint16_t) value.type.Unsigned_Int); Edouard@2020: } Edouard@2020: break; Edouard@2020: case PROP_VENDOR_IDENTIFIER: Edouard@2020: status = Edouard@2020: WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, Edouard@2020: &wp_data->error_class, &wp_data->error_code); Edouard@2020: if (status) { Edouard@2020: /* FIXME: bounds check? */ Edouard@2020: Device_Set_Vendor_Identifier((uint16_t) value. Edouard@2020: type.Unsigned_Int); Edouard@2020: } Edouard@2020: break; Edouard@2020: // case PROP_SYSTEM_STATUS: Edouard@2020: // status = Edouard@2020: // WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, Edouard@2020: // &wp_data->error_class, &wp_data->error_code); Edouard@2020: // if (status) { Edouard@2020: // temp = Device_Set_System_Status((BACNET_DEVICE_STATUS) Edouard@2020: // value.type.Enumerated, false); Edouard@2020: // if (temp != 0) { Edouard@2020: // status = false; Edouard@2020: // wp_data->error_class = ERROR_CLASS_PROPERTY; Edouard@2020: // if (temp == -1) { Edouard@2020: // wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; Edouard@2020: // } else { Edouard@2020: // wp_data->error_code = Edouard@2020: // ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED; Edouard@2020: // } Edouard@2020: // } Edouard@2020: // } Edouard@2020: // break; Edouard@2020: case PROP_OBJECT_NAME: Edouard@2020: status = Edouard@2020: WPValidateString(&value, Edouard@2020: characterstring_capacity(&My_Object_Name), false, Edouard@2020: &wp_data->error_class, &wp_data->error_code); Edouard@2020: if (status) { Edouard@2020: /* All the object names in a device must be unique */ Edouard@2020: if (Device_Valid_Object_Name(&value.type.Character_String, Edouard@2020: &object_type, &object_instance)) { Edouard@2020: if ((object_type == wp_data->object_type) && Edouard@2020: (object_instance == wp_data->object_instance)) { Edouard@2020: /* writing same name to same object */ Edouard@2020: status = true; Edouard@2020: } else { Edouard@2020: status = false; Edouard@2020: wp_data->error_class = ERROR_CLASS_PROPERTY; Edouard@2020: wp_data->error_code = ERROR_CODE_DUPLICATE_NAME; Edouard@2020: } Edouard@2020: } else { Edouard@2020: Device_Set_Object_Name(&value.type.Character_String); Edouard@2020: } Edouard@2020: } Edouard@2020: break; Edouard@2020: case PROP_LOCATION: Edouard@2020: status = Edouard@2020: WPValidateString(&value, MAX_DEV_LOC_LEN, true, Edouard@2020: &wp_data->error_class, &wp_data->error_code); Edouard@2020: if (status) { Edouard@2020: Device_Set_Location(characterstring_value(&value. Edouard@2020: type.Character_String), Edouard@2020: characterstring_length(&value.type.Character_String)); Edouard@2020: } Edouard@2020: break; Edouard@2020: Edouard@2020: case PROP_DESCRIPTION: Edouard@2020: status = Edouard@2020: WPValidateString(&value, MAX_DEV_DESC_LEN, true, Edouard@2020: &wp_data->error_class, &wp_data->error_code); Edouard@2020: if (status) { Edouard@2020: Device_Set_Description(characterstring_value(&value. Edouard@2020: type.Character_String), Edouard@2020: characterstring_length(&value.type.Character_String)); Edouard@2020: } Edouard@2020: break; Edouard@2020: case PROP_MODEL_NAME: Edouard@2020: status = Edouard@2020: WPValidateString(&value, MAX_DEV_MOD_LEN, true, Edouard@2020: &wp_data->error_class, &wp_data->error_code); Edouard@2020: if (status) { Edouard@2020: Device_Set_Model_Name(characterstring_value(&value. Edouard@2020: type.Character_String), Edouard@2020: characterstring_length(&value.type.Character_String)); Edouard@2020: } Edouard@2020: break; Edouard@2020: Edouard@2020: case PROP_OBJECT_TYPE: Edouard@2020: case PROP_SYSTEM_STATUS: Edouard@2020: case PROP_VENDOR_NAME: Edouard@2020: case PROP_FIRMWARE_REVISION: Edouard@2020: case PROP_APPLICATION_SOFTWARE_VERSION: Edouard@2020: case PROP_LOCAL_TIME: Edouard@2020: case PROP_UTC_OFFSET: Edouard@2020: case PROP_LOCAL_DATE: Edouard@2020: case PROP_DAYLIGHT_SAVINGS_STATUS: Edouard@2020: case PROP_PROTOCOL_VERSION: Edouard@2020: case PROP_PROTOCOL_REVISION: Edouard@2020: case PROP_PROTOCOL_SERVICES_SUPPORTED: Edouard@2020: case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: Edouard@2020: case PROP_OBJECT_LIST: Edouard@2020: case PROP_MAX_APDU_LENGTH_ACCEPTED: Edouard@2020: case PROP_SEGMENTATION_SUPPORTED: Edouard@2020: case PROP_DEVICE_ADDRESS_BINDING: Edouard@2020: case PROP_DATABASE_REVISION: Edouard@2020: // case PROP_PROPERTY_LIST: Edouard@2020: wp_data->error_class = ERROR_CLASS_PROPERTY; Edouard@2020: wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; Edouard@2020: break; Edouard@2020: default: Edouard@2020: wp_data->error_class = ERROR_CLASS_PROPERTY; Edouard@2020: wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; Edouard@2020: break; Edouard@2020: } Edouard@2020: Edouard@2020: return status; Edouard@2020: } Edouard@2020: Edouard@2020: /** Looks up the requested Object and Property, and set the new Value in it, Edouard@2020: * if allowed. Edouard@2020: * If the Object or Property can't be found, sets the error class and code. Edouard@2020: * Edouard@2020: * param: wp_data [in,out] Structure with the desired Object and Property info Edouard@2020: * and new Value on entry, and APDU message on return. Edouard@2020: * return: True on success, else False if there is an error. Edouard@2020: */ Edouard@2020: bool Device_Write_Property( Edouard@2020: BACNET_WRITE_PROPERTY_DATA * wp_data) Edouard@2020: { Edouard@2020: bool status = false; /* Ever the pessamist! */ Edouard@2020: struct object_functions *pObject = NULL; Edouard@2020: Edouard@2020: /* initialize the default return values */ Edouard@2020: wp_data->error_class = ERROR_CLASS_OBJECT; Edouard@2020: wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; Edouard@2020: pObject = Device_Objects_Find_Functions(wp_data->object_type); Edouard@2020: if (pObject != NULL) { Edouard@2020: if (pObject->Object_Valid_Instance && Edouard@2020: pObject->Object_Valid_Instance(wp_data->object_instance)) { Edouard@2020: if (pObject->Object_Write_Property) { Edouard@2020: status = pObject->Object_Write_Property(wp_data); Edouard@2020: } else { Edouard@2020: wp_data->error_class = ERROR_CLASS_PROPERTY; Edouard@2020: wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; Edouard@2020: } Edouard@2020: } else { Edouard@2020: wp_data->error_class = ERROR_CLASS_OBJECT; Edouard@2020: wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; Edouard@2020: } Edouard@2020: } else { Edouard@2020: wp_data->error_class = ERROR_CLASS_OBJECT; Edouard@2020: wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; Edouard@2020: } Edouard@2020: Edouard@2020: return (status); Edouard@2020: } Edouard@2020: Edouard@2020: /** Looks up the requested Object, and fills the Property Value list. Edouard@2020: * If the Object or Property can't be found, returns false. Edouard@2020: * param: [in] The object type to be looked up. Edouard@2020: * param: [in] The object instance number to be looked up. Edouard@2020: * param: [out] The value list Edouard@2020: * return: True if the object instance supports this feature and value changed. Edouard@2020: */ Edouard@2020: bool Device_Encode_Value_List( Edouard@2020: BACNET_OBJECT_TYPE object_type, Edouard@2020: uint32_t object_instance, Edouard@2020: BACNET_PROPERTY_VALUE * value_list) Edouard@2020: { Edouard@2020: bool status = false; /* Ever the pessamist! */ Edouard@2020: struct object_functions *pObject = NULL; Edouard@2020: Edouard@2020: pObject = Device_Objects_Find_Functions(object_type); Edouard@2020: if (pObject != NULL) { Edouard@2020: if (pObject->Object_Valid_Instance && Edouard@2020: pObject->Object_Valid_Instance(object_instance)) { Edouard@2020: if (pObject->Object_Value_List) { Edouard@2020: status = Edouard@2020: pObject->Object_Value_List(object_instance, value_list); Edouard@2020: } Edouard@2020: } Edouard@2020: } Edouard@2020: Edouard@2020: return (status); Edouard@2020: } Edouard@2020: Edouard@2020: /** Checks the COV flag in the requested Object Edouard@2020: * param: [in] The object type to be looked up. Edouard@2020: * param: [in] The object instance to be looked up. Edouard@2020: * return: True if the COV flag is set Edouard@2020: */ Edouard@2020: bool Device_COV( Edouard@2020: BACNET_OBJECT_TYPE object_type, Edouard@2020: uint32_t object_instance) Edouard@2020: { Edouard@2020: bool status = false; /* Ever the pessamist! */ Edouard@2020: struct object_functions *pObject = NULL; Edouard@2020: Edouard@2020: pObject = Device_Objects_Find_Functions(object_type); Edouard@2020: if (pObject != NULL) { Edouard@2020: if (pObject->Object_Valid_Instance && Edouard@2020: pObject->Object_Valid_Instance(object_instance)) { Edouard@2020: if (pObject->Object_COV) { Edouard@2020: status = pObject->Object_COV(object_instance); Edouard@2020: } Edouard@2020: } Edouard@2020: } Edouard@2020: Edouard@2020: return (status); Edouard@2020: } Edouard@2020: Edouard@2020: /** Clears the COV flag in the requested Object Edouard@2020: * param: [in] The object type to be looked up. Edouard@2020: * param: [in] The object instance to be looked up. Edouard@2020: */ Edouard@2020: void Device_COV_Clear( Edouard@2020: BACNET_OBJECT_TYPE object_type, Edouard@2020: uint32_t object_instance) Edouard@2020: { Edouard@2020: struct object_functions *pObject = NULL; Edouard@2020: Edouard@2020: pObject = Device_Objects_Find_Functions(object_type); Edouard@2020: if (pObject != NULL) { Edouard@2020: if (pObject->Object_Valid_Instance && Edouard@2020: pObject->Object_Valid_Instance(object_instance)) { Edouard@2020: if (pObject->Object_COV_Clear) { Edouard@2020: pObject->Object_COV_Clear(object_instance); Edouard@2020: } Edouard@2020: } Edouard@2020: } Edouard@2020: } Edouard@2020: Edouard@2020: Edouard@2020: /** Looks up the requested Object to see if the functionality is supported. Edouard@2020: * param: [in] The object type to be looked up. Edouard@2020: * return: True if the object instance supports this feature. Edouard@2020: */ Edouard@2020: bool Device_Value_List_Supported( Edouard@2020: BACNET_OBJECT_TYPE object_type) Edouard@2020: { Edouard@2020: bool status = false; /* Ever the pessamist! */ Edouard@2020: struct object_functions *pObject = NULL; Edouard@2020: Edouard@2020: pObject = Device_Objects_Find_Functions(object_type); Edouard@2020: if (pObject != NULL) { Edouard@2020: if (pObject->Object_Value_List) { Edouard@2020: status = true; Edouard@2020: } Edouard@2020: } Edouard@2020: Edouard@2020: return (status); Edouard@2020: } Edouard@2020: Edouard@2020: /** Initialize the Device Object. Edouard@2020: Initialize the group of object helper functions for any supported Object. Edouard@2020: Initialize each of the Device Object child Object instances. Edouard@2020: * param: The BACnet Object Name of the bacnet server Edouard@2020: */ Edouard@2020: void Device_Init( Edouard@2020: const char * Device_Object_Name) Edouard@2020: { Edouard@2020: struct object_functions *pObject = NULL; Edouard@2020: Edouard@2020: /* initialize the Device_Properties_List array */ Edouard@2020: int len = 0; Edouard@2020: len += BACnet_Init_Properties_List(Device_Properties_List + len, Edouard@2020: Device_Properties_Required); Edouard@2020: len += BACnet_Init_Properties_List(Device_Properties_List + len, Edouard@2020: Device_Properties_Optional); Edouard@2020: len += BACnet_Init_Properties_List(Device_Properties_List + len, Edouard@2020: Device_Properties_Proprietary); Edouard@2020: Edouard@2020: characterstring_init_ansi(&My_Object_Name, Device_Object_Name); Edouard@2020: Object_Table = &My_Object_Table[0]; // sets glogbal variable! Edouard@2020: pObject = Object_Table; Edouard@2020: while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { Edouard@2020: if (pObject->Object_Init) { Edouard@2020: pObject->Object_Init(); Edouard@2020: } Edouard@2020: pObject++; Edouard@2020: } Edouard@2020: } Edouard@2020: Edouard@2020: bool DeviceGetRRInfo( Edouard@2020: BACNET_READ_RANGE_DATA * pRequest, /* Info on the request */ Edouard@2020: RR_PROP_INFO * pInfo) Edouard@2020: { /* Where to put the response */ Edouard@2020: bool status = false; /* return value */ Edouard@2020: Edouard@2020: switch (pRequest->object_property) { Edouard@2020: case PROP_VT_CLASSES_SUPPORTED: Edouard@2020: case PROP_ACTIVE_VT_SESSIONS: Edouard@2020: case PROP_LIST_OF_SESSION_KEYS: Edouard@2020: case PROP_TIME_SYNCHRONIZATION_RECIPIENTS: Edouard@2020: case PROP_MANUAL_SLAVE_ADDRESS_BINDING: Edouard@2020: case PROP_SLAVE_ADDRESS_BINDING: Edouard@2020: case PROP_RESTART_NOTIFICATION_RECIPIENTS: Edouard@2020: case PROP_UTC_TIME_SYNCHRONIZATION_RECIPIENTS: Edouard@2020: pInfo->RequestTypes = RR_BY_POSITION; Edouard@2020: pRequest->error_class = ERROR_CLASS_PROPERTY; Edouard@2020: pRequest->error_code = ERROR_CODE_UNKNOWN_PROPERTY; Edouard@2020: break; Edouard@2020: Edouard@2020: case PROP_DEVICE_ADDRESS_BINDING: Edouard@2020: pInfo->RequestTypes = RR_BY_POSITION; Edouard@2020: pInfo->Handler = rr_address_list_encode; Edouard@2020: status = true; Edouard@2020: break; Edouard@2020: Edouard@2020: default: Edouard@2020: pRequest->error_class = ERROR_CLASS_SERVICES; Edouard@2020: pRequest->error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST; Edouard@2020: break; Edouard@2020: } Edouard@2020: Edouard@2020: return status; Edouard@2020: } Edouard@2020: Edouard@2020: Edouard@2020: