Files
bacnet_stack/src/bacnet/basic/server/bacnet_device.c
T
Steve Karg b357bca5dd Converted device object test to use common read-write property test. Extended the basic BACnet device object example API. (#1106)
* Converted device object test to use common read-write property test. Extended the basic BACnet device object example API.

* Created BACnet/IP and COV test mocks to enable device object testing with less dependencies.
2025-09-24 07:09:56 -05:00

2847 lines
96 KiB
C

/**
* @file
* @brief Base "class" for handling all BACnet objects belonging
* to a BACnet device, as well as Device-specific properties.
* @author Steve Karg <skarg@users.sourceforge.net>
* @date March 2024
* @copyright SPDX-License-Identifier: MIT
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
/* BACnet Stack defines - first */
#include "bacnet/bacdef.h"
/* BACnet Stack API */
#include "bacnet/bacdcode.h"
#include "bacnet/bacapp.h"
#include "bacnet/datetime.h"
#include "bacnet/apdu.h"
#include "bacnet/wp.h" /* WriteProperty handling */
#include "bacnet/rp.h" /* ReadProperty handling */
#include "bacnet/dcc.h" /* DeviceCommunicationControl handling */
#include "bacnet/version.h"
#if defined(BACDL_MSTP)
#include "bacnet/datalink/dlmstp.h"
#endif
#include "bacnet/basic/services.h"
#include "bacnet/basic/binding/address.h"
/* include the device object */
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/object/acc.h"
#include "bacnet/basic/object/ai.h"
#include "bacnet/basic/object/ao.h"
#include "bacnet/basic/object/av.h"
#include "bacnet/basic/object/bi.h"
#include "bacnet/basic/object/bo.h"
#include "bacnet/basic/object/bv.h"
#include "bacnet/basic/object/calendar.h"
#include "bacnet/basic/object/command.h"
#include "bacnet/basic/object/program.h"
#include "bacnet/basic/object/lc.h"
#include "bacnet/basic/object/lsp.h"
#include "bacnet/basic/object/lsz.h"
#include "bacnet/basic/object/ms-input.h"
#include "bacnet/basic/object/mso.h"
#include "bacnet/basic/object/msv.h"
#include "bacnet/basic/object/schedule.h"
#include "bacnet/basic/object/structured_view.h"
#include "bacnet/basic/object/trendlog.h"
#include "bacnet/basic/object/nc.h"
#include "bacnet/basic/object/bacfile.h"
#include "bacnet/basic/object/bitstring_value.h"
#include "bacnet/basic/object/csv.h"
#include "bacnet/basic/object/iv.h"
#include "bacnet/basic/object/time_value.h"
#include "bacnet/basic/object/channel.h"
#include "bacnet/basic/object/program.h"
#include "bacnet/basic/object/lo.h"
#include "bacnet/basic/object/blo.h"
#include "bacnet/basic/object/netport.h"
#include "bacnet/basic/object/color_object.h"
#include "bacnet/basic/object/color_temperature.h"
#include "bacnet/basic/object/program.h"
#ifdef CONFIG_BACNET_BASIC_DEVICE_OBJECT_VERSION
#define BACNET_DEVICE_VERSION CONFIG_BACNET_BASIC_DEVICE_OBJECT_VERSION
#else
#define BACNET_DEVICE_VERSION "1.0.0"
#endif
#ifdef CONFIG_BACNET_BASIC_DEVICE_OBJECT_NAME
#define BACNET_DEVICE_OBJECT_NAME CONFIG_BACNET_BASIC_DEVICE_OBJECT_NAME
#else
#define BACNET_DEVICE_OBJECT_NAME "BACnet Basic Device"
#endif
#ifdef CONFIG_BACNET_BASIC_DEVICE_DESCRIPTION
#define BACNET_DEVICE_DESCRIPTION CONFIG_BACNET_BASIC_DEVICE_DESCRIPTION
#else
#define BACNET_DEVICE_DESCRIPTION "BACnet Basic Server Device"
#endif
#ifdef CONFIG_BACNET_BASIC_DEVICE_MODEL_NAME
#define BACNET_DEVICE_MODEL_NAME CONFIG_BACNET_BASIC_DEVICE_MODEL_NAME
#else
#define BACNET_DEVICE_MODEL_NAME "GNU Basic Server Model 42"
#endif
#ifdef CONFIG_BACNET_BASIC_DEVICE_LOCATION_NAME
#define BACNET_DEVICE_LOCATION_NAME CONFIG_BACNET_BASIC_DEVICE_LOCATION_NAME
#else
#define BACNET_DEVICE_LOCATION_NAME "GNU Basic Building"
#endif
#ifdef CONFIG_BACNET_BASIC_DEVICE_SERIAL_NUMBER
#define BACNET_DEVICE_SERIAL_NUMBER CONFIG_BACNET_BASIC_DEVICE_SERIAL_NUMBER
#else
#define BACNET_DEVICE_SERIAL_NUMBER "BACnetDMcN56RBkeDJuNfxn3M44tfC2Y"
#endif
#ifdef CONFIG_BACNET_BASIC_COV_SUBSCRIPTIONS_SIZE
#define BACNET_COV_SUBSCRIPTIONS_SIZE CONFIG_BACNET_BASIC_COV_SUBSCRIPTIONS_SIZE
#else
#define BACNET_COV_SUBSCRIPTIONS_SIZE 0
#endif
#if !( \
defined(CONFIG_BACNET_BASIC_OBJECT_ALL) || \
defined(CONFIG_BACNET_BASIC_OBJECT_ANALOG_INPUT) || \
defined(CONFIG_BACNET_BASIC_OBJECT_ANALOG_OUTPUT) || \
defined(CONFIG_BACNET_BASIC_OBJECT_ANALOG_VALUE) || \
defined(CONFIG_BACNET_BASIC_OBJECT_BINARY_INPUT) || \
defined(CONFIG_BACNET_BASIC_OBJECT_BINARY_OUTPUT) || \
defined(CONFIG_BACNET_BASIC_OBJECT_BINARY_VALUE) || \
defined(CONFIG_BACNET_BASIC_OBJECT_MULTISTATE_INPUT) || \
defined(CONFIG_BACNET_BASIC_OBJECT_MULTISTATE_OUTPUT) || \
defined(CONFIG_BACNET_BASIC_OBJECT_MULTISTATE_VALUE) || \
defined(CONFIG_BACNET_BASIC_OBJECT_NETWORK_PORT) || \
defined(CONFIG_BACNET_BASIC_OBJECT_CALENDAR) || \
defined(CONFIG_BACNET_BASIC_OBJECT_INTEGER_VALUE) || \
defined(CONFIG_BACNET_BASIC_OBJECT_LIFE_SAFETY_POINT) || \
defined(CONFIG_BACNET_BASIC_OBJECT_LIFE_SAFETY_ZONE) || \
defined(CONFIG_BACNET_BASIC_OBJECT_LIGHTING_OUTPUT) || \
defined(CONFIG_BACNET_BASIC_OBJECT_LOAD_CONTROL) || \
defined(CONFIG_BACNET_BASIC_OBJECT_CHANNEL) || \
defined(CONFIG_BACNET_BASIC_OBJECT_BINARY_LIGHTING_OUTPUT) || \
defined(CONFIG_BACNET_BASIC_OBJECT_COLOR) || \
defined(CONFIG_BACNET_BASIC_OBJECT_COLOR_TEMPERATURE) || \
defined(CONFIG_BACNET_BASIC_OBJECT_FILE) || \
defined(CONFIG_BACNET_BASIC_OBJECT_STRUCTURED_VIEW) || \
defined(CONFIG_BACNET_BASIC_OBJECT_BITSTRING_VALUE) || \
defined(CONFIG_BACNET_BASIC_OBJECT_PROGRAM) || \
defined(CONFIG_BACNET_BASIC_OBJECT_CHARACTERSTRING_VALUE))
#define CONFIG_BACNET_BASIC_OBJECT_ALL
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_ALL)
#define CONFIG_BACNET_BASIC_OBJECT_ANALOG_INPUT
#define CONFIG_BACNET_BASIC_OBJECT_ANALOG_OUTPUT
#define CONFIG_BACNET_BASIC_OBJECT_ANALOG_VALUE
#define CONFIG_BACNET_BASIC_OBJECT_BINARY_INPUT
#define CONFIG_BACNET_BASIC_OBJECT_BINARY_OUTPUT
#define CONFIG_BACNET_BASIC_OBJECT_BINARY_VALUE
#define CONFIG_BACNET_BASIC_OBJECT_MULTISTATE_INPUT
#define CONFIG_BACNET_BASIC_OBJECT_MULTISTATE_OUTPUT
#define CONFIG_BACNET_BASIC_OBJECT_MULTISTATE_VALUE
#define CONFIG_BACNET_BASIC_OBJECT_NETWORK_PORT
#define CONFIG_BACNET_BASIC_OBJECT_CALENDAR
#define CONFIG_BACNET_BASIC_OBJECT_INTEGER_VALUE
#define CONFIG_BACNET_BASIC_OBJECT_LIFE_SAFETY_POINT
#define CONFIG_BACNET_BASIC_OBJECT_LIFE_SAFETY_ZONE
#define CONFIG_BACNET_BASIC_OBJECT_LIGHTING_OUTPUT
#define CONFIG_BACNET_BASIC_OBJECT_LOAD_CONTROL
#define CONFIG_BACNET_BASIC_OBJECT_CHANNEL
#define CONFIG_BACNET_BASIC_OBJECT_BINARY_LIGHTING_OUTPUT
#define CONFIG_BACNET_BASIC_OBJECT_COLOR
#define CONFIG_BACNET_BASIC_OBJECT_COLOR_TEMPERATURE
#define CONFIG_BACNET_BASIC_OBJECT_FILE
#define CONFIG_BACNET_BASIC_OBJECT_STRUCTURED_VIEW
#define CONFIG_BACNET_BASIC_OBJECT_BITSTRING_VALUE
#define CONFIG_BACNET_BASIC_OBJECT_PROGRAM
#define CONFIG_BACNET_BASIC_OBJECT_CHARACTERSTRING_VALUE
#endif
#if (BACNET_PROTOCOL_REVISION < 14)
#ifdef CONFIG_BACNET_BASIC_OBJECT_CHANNEL
#undef CONFIG_BACNET_BASIC_OBJECT_CHANNEL
#warning "Channel configured, but BACnet Protocol Revision < 14"
#endif
#ifdef CONFIG_BACNET_BASIC_OBJECT_LIGHTING_OUTPUT
#undef CONFIG_BACNET_BASIC_OBJECT_LIGHTING_OUTPUT
#warning "Lighting Output configured, but BACnet Protocol Revision < 14"
#endif
#endif
#if (BACNET_PROTOCOL_REVISION < 16)
#ifdef CONFIG_BACNET_BASIC_OBJECT_BINARY_LIGHTING_OUTPUT
#undef CONFIG_BACNET_BASIC_OBJECT_BINARY_LIGHTING_OUTPUT
#warning "Binary Lighting Output configured, but BACnet Protocol Revision < 16"
#endif
#endif
#if (BACNET_PROTOCOL_REVISION < 17)
#ifdef CONFIG_BACNET_BASIC_OBJECT_NETWORK_PORT
#undef CONFIG_BACNET_BASIC_OBJECT_NETWORK_PORT
#warning "Network Port is configured, but BACnet Protocol Revision < 17"
#endif
#endif
#if (BACNET_PROTOCOL_REVISION < 24)
#ifdef CONFIG_BACNET_BASIC_OBJECT_COLOR
#undef CONFIG_BACNET_BASIC_OBJECT_COLOR
#warning "Color configured, but BACnet Protocol Revision < 24"
#endif
#ifdef CONFIG_BACNET_BASIC_OBJECT_COLOR_TEMPERATURE
#undef CONFIG_BACNET_BASIC_OBJECT_COLOR_TEMPERATURE
#warning "Color Temperature configured, but BACnet Protocol Revision < 24"
#endif
#endif
/* may be overridden by outside table */
static object_functions_t *Object_Table;
static object_functions_t My_Object_Table[] = {
{ OBJECT_DEVICE,
NULL, /* don't init - recursive! */
Device_Count,
Device_Index_To_Instance,
Device_Valid_Object_Instance_Number,
Device_Object_Name,
Device_Read_Property_Local,
Device_Write_Property_Local,
Device_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
NULL /* Value_Lists */,
NULL /* COV */,
NULL /* COV Clear */,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
NULL /* Create */,
NULL /* Delete */,
NULL /* Timer */ },
#if defined(CONFIG_BACNET_BASIC_OBJECT_ANALOG_INPUT)
{ OBJECT_ANALOG_INPUT,
Analog_Input_Init,
Analog_Input_Count,
Analog_Input_Index_To_Instance,
Analog_Input_Valid_Instance,
Analog_Input_Object_Name,
Analog_Input_Read_Property,
Analog_Input_Write_Property,
Analog_Input_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
Analog_Input_Encode_Value_List,
Analog_Input_Change_Of_Value,
Analog_Input_Change_Of_Value_Clear,
Analog_Input_Intrinsic_Reporting,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Analog_Input_Create,
Analog_Input_Delete,
NULL /* Timer */ },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_ANALOG_OUTPUT)
{ OBJECT_ANALOG_OUTPUT,
Analog_Output_Init,
Analog_Output_Count,
Analog_Output_Index_To_Instance,
Analog_Output_Valid_Instance,
Analog_Output_Object_Name,
Analog_Output_Read_Property,
Analog_Output_Write_Property,
Analog_Output_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
Analog_Output_Encode_Value_List,
Analog_Output_Change_Of_Value,
Analog_Output_Change_Of_Value_Clear,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Analog_Output_Create,
Analog_Output_Delete,
NULL /* Timer */ },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_ANALOG_VALUE)
{ OBJECT_ANALOG_VALUE,
Analog_Value_Init,
Analog_Value_Count,
Analog_Value_Index_To_Instance,
Analog_Value_Valid_Instance,
Analog_Value_Object_Name,
Analog_Value_Read_Property,
Analog_Value_Write_Property,
Analog_Value_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
Analog_Value_Encode_Value_List,
Analog_Value_Change_Of_Value,
Analog_Value_Change_Of_Value_Clear,
Analog_Value_Intrinsic_Reporting,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Analog_Value_Create,
Analog_Value_Delete,
NULL /* Timer */ },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_BINARY_INPUT)
{ OBJECT_BINARY_INPUT,
Binary_Input_Init,
Binary_Input_Count,
Binary_Input_Index_To_Instance,
Binary_Input_Valid_Instance,
Binary_Input_Object_Name,
Binary_Input_Read_Property,
Binary_Input_Write_Property,
Binary_Input_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
Binary_Input_Encode_Value_List,
Binary_Input_Change_Of_Value,
Binary_Input_Change_Of_Value_Clear,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Binary_Input_Create,
Binary_Input_Delete,
NULL /* Timer */ },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_BINARY_OUTPUT)
{ OBJECT_BINARY_OUTPUT,
Binary_Output_Init,
Binary_Output_Count,
Binary_Output_Index_To_Instance,
Binary_Output_Valid_Instance,
Binary_Output_Object_Name,
Binary_Output_Read_Property,
Binary_Output_Write_Property,
Binary_Output_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
Binary_Output_Encode_Value_List,
Binary_Output_Change_Of_Value,
Binary_Output_Change_Of_Value_Clear,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Binary_Output_Create,
Binary_Output_Delete,
NULL /* Timer */ },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_BINARY_VALUE)
{ OBJECT_BINARY_VALUE,
Binary_Value_Init,
Binary_Value_Count,
Binary_Value_Index_To_Instance,
Binary_Value_Valid_Instance,
Binary_Value_Object_Name,
Binary_Value_Read_Property,
Binary_Value_Write_Property,
Binary_Value_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
Binary_Value_Encode_Value_List,
Binary_Value_Change_Of_Value,
Binary_Value_Change_Of_Value_Clear,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Binary_Value_Create,
Binary_Value_Delete,
NULL /* Timer */ },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_MULTISTATE_INPUT)
{ OBJECT_MULTI_STATE_INPUT,
Multistate_Input_Init,
Multistate_Input_Count,
Multistate_Input_Index_To_Instance,
Multistate_Input_Valid_Instance,
Multistate_Input_Object_Name,
Multistate_Input_Read_Property,
Multistate_Input_Write_Property,
Multistate_Input_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
Multistate_Input_Encode_Value_List,
Multistate_Input_Change_Of_Value,
Multistate_Input_Change_Of_Value_Clear,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Multistate_Input_Create,
Multistate_Input_Delete,
NULL /* Timer */ },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_MULTISTATE_OUTPUT)
{ OBJECT_MULTI_STATE_OUTPUT,
Multistate_Output_Init,
Multistate_Output_Count,
Multistate_Output_Index_To_Instance,
Multistate_Output_Valid_Instance,
Multistate_Output_Object_Name,
Multistate_Output_Read_Property,
Multistate_Output_Write_Property,
Multistate_Output_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
Multistate_Output_Encode_Value_List,
Multistate_Output_Change_Of_Value,
Multistate_Output_Change_Of_Value_Clear,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Multistate_Output_Create,
Multistate_Output_Delete,
NULL /* Timer */ },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_MULTISTATE_VALUE)
{ OBJECT_MULTI_STATE_VALUE,
Multistate_Value_Init,
Multistate_Value_Count,
Multistate_Value_Index_To_Instance,
Multistate_Value_Valid_Instance,
Multistate_Value_Object_Name,
Multistate_Value_Read_Property,
Multistate_Value_Write_Property,
Multistate_Value_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
Multistate_Value_Encode_Value_List,
Multistate_Value_Change_Of_Value,
Multistate_Value_Change_Of_Value_Clear,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Multistate_Value_Create,
Multistate_Value_Delete,
NULL /* Timer */ },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_NETWORK_PORT)
{ OBJECT_NETWORK_PORT,
Network_Port_Init,
Network_Port_Count,
Network_Port_Index_To_Instance,
Network_Port_Valid_Instance,
Network_Port_Object_Name,
Network_Port_Read_Property,
Network_Port_Write_Property,
Network_Port_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
NULL /* Value_Lists */,
NULL /* COV */,
NULL /* COV Clear */,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
NULL /* Create */,
NULL /* Delete */,
NULL /* Timer */ },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_CALENDAR)
{ OBJECT_CALENDAR,
Calendar_Init,
Calendar_Count,
Calendar_Index_To_Instance,
Calendar_Valid_Instance,
Calendar_Object_Name,
Calendar_Read_Property,
Calendar_Write_Property,
Calendar_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
NULL /* Value_Lists */,
NULL /* COV */,
NULL /* COV Clear */,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Calendar_Create,
Calendar_Delete,
NULL /* Timer */ },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_INTEGER_VALUE)
{ OBJECT_INTEGER_VALUE,
Integer_Value_Init,
Integer_Value_Count,
Integer_Value_Index_To_Instance,
Integer_Value_Valid_Instance,
Integer_Value_Object_Name,
Integer_Value_Read_Property,
Integer_Value_Write_Property,
Integer_Value_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
NULL /* Value_Lists */,
NULL /* COV */,
NULL /* COV Clear */,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Integer_Value_Create,
Integer_Value_Delete,
NULL /* Timer */ },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_LIFE_SAFETY_POINT)
{ OBJECT_LIFE_SAFETY_POINT,
Life_Safety_Point_Init,
Life_Safety_Point_Count,
Life_Safety_Point_Index_To_Instance,
Life_Safety_Point_Valid_Instance,
Life_Safety_Point_Object_Name,
Life_Safety_Point_Read_Property,
Life_Safety_Point_Write_Property,
Life_Safety_Point_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
NULL /* Value_Lists */,
NULL /* COV */,
NULL /* COV Clear */,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Life_Safety_Point_Create,
Life_Safety_Point_Delete,
NULL /* Timer */ },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_LIFE_SAFETY_ZONE)
{ OBJECT_LIFE_SAFETY_ZONE,
Life_Safety_Zone_Init,
Life_Safety_Zone_Count,
Life_Safety_Zone_Index_To_Instance,
Life_Safety_Zone_Valid_Instance,
Life_Safety_Zone_Object_Name,
Life_Safety_Zone_Read_Property,
Life_Safety_Zone_Write_Property,
Life_Safety_Zone_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
NULL /* Value_Lists */,
NULL /* COV */,
NULL /* COV Clear */,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Life_Safety_Zone_Create,
Life_Safety_Zone_Delete,
NULL /* Timer */ },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_LOAD_CONTROL)
{ OBJECT_LOAD_CONTROL,
Load_Control_Init,
Load_Control_Count,
Load_Control_Index_To_Instance,
Load_Control_Valid_Instance,
Load_Control_Object_Name,
Load_Control_Read_Property,
Load_Control_Write_Property,
Load_Control_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
NULL /* Value_Lists */,
NULL /* COV */,
NULL /* COV Clear */,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Load_Control_Create,
Load_Control_Delete,
Load_Control_Timer },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_LIGHTING_OUTPUT)
{ OBJECT_LIGHTING_OUTPUT,
Lighting_Output_Init,
Lighting_Output_Count,
Lighting_Output_Index_To_Instance,
Lighting_Output_Valid_Instance,
Lighting_Output_Object_Name,
Lighting_Output_Read_Property,
Lighting_Output_Write_Property,
Lighting_Output_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
NULL /* Value_Lists */,
NULL /* COV */,
NULL /* COV Clear */,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Lighting_Output_Create,
Lighting_Output_Delete,
Lighting_Output_Timer },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_CHANNEL)
{ OBJECT_CHANNEL,
Channel_Init,
Channel_Count,
Channel_Index_To_Instance,
Channel_Valid_Instance,
Channel_Object_Name,
Channel_Read_Property,
Channel_Write_Property,
Channel_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
NULL /* Value_Lists */,
NULL /* COV */,
NULL /* COV Clear */,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Channel_Create,
Channel_Delete,
NULL /* Timer */ },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_BINARY_LIGHTING_OUTPUT)
{ OBJECT_BINARY_LIGHTING_OUTPUT,
Binary_Lighting_Output_Init,
Binary_Lighting_Output_Count,
Binary_Lighting_Output_Index_To_Instance,
Binary_Lighting_Output_Valid_Instance,
Binary_Lighting_Output_Object_Name,
Binary_Lighting_Output_Read_Property,
Binary_Lighting_Output_Write_Property,
Binary_Lighting_Output_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
NULL /* Value_Lists */,
NULL /* COV */,
NULL /* COV Clear */,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Binary_Lighting_Output_Create,
Binary_Lighting_Output_Delete,
Binary_Lighting_Output_Timer },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_COLOR)
{ OBJECT_COLOR,
Color_Init,
Color_Count,
Color_Index_To_Instance,
Color_Valid_Instance,
Color_Object_Name,
Color_Read_Property,
Color_Write_Property,
Color_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
NULL /* Value_Lists */,
NULL /* COV */,
NULL /* COV Clear */,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Color_Create,
Color_Delete,
Color_Timer },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_COLOR_TEMPERATURE)
{ OBJECT_COLOR_TEMPERATURE,
Color_Temperature_Init,
Color_Temperature_Count,
Color_Temperature_Index_To_Instance,
Color_Temperature_Valid_Instance,
Color_Temperature_Object_Name,
Color_Temperature_Read_Property,
Color_Temperature_Write_Property,
Color_Temperature_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
NULL /* Value_Lists */,
NULL /* COV */,
NULL /* COV Clear */,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Color_Temperature_Create,
Color_Temperature_Delete,
Color_Temperature_Timer },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_FILE)
{ OBJECT_FILE,
bacfile_init,
bacfile_count,
bacfile_index_to_instance,
bacfile_valid_instance,
bacfile_object_name,
bacfile_read_property,
bacfile_write_property,
BACfile_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
NULL /* Value_Lists */,
NULL /* COV */,
NULL /* COV Clear */,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
bacfile_create,
bacfile_delete,
NULL /* Timer */ },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_STRUCTURED_VIEW)
{ OBJECT_STRUCTURED_VIEW,
Structured_View_Init,
Structured_View_Count,
Structured_View_Index_To_Instance,
Structured_View_Valid_Instance,
Structured_View_Object_Name,
Structured_View_Read_Property,
NULL /* Write_Property */,
Structured_View_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
NULL /* Value_Lists */,
NULL /* COV */,
NULL /* COV Clear */,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Structured_View_Create,
Structured_View_Delete,
NULL /* Timer */ },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_BITSTRING_VALUE)
{ OBJECT_BITSTRING_VALUE,
BitString_Value_Init,
BitString_Value_Count,
BitString_Value_Index_To_Instance,
BitString_Value_Valid_Instance,
BitString_Value_Object_Name,
BitString_Value_Read_Property,
BitString_Value_Write_Property,
BitString_Value_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
BitString_Value_Encode_Value_List,
BitString_Value_Change_Of_Value,
BitString_Value_Change_Of_Value_Clear,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
BitString_Value_Create,
BitString_Value_Delete,
NULL /* Timer */ },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_CHARACTERSTRING_VALUE)
{ OBJECT_CHARACTERSTRING_VALUE,
CharacterString_Value_Init,
CharacterString_Value_Count,
CharacterString_Value_Index_To_Instance,
CharacterString_Value_Valid_Instance,
CharacterString_Value_Object_Name,
CharacterString_Value_Read_Property,
CharacterString_Value_Write_Property,
CharacterString_Value_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
CharacterString_Value_Encode_Value_List,
CharacterString_Value_Change_Of_Value,
CharacterString_Value_Change_Of_Value_Clear,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
CharacterString_Value_Create,
CharacterString_Value_Delete,
NULL /* Timer */ },
#endif
#if defined(CONFIG_BACNET_BASIC_OBJECT_PROGRAM)
{ OBJECT_BITSTRING_VALUE,
Program_Init,
Program_Count,
Program_Index_To_Instance,
Program_Valid_Instance,
Program_Object_Name,
Program_Read_Property,
Program_Write_Property,
Program_Property_Lists,
NULL /* ReadRangeInfo */,
NULL /* Iterator */,
NULL /* Value_Lists */,
NULL /* COV */,
NULL /* COV Clear */,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
Program_Create,
Program_Delete,
Program_Timer },
#endif
{
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 */,
NULL /* Add_List_Element */,
NULL /* Remove_List_Element */,
NULL /* Create */,
NULL /* Delete */,
NULL /* Timer */
}
};
/** 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;
while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) {
/* handle each object type */
if (pObject->Object_Type == Object_Type) {
return (pObject);
}
pObject++;
}
return (NULL);
}
/** Try to find a rr_info_function helper function for the requested object
* type.
* @ingroup ObjIntf
*
* @param object_type [in] The type of BACnet Object the handler wants to
* access.
* @return Pointer to the object helper function that implements the
* ReadRangeInfo function, Object_RR_Info, for this type of Object on
* success, else a NULL pointer if the type of Object isn't supported
* or doesn't have a ReadRangeInfo function.
*/
rr_info_function Device_Objects_RR_Info(BACNET_OBJECT_TYPE object_type)
{
struct object_functions *pObject = NULL;
pObject = Device_Objects_Find_Functions(object_type);
return (pObject != NULL ? pObject->Object_RR_Info : NULL);
}
/** For a given object type, returns the special property list.
* This function is used for ReadPropertyMultiple calls which want
* just Required, just Optional, or All properties.
* @ingroup ObjIntf
*
* @param object_type [in] The desired BACNET_OBJECT_TYPE whose properties
* are to be listed.
* @param pPropertyList [out] Reference to the structure which will, on return,
* list, separately, the Required, Optional, and Proprietary object
* properties with their counts.
*/
void Device_Objects_Property_List(
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
struct special_property_list_t *pPropertyList)
{
struct object_functions *pObject = NULL;
(void)object_instance;
pPropertyList->Required.pList = NULL;
pPropertyList->Optional.pList = NULL;
pPropertyList->Proprietary.pList = NULL;
/* If we can find an entry for the required object type
* and there is an Object_List_RPM fn ptr then call it
* to populate the pointers to the individual list counters.
*/
pObject = Device_Objects_Find_Functions(object_type);
if ((pObject != NULL) && (pObject->Object_RPM_List != NULL)) {
pObject->Object_RPM_List(
&pPropertyList->Required.pList, &pPropertyList->Optional.pList,
&pPropertyList->Proprietary.pList);
}
/* Fetch the counts if available otherwise zero them */
pPropertyList->Required.count = pPropertyList->Required.pList == NULL
? 0
: property_list_count(pPropertyList->Required.pList);
pPropertyList->Optional.count = pPropertyList->Optional.pList == NULL
? 0
: property_list_count(pPropertyList->Optional.pList);
pPropertyList->Proprietary.count = pPropertyList->Proprietary.pList == NULL
? 0
: property_list_count(pPropertyList->Proprietary.pList);
return;
}
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Device_Properties_Required[] = {
/* List of Required properties in this object */
PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME,
PROP_OBJECT_TYPE,
PROP_SYSTEM_STATUS,
PROP_VENDOR_NAME,
PROP_VENDOR_IDENTIFIER,
PROP_MODEL_NAME,
PROP_FIRMWARE_REVISION,
PROP_APPLICATION_SOFTWARE_VERSION,
PROP_PROTOCOL_VERSION,
PROP_PROTOCOL_REVISION,
PROP_PROTOCOL_SERVICES_SUPPORTED,
PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED,
PROP_OBJECT_LIST,
PROP_MAX_APDU_LENGTH_ACCEPTED,
PROP_SEGMENTATION_SUPPORTED,
PROP_APDU_TIMEOUT,
PROP_NUMBER_OF_APDU_RETRIES,
PROP_DEVICE_ADDRESS_BINDING,
PROP_DATABASE_REVISION,
-1
};
static const int Device_Properties_Optional[] = {
/* List of Optional properties in this object */
#if defined(BACDL_MSTP)
PROP_MAX_MASTER,
PROP_MAX_INFO_FRAMES,
#endif
PROP_DESCRIPTION,
PROP_LOCAL_TIME,
PROP_UTC_OFFSET,
PROP_LOCAL_DATE,
PROP_DAYLIGHT_SAVINGS_STATUS,
PROP_LOCATION,
#if (BACNET_COV_SUBSCRIPTIONS_SIZE > 0)
PROP_ACTIVE_COV_SUBSCRIPTIONS,
#endif
#if defined(BACNET_TIME_MASTER)
PROP_TIME_SYNCHRONIZATION_RECIPIENTS,
PROP_TIME_SYNCHRONIZATION_INTERVAL,
PROP_ALIGN_INTERVALS,
PROP_INTERVAL_OFFSET,
#endif
PROP_SERIAL_NUMBER,
PROP_TIME_OF_DEVICE_RESTART,
-1
};
static const int Device_Properties_Proprietary[] = {
/* List of Proprietary properties in this object */
-1
};
/**
* @brief Returns the list of required, optional, and proprietary properties
* for the Device object.
* @param pRequired [out] Pointer to the list of required properties
* @param pOptional [out] Pointer to the list of optional properties
* @param pProprietary [out] Pointer to the list of proprietary properties
* @note The lists are terminated with -1.
* @note The lists are not allocated, so do not free them.
* @note The lists are static, so do not modify them.
* @ingroup ObjIntf
*/
void Device_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Device_Properties_Required;
}
if (pOptional) {
*pOptional = Device_Properties_Optional;
}
if (pProprietary) {
*pProprietary = Device_Properties_Proprietary;
}
return;
}
/**
* @brief Determine if the object property is a member of this object instance
* @param object_type - object type of the object
* @param object_instance - object-instance number of the object
* @param object_property - object-property to be checked
* @return true if the property is a member of this object instance
*/
bool Device_Objects_Property_List_Member(
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_ID object_property)
{
bool found = false;
struct special_property_list_t property_list = { 0 };
Device_Objects_Property_List(object_type, object_instance, &property_list);
found = property_list_member(property_list.Required.pList, object_property);
if (!found) {
found =
property_list_member(property_list.Optional.pList, object_property);
}
if (!found) {
found = property_list_member(
property_list.Proprietary.pList, object_property);
}
return found;
}
/* 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. */
/* local data */
static uint32_t Object_Instance_Number = BACNET_MAX_INSTANCE;
static BACNET_CHARACTER_STRING My_Object_Name;
static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL;
static const char *Device_Name_Default = BACNET_DEVICE_OBJECT_NAME;
static const char *Device_Vendor_Name_Default = BACNET_VENDOR_NAME;
static uint16_t Vendor_Identifier = BACNET_VENDOR_ID;
static const char *Model_Name = BACNET_DEVICE_MODEL_NAME;
static const char *Application_Software_Version = BACNET_DEVICE_VERSION;
static const char *Firmware_Revision = BACNET_VERSION_TEXT;
static const char *Device_Location_Default = BACNET_DEVICE_LOCATION_NAME;
static const char *Device_Description_Default = BACNET_DEVICE_DESCRIPTION;
static uint32_t Database_Revision;
static BACNET_REINITIALIZED_STATE Reinitialize_State = BACNET_REINIT_IDLE;
static BACNET_CHARACTER_STRING Reinit_Password;
static write_property_function Device_Write_Property_Store_Callback;
static uint8_t Device_UUID[16];
static const char *Serial_Number = BACNET_DEVICE_SERIAL_NUMBER;
static BACNET_TIMESTAMP Time_Of_Device_Restart;
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 int16_t UTC_Offset = 5 * 60;
static bool Daylight_Savings_Status = false; /* rely on OS */
#if defined(BACNET_TIME_MASTER)
static bool Align_Intervals;
static uint32_t Interval_Minutes;
static uint32_t Interval_Offset_Minutes;
/* Time_Synchronization_Recipients */
#endif
/**
* @brief Sets the ReinitializeDevice password
*
* The password shall be a null terminated C string of up to
* 20 ASCII characters for those devices that require the password.
*
* For those devices that do not require a password, set to NULL or
* point to a zero length C string (null terminated).
*
* @param the ReinitializeDevice password; can be NULL or empty string
*/
bool Device_Reinitialize_Password_Set(const char *password)
{
characterstring_init_ansi(&Reinit_Password, password);
return true;
}
/**
* @brief Commands a Device re-initialization, to a given state.
* The request's password must match for the operation to succeed.
* This implementation provides a framework, but doesn't
* actually *DO* anything.
* @note You could use a mix of states and passwords to multiple outcomes.
* @note You probably want to restart *after* the simple ack has been sent
* from the return handler, so just set a local flag here.
* @ingroup ObjIntf
*
* @param rd_data [in,out] The information from the RD request.
* On failure, the error class and code will be set.
* @return True if succeeds (password is correct), else False.
*/
bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data)
{
bool status = false;
bool password_success = false;
unsigned i;
/* From 16.4.1.1.2 Password
This optional parameter shall be a CharacterString of up to
20 characters. For those devices that require the password as a
protection, the service request shall be denied if the parameter
is absent or if the password is incorrect. For those devices that
do not require a password, this parameter shall be ignored.*/
if (characterstring_length(&Reinit_Password) > 0) {
if (characterstring_length(&rd_data->password) > 20) {
rd_data->error_class = ERROR_CLASS_SERVICES;
rd_data->error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
} else if (characterstring_same(&rd_data->password, &Reinit_Password)) {
password_success = true;
} else {
rd_data->error_class = ERROR_CLASS_SECURITY;
rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE;
}
} else {
password_success = true;
}
if (password_success) {
switch (rd_data->state) {
case BACNET_REINIT_COLDSTART:
case BACNET_REINIT_WARMSTART:
dcc_set_status_duration(COMMUNICATION_ENABLE, 0);
/* Note: you could use a mix of state
and password to multiple things */
/* note: you probably want to restart *after* the
simple ack has been sent from the return handler
so just set a flag from here */
Reinitialize_State = rd_data->state;
status = true;
break;
case BACNET_REINIT_STARTBACKUP:
case BACNET_REINIT_ENDBACKUP:
case BACNET_REINIT_STARTRESTORE:
case BACNET_REINIT_ENDRESTORE:
case BACNET_REINIT_ABORTRESTORE:
if (dcc_communication_disabled()) {
rd_data->error_class = ERROR_CLASS_SERVICES;
rd_data->error_code = ERROR_CODE_COMMUNICATION_DISABLED;
} else {
rd_data->error_class = ERROR_CLASS_SERVICES;
rd_data->error_code =
ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED;
}
break;
case BACNET_REINIT_ACTIVATE_CHANGES:
/* note: activate changes *after* the simple ack is sent */
for (i = 0; i < Network_Port_Count(); i++) {
Network_Port_Changes_Pending_Activate(
Network_Port_Index_To_Instance(i));
}
Reinitialize_State = rd_data->state;
status = true;
break;
default:
rd_data->error_class = ERROR_CLASS_SERVICES;
rd_data->error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
break;
}
}
return status;
}
BACNET_REINITIALIZED_STATE Device_Reinitialized_State(void)
{
return Reinitialize_State;
}
bool Device_Reinitialize_State_Set(BACNET_REINITIALIZED_STATE state)
{
Reinitialize_State = state;
return true;
}
unsigned Device_Count(void)
{
return 1;
}
uint32_t Device_Index_To_Instance(unsigned index)
{
(void)index;
return Object_Instance_Number;
}
/** 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)
{
return Object_Instance_Number;
}
bool Device_Set_Object_Instance_Number(uint32_t object_id)
{
bool status = true; /* return value */
if (object_id <= BACNET_MAX_INSTANCE) {
Object_Instance_Number = object_id;
} else {
status = false;
}
return status;
}
bool Device_Valid_Object_Instance_Number(uint32_t object_id)
{
return (Object_Instance_Number == object_id);
}
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(const 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;
}
/**
* @brief Initialize the Device Object Name with an ANSI C string
* @param value [in] The object name as a null-terminated string
* @return True on success, else False
*/
bool Device_Object_Name_ANSI_Init(const char *value)
{
return characterstring_init_ansi(&My_Object_Name, value);
}
/**
* @brief Get the Device Object Name as a C string
* @return The object name as a null-terminated string
*/
char *Device_Object_Name_ANSI(void)
{
return (char *)characterstring_value(&My_Object_Name);
}
/**
* @brief Initialize a UUID for storing the unique identifier of this device
* @note A Universally Unique IDentifier (UUID) - also called a
* Global Unique IDentifier (GUID) - is a 128-bit value, see RFC 4122.
*
* 4.4. Algorithms for Creating a UUID from Truly Random or
* Pseudo-Random Numbers
*
* The version 4 UUID is meant for generating UUIDs from truly-random or
* pseudo-random numbers.
*
* The algorithm is as follows:
*
* o Set the two most significant bits (bits 6 and 7) of the
* clock_seq_hi_and_reserved to zero and one, respectively.
*
* o Set the four most significant bits (bits 12 through 15) of the
* time_hi_and_version field to the 4-bit version number from
* Section 4.1.3.
*
* o Set all the other bits to randomly (or pseudo-randomly) chosen
* values.
*/
void Device_UUID_Init(void)
{
unsigned i = 0;
/* 1. Generate 16 random bytes = 128 bits */
for (i = 0; i < sizeof(Device_UUID); i++) {
Device_UUID[i] = rand() % 256;
}
/* 2. Adjust certain bits according to RFC 4122 section 4.4.
This just means do the following
(a) set the high nibble of the 7th byte equal to 4 and
(b) set the two most significant bits of the 9th byte to 10'B,
so the high nibble will be one of {8,9,A,B}.
From http://www.cryptosys.net/pki/Uuid.c.html */
Device_UUID[6] = 0x40 | (Device_UUID[6] & 0x0f);
Device_UUID[8] = 0x80 | (Device_UUID[8] & 0x3f);
}
/**
* @brief Set the UUID for this device
* @param new_uuid [in] The new UUID to set
* @param length [in] The length of the new UUID
*/
void Device_UUID_Set(const uint8_t *new_uuid, size_t length)
{
if (new_uuid && (length == sizeof(Device_UUID))) {
memcpy(Device_UUID, new_uuid, sizeof(Device_UUID));
}
}
/**
* @brief Get the UUID for this device
* @param uuid [out] The UUID of this device
* @param length [in] The length of the UUID
*/
void Device_UUID_Get(uint8_t *uuid, size_t length)
{
if (uuid && (length == sizeof(Device_UUID))) {
memcpy(uuid, Device_UUID, sizeof(Device_UUID));
}
}
BACNET_DEVICE_STATUS Device_System_Status(void)
{
return System_Status;
}
int Device_Set_System_Status(BACNET_DEVICE_STATUS status, bool local)
{
/*return value - 0 = ok, -1 = bad value, -2 = not allowed */
int result = -1;
(void)local;
if (status < MAX_DEVICE_STATUS) {
System_Status = status;
result = 0;
}
return result;
}
const char *Device_Vendor_Name(void)
{
return Device_Vendor_Name_Default;
}
bool Device_Set_Vendor_Name(const char *name, size_t length)
{
(void)length;
Device_Vendor_Name_Default = name ? name : BACNET_VENDOR_NAME;
return true;
}
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)
{
(void)length;
Model_Name = name ? name : BACNET_DEVICE_MODEL_NAME;
return true;
}
const char *Device_Firmware_Revision(void)
{
return Firmware_Revision;
}
bool Device_Set_Firmware_Revision(const char *name, size_t length)
{
(void)length;
Firmware_Revision = name ? name : BACNET_VERSION_TEXT;
return true;
}
const char *Device_Application_Software_Version(void)
{
return Application_Software_Version;
}
bool Device_Set_Application_Software_Version(const char *name, size_t length)
{
(void)length;
Application_Software_Version = name ? name : BACNET_DEVICE_VERSION;
return true;
}
const char *Device_Description(void)
{
return Device_Description_Default;
}
bool Device_Set_Description(const char *name, size_t length)
{
(void)length;
Device_Description_Default = name ? name : BACNET_DEVICE_DESCRIPTION;
return true;
}
const char *Device_Location(void)
{
return Device_Location_Default;
}
bool Device_Set_Location(const char *name, size_t length)
{
(void)length;
Device_Location_Default = name ? name : BACNET_DEVICE_LOCATION_NAME;
return true;
}
/**
* @brief Get the device serial-number property value.
* @return The device serial-number, as a character string.
*/
const char *Device_Serial_Number(void)
{
return Serial_Number;
}
/**
* @brief Set the device serial-number property value.
* @param str [in] The new device serial-number, as a character string.
* @param length [in] The number of characters in the string.
* @return true if the device serial-number was set, false if the value was
* too long to store in the object.
*/
bool Device_Serial_Number_Set(const char *str, size_t length)
{
(void)length;
Serial_Number = str ? str : BACNET_DEVICE_SERIAL_NUMBER;
return true;
}
void Device_Time_Of_Restart(BACNET_TIMESTAMP *time_of_restart)
{
bacapp_timestamp_copy(time_of_restart, &Time_Of_Device_Restart);
}
bool Device_Set_Time_Of_Restart(const BACNET_TIMESTAMP *time_of_restart)
{
bool status = false;
if (time_of_restart) {
bacapp_timestamp_copy(&Time_Of_Device_Restart, time_of_restart);
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;
}
/**
* @brief Get the Database Revision property of the Device Object
* @return The Database Revision property of the Device Object
*/
uint32_t Device_Database_Revision(void)
{
return Database_Revision;
}
/**
* @brief Set the Database Revision property of the Device Object
* @param revision [in] The new value for the Database Revision property
*/
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;
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(
uint32_t array_index, BACNET_OBJECT_TYPE *object_type, uint32_t *instance)
{
bool status = false;
uint32_t count = 0;
uint32_t object_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 && pObject->Object_Index_To_Instance) {
object_index -= count;
count = pObject->Object_Count();
if (object_index < count) {
*object_type = pObject->Object_Type;
*instance = pObject->Object_Index_To_Instance(object_index);
status = true;
break;
}
}
pObject++;
}
return status;
}
/**
* @brief Encode a BACnetARRAY property element
* @param object_instance [in] BACnet network port object instance number
* @param array_index [in] array index requested:
* 0 to N for individual array members
* @param apdu [out] Buffer in which the APDU contents are built, or NULL to
* return the length of buffer if it had been built
* @return The length of the apdu encoded or
* BACNET_STATUS_ERROR for ERROR_CODE_INVALID_ARRAY_INDEX
*/
int Device_Object_List_Element_Encode(
uint32_t object_instance, BACNET_ARRAY_INDEX array_index, uint8_t *apdu)
{
int apdu_len = BACNET_STATUS_ERROR;
BACNET_OBJECT_TYPE object_type;
uint32_t instance;
bool found;
if (object_instance == Device_Object_Instance_Number()) {
/* single element is zero based, add 1 for BACnetARRAY which is one
* based */
array_index++;
found =
Device_Object_List_Identifier(array_index, &object_type, &instance);
if (found) {
apdu_len =
encode_application_object_id(apdu, object_type, instance);
}
}
return apdu_len;
}
/** Determine if we have an object with the given object_name.
* If the object_type and object_instance pointers are not null,
* and the lookup succeeds, they will be given the resulting values.
* @param object_name [in] The desired Object Name to look for.
* @param object_type [out] The BACNET_OBJECT_TYPE of the matching Object.
* @param object_instance [out] The object instance number of the matching
* Object.
* @return True on success or else False if not found.
*/
bool Device_Valid_Object_Name(
const BACNET_CHARACTER_STRING *object_name1,
BACNET_OBJECT_TYPE *object_type,
uint32_t *object_instance)
{
bool found = false;
BACNET_OBJECT_TYPE type = OBJECT_NONE;
uint32_t instance;
uint32_t 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 = 1; i <= max_objects; i++) {
check_id = Device_Object_List_Identifier(i, &type, &instance);
if (check_id) {
pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE)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(
BACNET_OBJECT_TYPE object_type, uint32_t object_instance)
{
bool status = false; /* return value */
struct object_functions *pObject = NULL;
pObject = Device_Objects_Find_Functions((BACNET_OBJECT_TYPE)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;
}
static void Update_Current_Time(void)
{
datetime_local(
&Local_Date, &Local_Time, &UTC_Offset, &Daylight_Savings_Status);
}
void Device_getCurrentDateTime(BACNET_DATE_TIME *DateTime)
{
Update_Current_Time();
DateTime->date = Local_Date;
DateTime->time = Local_Time;
}
int32_t Device_UTC_Offset(void)
{
Update_Current_Time();
return UTC_Offset;
}
void Device_UTC_Offset_Set(int16_t offset)
{
UTC_Offset = offset;
}
bool Device_Daylight_Savings_Status(void)
{
return Daylight_Savings_Status;
}
#if defined(BACNET_TIME_MASTER)
/**
* Sets the time sync interval in minutes
*
* @param flag
* This property, of type BOOLEAN, specifies whether (TRUE)
* or not (FALSE) clock-aligned periodic time synchronization is
* enabled. If periodic time synchronization is enabled and the
* time synchronization interval is a factor of (divides without
* remainder) an hour or day, then the beginning of the period
* specified for time synchronization shall be aligned to the hour or
* day, respectively. If this property is present, it shall be writable.
*/
bool Device_Align_Intervals_Set(bool flag)
{
Align_Intervals = flag;
return true;
}
bool Device_Align_Intervals(void)
{
return Align_Intervals;
}
/**
* Sets the time sync interval in minutes
*
* @param minutes
* This property, of type Unsigned, specifies the periodic
* interval in minutes at which TimeSynchronization and
* UTCTimeSynchronization requests shall be sent. If this
* property has a value of zero, then periodic time synchronization is
* disabled. If this property is present, it shall be writable.
*/
bool Device_Time_Sync_Interval_Set(uint32_t minutes)
{
Interval_Minutes = minutes;
return true;
}
uint32_t Device_Time_Sync_Interval(void)
{
return Interval_Minutes;
}
/**
* Sets the time sync interval offset value.
*
* @param minutes
* This property, of type Unsigned, specifies the offset in
* minutes from the beginning of the period specified for time
* synchronization until the actual time synchronization requests
* are sent. The offset used shall be the value of Interval_Offset
* modulo the value of Time_Synchronization_Interval;
* e.g., if Interval_Offset has the value 31 and
* Time_Synchronization_Interval is 30, the offset used shall be 1.
* Interval_Offset shall have no effect if Align_Intervals is
* FALSE. If this property is present, it shall be writable.
*/
bool Device_Interval_Offset_Set(uint32_t minutes)
{
Interval_Offset_Minutes = minutes;
return true;
}
uint32_t Device_Interval_Offset(void)
{
return Interval_Offset_Minutes;
}
#endif
/* 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 */
BACNET_BIT_STRING bit_string = { 0 };
BACNET_CHARACTER_STRING char_string = { 0 };
uint32_t i = 0;
uint32_t count = 0;
uint8_t *apdu = NULL;
struct object_functions *pObject = NULL;
uint16_t apdu_max = 0;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
apdu_max = rpdata->application_data_len;
switch ((int)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, Device_Description_Default);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_SYSTEM_STATUS:
apdu_len =
encode_application_enumerated(&apdu[0], Device_System_Status());
break;
case PROP_VENDOR_NAME:
characterstring_init_ansi(&char_string, Device_Vendor_Name_Default);
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, Firmware_Revision);
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, Device_Location_Default);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_LOCAL_TIME:
Update_Current_Time();
apdu_len = encode_application_time(&apdu[0], &Local_Time);
break;
case PROP_UTC_OFFSET:
Update_Current_Time();
apdu_len = encode_application_signed(&apdu[0], UTC_Offset);
break;
case PROP_LOCAL_DATE:
Update_Current_Time();
apdu_len = encode_application_date(&apdu[0], &Local_Date);
break;
case PROP_DAYLIGHT_SAVINGS_STATUS:
Update_Current_Time();
apdu_len =
encode_application_boolean(&apdu[0], Daylight_Savings_Status);
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;
while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) {
if ((pObject->Object_Count) && (pObject->Object_Count() > 0)) {
bitstring_set_bit(
&bit_string, (uint8_t)pObject->Object_Type, true);
}
pObject++;
}
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_OBJECT_LIST:
count = Device_Object_List_Count();
apdu_len = bacnet_array_encode(
rpdata->object_instance, rpdata->array_index,
Device_Object_List_Element_Encode, count, apdu, apdu_max);
if (apdu_len == BACNET_STATUS_ABORT) {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
} else if (apdu_len == BACNET_STATUS_ERROR) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
}
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:
apdu_len = address_list_encode(&apdu[0], apdu_max);
break;
case PROP_DATABASE_REVISION:
apdu_len = encode_application_unsigned(
&apdu[0], Device_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
#if defined(BACNET_TIME_MASTER)
case PROP_TIME_SYNCHRONIZATION_RECIPIENTS:
apdu_len = handler_timesync_encode_recipients(&apdu[0], MAX_APDU);
if (apdu_len < 0) {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
}
break;
case PROP_TIME_SYNCHRONIZATION_INTERVAL:
apdu_len = encode_application_unsigned(
&apdu[0], Device_Time_Sync_Interval());
break;
case PROP_ALIGN_INTERVALS:
apdu_len =
encode_application_boolean(&apdu[0], Device_Align_Intervals());
break;
case PROP_INTERVAL_OFFSET:
apdu_len =
encode_application_unsigned(&apdu[0], Device_Interval_Offset());
break;
#endif
#if (BACNET_COV_SUBSCRIPTIONS_SIZE > 0)
case PROP_ACTIVE_COV_SUBSCRIPTIONS:
if ((apdu_len = handler_cov_encode_subscriptions(
&apdu[0], apdu_max)) < 0) {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
}
break;
#endif
case PROP_SERIAL_NUMBER:
characterstring_init_ansi(&char_string, Serial_Number);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_TIME_OF_DEVICE_RESTART:
apdu_len =
bacapp_encode_timestamp(&apdu[0], &Time_Of_Device_Restart);
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = BACNET_STATUS_ERROR;
break;
}
return apdu_len;
}
/** Looks up the common Object and Property, and encodes its Value in an
* APDU. Sets the error class and code if request is not appropriate.
* @param pObject - object table
* @param rpdata [in,out] Structure with the requested Object & Property info
* on entry, and APDU message on return.
* @return The length of the APDU on success, else BACNET_STATUS_ERROR
*/
static int Read_Property_Common(
const struct object_functions *pObject, BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = BACNET_STATUS_ERROR;
BACNET_CHARACTER_STRING char_string;
uint8_t *apdu = NULL;
#if (BACNET_PROTOCOL_REVISION >= 14)
struct special_property_list_t property_list;
#endif
if ((rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
if (property_list_common(rpdata->object_property)) {
apdu_len = property_list_common_encode(rpdata, Object_Instance_Number);
} else if (rpdata->object_property == PROP_OBJECT_NAME) {
characterstring_init_ansi(&char_string, "");
if (pObject->Object_Name) {
(void)pObject->Object_Name(rpdata->object_instance, &char_string);
}
apdu_len = encode_application_character_string(&apdu[0], &char_string);
#if (BACNET_PROTOCOL_REVISION >= 14)
} else if (rpdata->object_property == PROP_PROPERTY_LIST) {
Device_Objects_Property_List(
rpdata->object_type, rpdata->object_instance, &property_list);
apdu_len = property_list_encode(
rpdata, property_list.Required.pList, property_list.Optional.pList,
property_list.Proprietary.pList);
#endif
} else if (pObject->Object_Read_Property) {
apdu_len = pObject->Object_Read_Property(rpdata);
}
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)) {
apdu_len = Read_Property_Common(pObject, rpdata);
} else {
rpdata->error_class = ERROR_CLASS_OBJECT;
rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
} else {
rpdata->error_class = ERROR_CLASS_OBJECT;
rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
return apdu_len;
}
/* returns true if successful */
bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
int len = 0;
BACNET_APPLICATION_DATA_VALUE value = { 0 };
BACNET_OBJECT_TYPE object_type = OBJECT_NONE;
uint32_t object_instance = 0;
int result = 0;
#if defined(BACNET_TIME_MASTER)
uint32_t minutes = 0;
#endif
/* decode the some of the request */
len = bacapp_decode_known_property(
wp_data->application_data, wp_data->application_data_len, &value,
wp_data->object_type, wp_data->object_property);
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
/* FIXME: len < application_data_len: more data? */
switch (wp_data->object_property) {
case PROP_OBJECT_IDENTIFIER:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_OBJECT_ID);
if (status) {
if ((value.type.Object_Id.type == OBJECT_DEVICE) &&
(Device_Set_Object_Instance_Number(
value.type.Object_Id.instance))) {
/* we could send an I-Am broadcast to let the world know */
} else {
status = false;
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
case PROP_NUMBER_OF_APDU_RETRIES:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT);
if (status) {
/* FIXME: bounds check? */
apdu_retries_set((uint8_t)value.type.Unsigned_Int);
}
break;
case PROP_APDU_TIMEOUT:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT);
if (status) {
/* FIXME: bounds check? */
apdu_timeout_set((uint16_t)value.type.Unsigned_Int);
}
break;
case PROP_VENDOR_IDENTIFIER:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT);
if (status) {
/* FIXME: bounds check? */
Device_Set_Vendor_Identifier((uint16_t)value.type.Unsigned_Int);
}
break;
case PROP_SYSTEM_STATUS:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED);
if (status) {
result = Device_Set_System_Status(
(BACNET_DEVICE_STATUS)value.type.Enumerated, false);
if (result != 0) {
/* result: - 0 = ok, -1 = bad value, -2 = not allowed */
status = false;
wp_data->error_class = ERROR_CLASS_PROPERTY;
if (result == -1) {
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
} else {
wp_data->error_code =
ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED;
}
}
}
break;
case PROP_OBJECT_NAME:
status = write_property_string_valid(
wp_data, &value, characterstring_capacity(&My_Object_Name));
if (status) {
/* All the object names in a device must be unique */
if (Device_Valid_Object_Name(
&value.type.Character_String, &object_type,
&object_instance)) {
if ((object_type == wp_data->object_type) &&
(object_instance == wp_data->object_instance)) {
/* writing same name to same object */
status = true;
} else {
status = false;
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_DUPLICATE_NAME;
}
} else {
Device_Set_Object_Name(&value.type.Character_String);
}
}
break;
case PROP_LOCATION:
status = write_property_empty_string_valid(
wp_data, &value, MAX_DEV_LOC_LEN);
if (status) {
Device_Set_Location(
characterstring_value(&value.type.Character_String),
characterstring_length(&value.type.Character_String));
}
break;
case PROP_DESCRIPTION:
status = write_property_empty_string_valid(
wp_data, &value, MAX_DEV_DESC_LEN);
if (status) {
Device_Set_Description(
characterstring_value(&value.type.Character_String),
characterstring_length(&value.type.Character_String));
}
break;
case PROP_MODEL_NAME:
status = write_property_empty_string_valid(
wp_data, &value, MAX_DEV_MOD_LEN);
if (status) {
Device_Set_Model_Name(
characterstring_value(&value.type.Character_String),
characterstring_length(&value.type.Character_String));
}
break;
#if defined(BACNET_TIME_MASTER)
case PROP_TIME_SYNCHRONIZATION_INTERVAL:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT);
if (status) {
if (value.type.Unsigned_Int < 65535) {
minutes = value.type.Unsigned_Int;
Device_Time_Sync_Interval_Set(minutes);
status = true;
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
case PROP_ALIGN_INTERVALS:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_BOOLEAN);
if (status) {
Device_Align_Intervals_Set(value.type.Boolean);
status = true;
}
break;
case PROP_INTERVAL_OFFSET:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT);
if (status) {
if (value.type.Unsigned_Int < 65535) {
minutes = value.type.Unsigned_Int;
Device_Interval_Offset_Set(minutes);
status = true;
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
#endif
case PROP_UTC_OFFSET:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_SIGNED_INT);
if (status) {
if ((value.type.Signed_Int < (12 * 60)) &&
(value.type.Signed_Int > (-12 * 60))) {
Device_UTC_Offset_Set(value.type.Signed_Int);
status = true;
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
#if defined(BACDL_MSTP)
case PROP_MAX_INFO_FRAMES:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT);
if (status) {
if (value.type.Unsigned_Int <= 255) {
dlmstp_set_max_info_frames(
(uint8_t)value.type.Unsigned_Int);
} else {
status = false;
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
case PROP_MAX_MASTER:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT);
if (status) {
if ((value.type.Unsigned_Int > 0) &&
(value.type.Unsigned_Int <= 127)) {
dlmstp_set_max_master((uint8_t)value.type.Unsigned_Int);
} else {
status = false;
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
#endif
case PROP_TIME_OF_DEVICE_RESTART:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_TIMESTAMP);
if (status) {
bacapp_timestamp_copy(
&Time_Of_Device_Restart, &value.type.Time_Stamp);
}
break;
default:
if (property_lists_member(
Device_Properties_Required, Device_Properties_Optional,
Device_Properties_Proprietary, wp_data->object_property)) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
}
break;
}
return status;
}
/**
* @brief Handles the writing of the object name property
* @param wp_data [in,out] WriteProperty data structure
* @param Object_Write_Property object specific function to write the property
* @return True on success, else False if there is an error.
*/
static bool Device_Write_Property_Object_Name(
BACNET_WRITE_PROPERTY_DATA *wp_data,
write_property_function Object_Write_Property)
{
bool status = false; /* return value */
int len = 0;
BACNET_CHARACTER_STRING value;
BACNET_OBJECT_TYPE object_type = OBJECT_NONE;
uint32_t object_instance = 0;
int apdu_size = 0;
const uint8_t *apdu = NULL;
if (!wp_data) {
return false;
}
apdu = wp_data->application_data;
apdu_size = wp_data->application_data_len;
len = bacnet_character_string_application_decode(apdu, apdu_size, &value);
if (len > 0) {
if ((characterstring_encoding(&value) != CHARACTER_ANSI_X34) ||
(characterstring_length(&value) == 0) ||
(!characterstring_printable(&value))) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
} else {
status = true;
}
} else if (len == 0) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
if (status) {
/* All the object names in a device must be unique */
if (Device_Valid_Object_Name(&value, &object_type, &object_instance)) {
if ((object_type == wp_data->object_type) &&
(object_instance == wp_data->object_instance)) {
/* writing same name to same object - but is it writable? */
status = Object_Write_Property(wp_data);
} else {
/* name already exists in some object */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_DUPLICATE_NAME;
status = false;
}
} else {
status = Object_Write_Property(wp_data);
}
}
return status;
}
/**
* @brief Set the callback for a WriteProperty successful operation
* @param cb [in] The function to be called, or NULL to disable
*/
void Device_Write_Property_Store_Callback_Set(write_property_function cb)
{
Device_Write_Property_Store_Callback = cb;
}
/**
* @brief Store the value of a property when WriteProperty is successful
*/
static void Device_Write_Property_Store(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
if (Device_Write_Property_Store_Callback) {
(void)Device_Write_Property_Store_Callback(wp_data);
}
}
/** Looks up the requested Object and Property, and set the new Value in it,
* if allowed.
* If the Object or Property can't be found, sets the error class and code.
* @ingroup ObjIntf
*
* @param wp_data [in,out] Structure with the desired Object and Property info
* and new Value on entry, and APDU message on return.
* @return True on success, else False if there is an error.
*/
bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* Ever the pessimist! */
struct object_functions *pObject = NULL;
/* initialize the default return values */
wp_data->error_class = ERROR_CLASS_OBJECT;
wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
pObject = Device_Objects_Find_Functions(wp_data->object_type);
if (pObject != NULL) {
if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(wp_data->object_instance)) {
if (pObject->Object_Write_Property) {
#if (BACNET_PROTOCOL_REVISION >= 14)
if (wp_data->object_property == PROP_PROPERTY_LIST) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
return false;
}
#endif
if (wp_data->object_property == PROP_OBJECT_NAME) {
status = Device_Write_Property_Object_Name(
wp_data, pObject->Object_Write_Property);
} else {
status = pObject->Object_Write_Property(wp_data);
}
if (status) {
Device_Write_Property_Store(wp_data);
}
} else {
if (Device_Objects_Property_List_Member(
wp_data->object_type, wp_data->object_instance,
wp_data->object_property)) {
/* this property is not writable */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
} else {
/* this property is not supported */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
}
}
} else {
wp_data->error_class = ERROR_CLASS_OBJECT;
wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
} else {
wp_data->error_class = ERROR_CLASS_OBJECT;
wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
return (status);
}
/**
* @brief AddListElement from an object list property
* @param list_element [in] Pointer to the BACnet_List_Element_Data structure,
* which is packed with the information from the request.
* @return The length of the apdu encoded or #BACNET_STATUS_ERROR or
* #BACNET_STATUS_ABORT or #BACNET_STATUS_REJECT.
*/
int Device_Add_List_Element(BACNET_LIST_ELEMENT_DATA *list_element)
{
int status = BACNET_STATUS_ERROR;
struct object_functions *pObject = NULL;
pObject = Device_Objects_Find_Functions(list_element->object_type);
if (pObject != NULL) {
if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(list_element->object_instance)) {
if (pObject->Object_Add_List_Element) {
status = pObject->Object_Add_List_Element(list_element);
} else {
list_element->error_class = ERROR_CLASS_PROPERTY;
list_element->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
}
} else {
list_element->error_class = ERROR_CLASS_OBJECT;
list_element->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
} else {
list_element->error_class = ERROR_CLASS_OBJECT;
list_element->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
return status;
}
/**
* @brief RemoveListElement from an object list property
* @param list_element [in] Pointer to the BACnet_List_Element_Data structure,
* which is packed with the information from the request.
* @return The length of the apdu encoded or #BACNET_STATUS_ERROR or
* #BACNET_STATUS_ABORT or #BACNET_STATUS_REJECT.
*/
int Device_Remove_List_Element(BACNET_LIST_ELEMENT_DATA *list_element)
{
int status = BACNET_STATUS_ERROR;
struct object_functions *pObject = NULL;
pObject = Device_Objects_Find_Functions(list_element->object_type);
if (pObject != NULL) {
if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(list_element->object_instance)) {
if (pObject->Object_Remove_List_Element) {
status = pObject->Object_Remove_List_Element(list_element);
} else {
list_element->error_class = ERROR_CLASS_PROPERTY;
list_element->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
}
} else {
list_element->error_class = ERROR_CLASS_OBJECT;
list_element->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
} else {
list_element->error_class = ERROR_CLASS_OBJECT;
list_element->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
return status;
}
/** Looks up the requested Object, and fills the Property Value list.
* If the Object or Property can't be found, returns false.
* @ingroup ObjHelpers
* @param [in] The object type to be looked up.
* @param [in] The object instance number to be looked up.
* @param [out] The value list
* @return True if the object instance supports this feature
* and was encoded correctly
*/
bool Device_Encode_Value_List(
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_VALUE *value_list)
{
bool status = false; /* Ever the pessimist! */
struct object_functions *pObject = NULL;
pObject = Device_Objects_Find_Functions(object_type);
if (pObject != NULL) {
if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(object_instance)) {
if (pObject->Object_Value_List) {
status =
pObject->Object_Value_List(object_instance, value_list);
}
}
}
return (status);
}
/** Checks the COV flag in the requested Object
* @ingroup ObjHelpers
* @param [in] The object type to be looked up.
* @param [in] The object instance to be looked up.
* @return True if the COV flag is set
*/
bool Device_COV(BACNET_OBJECT_TYPE object_type, uint32_t object_instance)
{
bool status = false; /* Ever the pessamist! */
struct object_functions *pObject = NULL;
pObject = Device_Objects_Find_Functions(object_type);
if (pObject != NULL) {
if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(object_instance)) {
if (pObject->Object_COV) {
status = pObject->Object_COV(object_instance);
}
}
}
return (status);
}
/** Clears the COV flag in the requested Object
* @ingroup ObjHelpers
* @param [in] The object type to be looked up.
* @param [in] The object instance to be looked up.
*/
void Device_COV_Clear(BACNET_OBJECT_TYPE object_type, uint32_t object_instance)
{
struct object_functions *pObject = NULL;
pObject = Device_Objects_Find_Functions(object_type);
if (pObject != NULL) {
if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(object_instance)) {
if (pObject->Object_COV_Clear) {
pObject->Object_COV_Clear(object_instance);
}
}
}
}
/**
* @brief Creates a child object, if supported
* @ingroup ObjHelpers
* @param data - CreateObject data, including error codes if failures
* @return true if object has been created
*/
bool Device_Create_Object(BACNET_CREATE_OBJECT_DATA *data)
{
bool status = false;
struct object_functions *pObject = NULL;
uint32_t object_instance;
pObject = Device_Objects_Find_Functions(data->object_type);
if (pObject != NULL) {
if (!pObject->Object_Create) {
/* The device supports the object type and may have
sufficient space, but does not support the creation of the
object for some other reason.*/
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_DYNAMIC_CREATION_NOT_SUPPORTED;
} else if (
pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(data->object_instance)) {
/* The object being created already exists */
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_OBJECT_IDENTIFIER_ALREADY_EXISTS;
} else {
if (data->list_of_initial_values) {
/* FIXME: add support for writing to list of initial values */
/* A property specified by the Property_Identifier in the
List of Initial Values does not support initialization
during the CreateObject service. */
data->first_failed_element_number = 1;
data->error_class = ERROR_CLASS_PROPERTY;
data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
/* and the object shall not be created */
} else {
object_instance = pObject->Object_Create(data->object_instance);
if (object_instance == BACNET_MAX_INSTANCE) {
/* The device cannot allocate the space needed
for the new object.*/
data->error_class = ERROR_CLASS_RESOURCES;
data->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT;
} else {
/* required by ACK */
data->object_instance = object_instance;
Device_Inc_Database_Revision();
status = true;
}
}
}
} else {
/* The device does not support the specified object type. */
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE;
}
return status;
}
/**
* @brief Deletes a child object, if supported
* @ingroup ObjHelpers
* @param data - DeleteObject data, including error codes if failures
* @return true if object has been deleted
*/
bool Device_Delete_Object(BACNET_DELETE_OBJECT_DATA *data)
{
bool status = false;
struct object_functions *pObject = NULL;
pObject = Device_Objects_Find_Functions(data->object_type);
if (pObject != NULL) {
if (!pObject->Object_Delete) {
/* The device supports the object type
but does not support the deletion of the
object for some reason.*/
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_OBJECT_DELETION_NOT_PERMITTED;
} else if (
pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(data->object_instance)) {
/* The object being deleted must already exist */
status = pObject->Object_Delete(data->object_instance);
if (status) {
Device_Inc_Database_Revision();
} else {
/* The object exists but cannot be deleted. */
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_OBJECT_DELETION_NOT_PERMITTED;
}
} else {
/* The object to be deleted does not exist. */
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
} else {
/* The device does not support the specified object type. */
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE;
}
return status;
}
/** Looks up the requested Object to see if the functionality is supported.
* @ingroup ObjHelpers
* @param [in] The object type to be looked up.
* @return True if the object instance supports this feature.
*/
bool Device_Value_List_Supported(BACNET_OBJECT_TYPE object_type)
{
bool status = false; /* Ever the pessamist! */
struct object_functions *pObject = NULL;
pObject = Device_Objects_Find_Functions(object_type);
if (pObject != NULL) {
if (pObject->Object_Value_List) {
status = true;
}
}
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;
datetime_init();
if (object_table) {
Object_Table = object_table;
} else {
Object_Table = &My_Object_Table[0];
}
pObject = Object_Table;
while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) {
if (pObject->Object_Init) {
pObject->Object_Init();
}
pObject++;
}
dcc_set_status_duration(COMMUNICATION_ENABLE, 0);
if (Object_Instance_Number > BACNET_MAX_INSTANCE) {
Object_Instance_Number = BACNET_MAX_INSTANCE;
}
characterstring_init_ansi(&My_Object_Name, Device_Name_Default);
#if (BACNET_PROTOCOL_REVISION >= 14)
Channel_Write_Property_Internal_Callback_Set(Device_Write_Property);
#endif
}
bool DeviceGetRRInfo(
BACNET_READ_RANGE_DATA *pRequest, /* Info on the request */
RR_PROP_INFO *pInfo)
{ /* Where to put the response */
bool status = false; /* return value */
switch (pRequest->object_property) {
case PROP_VT_CLASSES_SUPPORTED:
case PROP_ACTIVE_VT_SESSIONS:
case PROP_LIST_OF_SESSION_KEYS:
case PROP_TIME_SYNCHRONIZATION_RECIPIENTS:
case PROP_MANUAL_SLAVE_ADDRESS_BINDING:
case PROP_SLAVE_ADDRESS_BINDING:
case PROP_RESTART_NOTIFICATION_RECIPIENTS:
case PROP_UTC_TIME_SYNCHRONIZATION_RECIPIENTS:
pInfo->RequestTypes = RR_BY_POSITION;
pRequest->error_class = ERROR_CLASS_PROPERTY;
if (pRequest->array_index == BACNET_ARRAY_ALL) {
pRequest->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
} else {
pRequest->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
}
break;
case PROP_DEVICE_ADDRESS_BINDING:
pInfo->RequestTypes = RR_BY_POSITION;
pInfo->Handler = rr_address_list_encode;
status = true;
break;
case PROP_ACTIVE_COV_SUBSCRIPTIONS:
pInfo->RequestTypes = RR_BY_POSITION;
pRequest->error_class = ERROR_CLASS_PROPERTY;
if (pRequest->array_index == BACNET_ARRAY_ALL) {
pRequest->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
} else {
pRequest->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
}
break;
default:
pRequest->error_class = ERROR_CLASS_SERVICES;
pRequest->error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST;
if (pRequest->array_index == BACNET_ARRAY_ALL) {
pRequest->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
pRequest->error_class = ERROR_CLASS_PROPERTY;
}
break;
}
return status;
}
/**
* @brief Updates all the object timers with elapsed milliseconds
* @param milliseconds - number of milliseconds elapsed
*/
void Device_Timer(uint16_t milliseconds)
{
struct object_functions *pObject;
unsigned count = 0;
uint32_t instance;
pObject = Object_Table;
while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) {
if (pObject->Object_Count) {
count = pObject->Object_Count();
}
while (count) {
count--;
if ((pObject->Object_Timer) &&
(pObject->Object_Index_To_Instance)) {
instance = pObject->Object_Index_To_Instance(count);
pObject->Object_Timer(instance, milliseconds);
}
}
pObject++;
}
}