From b8251f314f5916368c05ab264340c4a5cb955baf Mon Sep 17 00:00:00 2001 From: petermcs Date: Tue, 10 Nov 2009 15:37:11 +0000 Subject: [PATCH] Implemented ReadRange functionality for the device "Device Address Binding" property and modified the ReadRange test app to use this real property for its target. Implemented a framework for allowing the ReadRange handler determine which properties support ReadRange and which varients of ReadRange they support. --- bacnet-stack/demo/handler/h_rr.c | 63 +++-- bacnet-stack/demo/object/device.c | 40 +++ bacnet-stack/demo/readrange/main.c | 251 ++++++++++-------- bacnet-stack/include/address.h | 7 + bacnet-stack/include/config.h | 1 + bacnet-stack/include/device.h | 9 + bacnet-stack/include/handlers.h | 5 + bacnet-stack/include/readrange.h | 83 +++++- bacnet-stack/ports/win32/bacnet/bacnet.vcproj | 2 +- bacnet-stack/ports/win32/main.c | 14 +- bacnet-stack/src/address.c | 166 ++++++++++++ bacnet-stack/src/readrange.c | 5 + 12 files changed, 499 insertions(+), 147 deletions(-) diff --git a/bacnet-stack/demo/handler/h_rr.c b/bacnet-stack/demo/handler/h_rr.c index d09dd377..5c9f1f9b 100644 --- a/bacnet-stack/demo/handler/h_rr.c +++ b/bacnet-stack/demo/handler/h_rr.c @@ -39,6 +39,16 @@ static uint8_t Temp_Buf[MAX_APDU] = { 0 }; +static rr_info_function get_rr_info[MAX_BACNET_OBJECT_TYPE]; + +void handler_rr_object_set( + BACNET_OBJECT_TYPE object_type, + rr_info_function pFunction1) +{ + if (object_type < MAX_BACNET_OBJECT_TYPE) + get_rr_info[object_type] = pFunction1; +} + /* Encodes the property APDU and returns the length, or sets the error, and returns -1 */ int Encode_RR_payload( @@ -48,26 +58,39 @@ int Encode_RR_payload( BACNET_ERROR_CODE * error_code) { int apdu_len = -1; + rr_info_function info_fn_ptr = NULL; + rr_handler_function rr_fn_ptr = NULL; + RR_PROP_INFO PropInfo; - /* FIXME: Stub function at the moment which just returns something - * for the sake of testing things out. Need to look at existing objects - * and see how we can do this for real. - */ - - pRequest->ItemCount = 6; - bitstring_init(&pRequest->ResultFlags); - bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_FIRST_ITEM, true); - bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_LAST_ITEM, true); - bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_MORE_ITEMS, false); - pRequest->FirstSequence = 0; - - apdu_len = 0; - apdu_len += encode_application_unsigned(&apdu[apdu_len], 1); - apdu_len += encode_application_unsigned(&apdu[apdu_len], 2); - apdu_len += encode_application_unsigned(&apdu[apdu_len], 3); - apdu_len += encode_application_unsigned(&apdu[apdu_len], 4); - apdu_len += encode_application_unsigned(&apdu[apdu_len], 5); - apdu_len += encode_application_unsigned(&apdu[apdu_len], 6); + /* initialize the default return values */ + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_OTHER; + + /* handle each object type */ + if (pRequest->object_type < MAX_BACNET_OBJECT_TYPE) + info_fn_ptr = get_rr_info[pRequest->object_type]; + + if ((info_fn_ptr != NULL) && (info_fn_ptr(pRequest->object_instance, pRequest->object_property, &PropInfo, error_class, error_code) != false)) { + /* We try and do some of the more generic error checking here to cut down on duplication of effort */ + + if(((PropInfo.RequestTypes & RR_ARRAY_OF_LISTS) == 0) && (pRequest->array_index != 0)) { + /* Array access attempted on a non array property */ + *error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + } + else if((pRequest->RequestType != RR_READ_ALL) && ((PropInfo.RequestTypes & pRequest->RequestType) == 0)) { + /* By Time or By Sequence not supported */ + *error_code = ERROR_CODE_OTHER; /* I couldn't see anything more appropriate so... */ + } + else if(pRequest->Count == 0) { /* Count cannot be zero */ + *error_code = ERROR_CODE_OTHER; /* I couldn't see anything more appropriate so... */ + } + else if(PropInfo.Handler != NULL) { + apdu_len = PropInfo.Handler(apdu, pRequest, error_class, error_code); + } + } else { + /* Either we don't support RR for this property yet or it is not a list or array of lists */ + *error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST; + } return apdu_len; } @@ -151,7 +174,7 @@ void handler_read_range( } else { len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], - service_data->invoke_id, SERVICE_CONFIRMED_READ_PROPERTY, + service_data->invoke_id, SERVICE_CONFIRMED_READ_RANGE, error_class, error_code); #if PRINT_ENABLED fprintf(stderr, "RR: Sending Error!\n"); diff --git a/bacnet-stack/demo/object/device.c b/bacnet-stack/demo/object/device.c index 7a960d8f..3c1101c4 100644 --- a/bacnet-stack/demo/object/device.c +++ b/bacnet-stack/demo/object/device.c @@ -870,6 +870,46 @@ void Device_Init( { } +bool DeviceGetRRInfo( + uint32_t Object, /* Which particular object - obviously not important for device object */ + BACNET_PROPERTY_ID Property, /* Which property */ + RR_PROP_INFO *pInfo, /* Where to put the information */ + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code) +{ +*error_class = ERROR_CLASS_SERVICES; +*error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST; + + switch(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; + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + + case PROP_DEVICE_ADDRESS_BINDING: + pInfo->RequestTypes = RR_BY_POSITION; + pInfo->Handler = &rr_address_list_encode; + return(true); + break; + + case PROP_ACTIVE_COV_SUBSCRIPTIONS: + pInfo->RequestTypes = RR_BY_POSITION; + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + +return(false); +} + #ifdef TEST #include #include diff --git a/bacnet-stack/demo/readrange/main.c b/bacnet-stack/demo/readrange/main.c index ba934475..c25844b5 100644 --- a/bacnet-stack/demo/readrange/main.c +++ b/bacnet-stack/demo/readrange/main.c @@ -143,7 +143,8 @@ static void Init_Object( write_property_function wp_function, object_count_function count_function, object_index_to_instance_function index_function, - object_name_function name_function) + object_name_function name_function, + rr_info_function rr_inf_function) { handler_read_property_object_set(object_type, rp_function, object_valid_function); @@ -151,6 +152,7 @@ static void Init_Object( handler_read_property_multiple_list_set(object_type, rpm_list_function); Device_Object_Function_Set(object_type, count_function, index_function, name_function); + handler_rr_object_set(object_type, rr_inf_function); } static void Init_Objects( @@ -159,68 +161,68 @@ static void Init_Objects( Device_Init(); Init_Object(OBJECT_DEVICE, Device_Property_Lists, Device_Encode_Property_APDU, Device_Valid_Object_Instance_Number, - Device_Write_Property, NULL, NULL, NULL); + Device_Write_Property, NULL, NULL, NULL, DeviceGetRRInfo); Analog_Input_Init(); Init_Object(OBJECT_ANALOG_INPUT, Analog_Input_Property_Lists, Analog_Input_Encode_Property_APDU, Analog_Input_Valid_Instance, NULL, - Analog_Input_Count, Analog_Input_Index_To_Instance, Analog_Input_Name); + Analog_Input_Count, Analog_Input_Index_To_Instance, Analog_Input_Name, NULL); Analog_Output_Init(); Init_Object(OBJECT_ANALOG_OUTPUT, Analog_Output_Property_Lists, Analog_Output_Encode_Property_APDU, Analog_Output_Valid_Instance, Analog_Output_Write_Property, Analog_Output_Count, - Analog_Output_Index_To_Instance, Analog_Output_Name); + Analog_Output_Index_To_Instance, Analog_Output_Name, NULL); Analog_Value_Init(); Init_Object(OBJECT_ANALOG_VALUE, Analog_Value_Property_Lists, Analog_Value_Encode_Property_APDU, Analog_Value_Valid_Instance, Analog_Value_Write_Property, Analog_Value_Count, - Analog_Value_Index_To_Instance, Analog_Value_Name); + Analog_Value_Index_To_Instance, Analog_Value_Name, NULL); Binary_Input_Init(); Init_Object(OBJECT_BINARY_INPUT, Binary_Input_Property_Lists, Binary_Input_Encode_Property_APDU, Binary_Input_Valid_Instance, NULL, - Binary_Input_Count, Binary_Input_Index_To_Instance, Binary_Input_Name); + Binary_Input_Count, Binary_Input_Index_To_Instance, Binary_Input_Name, NULL); Binary_Output_Init(); Init_Object(OBJECT_BINARY_OUTPUT, Binary_Output_Property_Lists, Binary_Output_Encode_Property_APDU, Binary_Output_Valid_Instance, Binary_Output_Write_Property, Binary_Output_Count, - Binary_Output_Index_To_Instance, Binary_Output_Name); + Binary_Output_Index_To_Instance, Binary_Output_Name, NULL); Binary_Value_Init(); Init_Object(OBJECT_BINARY_VALUE, Binary_Value_Property_Lists, Binary_Value_Encode_Property_APDU, Binary_Value_Valid_Instance, Binary_Value_Write_Property, Binary_Value_Count, - Binary_Value_Index_To_Instance, Binary_Value_Name); + Binary_Value_Index_To_Instance, Binary_Value_Name, NULL); Life_Safety_Point_Init(); Init_Object(OBJECT_LIFE_SAFETY_POINT, Life_Safety_Point_Property_Lists, Life_Safety_Point_Encode_Property_APDU, Life_Safety_Point_Valid_Instance, Life_Safety_Point_Write_Property, Life_Safety_Point_Count, Life_Safety_Point_Index_To_Instance, - Life_Safety_Point_Name); + Life_Safety_Point_Name, NULL); Load_Control_Init(); Init_Object(OBJECT_LOAD_CONTROL, Load_Control_Property_Lists, Load_Control_Encode_Property_APDU, Load_Control_Valid_Instance, Load_Control_Write_Property, Load_Control_Count, - Load_Control_Index_To_Instance, Load_Control_Name); + Load_Control_Index_To_Instance, Load_Control_Name, NULL); Multistate_Output_Init(); Init_Object(OBJECT_MULTI_STATE_OUTPUT, Multistate_Output_Property_Lists, Multistate_Output_Encode_Property_APDU, Multistate_Output_Valid_Instance, Multistate_Output_Write_Property, Multistate_Output_Count, Multistate_Output_Index_To_Instance, - Multistate_Output_Name); + Multistate_Output_Name, NULL); #if defined(BACFILE) bacfile_init(); Init_Object(OBJECT_FILE, BACfile_Property_Lists, bacfile_encode_property_apdu, bacfile_valid_instance, bacfile_write_property, bacfile_count, bacfile_index_to_instance, - bacfile_name); + bacfile_name, NULL); #endif } @@ -405,123 +407,144 @@ int main( if (found) { if (invoke_id == 0) { /* Safe to send a new request */ switch (iCount) { - case 0: - Request.RequestType = RR_BY_POSITION; - Request.Range.RefIndex = 20; - Request.Count = 30; - Request.object_type = OBJECT_ANALOG_INPUT; - Request.object_instance = 0; - Request.object_property = PROP_PRESENT_VALUE; - Request.array_index = 1; - break; + case 0: /* Pass - should read up to 1st 10 */ + Request.RequestType = RR_BY_POSITION; + Request.Range.RefIndex = 1; + Request.Count = 10; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = Target_Device_Object_Instance; + Request.object_property = PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 0; + break; - case 1: - Request.RequestType = RR_BY_SEQUENCE; - Request.Range.RefSeqNum = 20; - Request.Count = 30; - Request.object_type = OBJECT_ANALOG_INPUT; - Request.object_instance = 0; - Request.object_property = PROP_PRESENT_VALUE; - Request.array_index = 2; - break; + case 1: /* Pass - should read entries 2 and 3 */ + Request.RequestType = RR_BY_POSITION; + Request.Range.RefSeqNum = 3; + Request.Count = -2; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = Target_Device_Object_Instance; + Request.object_property = PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 0; + break; - case 2: - Request.RequestType = RR_BY_TIME; - Request.Range.RefTime.date.year = 2009; - Request.Range.RefTime.date.month = 9; - Request.Range.RefTime.date.day = 23; - Request.Range.RefTime.date.wday = 0xFF; /* Day of week unspecified */ - Request.Range.RefTime.time.hour = 22; - Request.Range.RefTime.time.min = 23; - Request.Range.RefTime.time.sec = 24; - Request.Range.RefTime.time.hundredths = 0; + case 2: /* Fail - By Time not supported */ + Request.RequestType = RR_BY_TIME; + Request.Range.RefTime.date.year = 2009; + Request.Range.RefTime.date.month = 9; + Request.Range.RefTime.date.day = 23; + Request.Range.RefTime.date.wday = 0xFF; + Request.Range.RefTime.time.hour = 22; + Request.Range.RefTime.time.min = 23; + Request.Range.RefTime.time.sec = 24; + Request.Range.RefTime.time.hundredths = 0; + + Request.Count = 10; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = Target_Device_Object_Instance; + Request.object_property = PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 0; + break; - Request.Count = 30; - Request.object_type = OBJECT_ANALOG_INPUT; - Request.object_instance = 0; - Request.object_property = PROP_PRESENT_VALUE; - Request.array_index = 3; - break; - case 3: - Request.RequestType = RR_BY_POSITION; - Request.Range.RefIndex = 20; - Request.Count = 30; - Request.object_type = OBJECT_ANALOG_INPUT; - Request.object_instance = 0; - Request.object_property = PROP_PRESENT_VALUE; - Request.array_index = BACNET_ARRAY_ALL; - break; + case 3: /* Fail - array not supported */ + Request.RequestType = RR_BY_POSITION; + Request.Range.RefIndex = 1; + Request.Count = 10; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = Target_Device_Object_Instance; + Request.object_property = PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 1; + break; - case 4: - Request.RequestType = RR_BY_SEQUENCE; - Request.Range.RefSeqNum = 20; - Request.Count = 30; - Request.object_type = OBJECT_ANALOG_INPUT; - Request.object_instance = 0; - Request.object_property = PROP_PRESENT_VALUE; - Request.array_index = BACNET_ARRAY_ALL; - break; + case 4: /* Fail - By Sequence not supported */ + Request.RequestType = RR_BY_SEQUENCE; + Request.Range.RefSeqNum = 1; + Request.Count = 10; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = Target_Device_Object_Instance; + Request.object_property = PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 0; + break; - case 5: - Request.RequestType = RR_BY_TIME; - Request.Range.RefTime.date.year = 2009; - Request.Range.RefTime.date.month = 9; - Request.Range.RefTime.date.day = 23; - Request.Range.RefTime.date.wday = 0xFF; /* Day of week unspecified */ - Request.Range.RefTime.time.hour = 22; - Request.Range.RefTime.time.min = 23; - Request.Range.RefTime.time.sec = 24; - Request.Range.RefTime.time.hundredths = 0; + case 5: /* Fail Bytime not supported and array not supported */ + Request.RequestType = RR_BY_TIME; + Request.Range.RefTime.date.year = 2009; + Request.Range.RefTime.date.month = 9; + Request.Range.RefTime.date.day = 23; + Request.Range.RefTime.date.wday = 0xFF; /* Day of week unspecified */ + Request.Range.RefTime.time.hour = 22; + Request.Range.RefTime.time.min = 23; + Request.Range.RefTime.time.sec = 24; + Request.Range.RefTime.time.hundredths = 0; + + Request.Count = 10; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = Target_Device_Object_Instance; + Request.object_property = PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 1; + break; - Request.Count = 30; - Request.object_type = OBJECT_ANALOG_INPUT; - Request.object_instance = 0; - Request.object_property = PROP_PRESENT_VALUE; - Request.array_index = BACNET_ARRAY_ALL; - break; + case 6: /* Pass - should try to return all entries */ + Request.RequestType = RR_READ_ALL; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = Target_Device_Object_Instance; + Request.object_property = PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 0; + break; - case 6: - Request.RequestType = RR_READ_ALL; - Request.Range.RefTime.date.year = 2009; - Request.Range.RefTime.date.month = 9; - Request.Range.RefTime.date.day = 23; - Request.Range.RefTime.date.wday = 0xFF; /* Day of week unspecified */ - Request.Range.RefTime.time.hour = 22; - Request.Range.RefTime.time.min = 23; - Request.Range.RefTime.time.sec = 24; - Request.Range.RefTime.time.hundredths = 0; + case 7: /* Fail - array not supported */ + Request.RequestType = RR_READ_ALL; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = Target_Device_Object_Instance; + Request.object_property = PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 1; + break; - Request.Count = 30; - Request.object_type = OBJECT_ANALOG_INPUT; - Request.object_instance = 0; - Request.object_property = PROP_PRESENT_VALUE; - Request.array_index = BACNET_ARRAY_ALL; - break; + case 8: /* Pass - should read 1st 1 */ + Request.RequestType = RR_BY_POSITION; + Request.Range.RefIndex = 1; + Request.Count = 1; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = Target_Device_Object_Instance; + Request.object_property = PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 0; + break; - case 7: - Request.RequestType = RR_READ_ALL; - Request.Range.RefTime.date.year = 2009; - Request.Range.RefTime.date.month = 9; - Request.Range.RefTime.date.day = 23; - Request.Range.RefTime.date.wday = 0xFF; /* Day of week unspecified */ - Request.Range.RefTime.time.hour = 22; - Request.Range.RefTime.time.min = 23; - Request.Range.RefTime.time.sec = 24; - Request.Range.RefTime.time.hundredths = 0; + case 9: /* Pass - should read 1st 2 */ + Request.RequestType = RR_BY_POSITION; + Request.Range.RefIndex = 1; + Request.Count = 2; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = Target_Device_Object_Instance; + Request.object_property = PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 0; + break; - Request.Count = 30; - Request.object_type = OBJECT_ANALOG_INPUT; - Request.object_instance = 0; - Request.object_property = PROP_PRESENT_VALUE; - Request.array_index = 7; - break; - } + case 10: /* Pass - should read 2nd and 3rd */ + Request.RequestType = RR_BY_POSITION; + Request.Range.RefIndex = 2; + Request.Count = 2; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = Target_Device_Object_Instance; + Request.object_property = PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 0; + break; + + case 11: /* Pass - should read 2nd up to 11th */ + Request.RequestType = RR_BY_POSITION; + Request.Range.RefIndex = 2; + Request.Count = 10; + Request.object_type = OBJECT_DEVICE; + Request.object_instance = Target_Device_Object_Instance; + Request.object_property = PROP_DEVICE_ADDRESS_BINDING; + Request.array_index = 0; + break; + } invoke_id = Send_ReadRange_Request(Target_Device_Object_Instance, &Request); } else if (tsm_invoke_id_free(invoke_id)) { - if (iCount != MY_MAX_BLOCK) { + if (iCount != 11) { iCount++; invoke_id = 0; } else { diff --git a/bacnet-stack/include/address.h b/bacnet-stack/include/address.h index 9d91c357..7113d340 100644 --- a/bacnet-stack/include/address.h +++ b/bacnet-stack/include/address.h @@ -38,6 +38,7 @@ #include #include #include "bacdef.h" +#include "readrange.h" #ifdef __cplusplus extern "C" { @@ -93,6 +94,12 @@ extern "C" { uint8_t * apdu, unsigned apdu_len); + int rr_address_list_encode( + uint8_t *apdu, + BACNET_READ_RANGE_DATA *pRequest, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code); + void address_set_device_TTL( uint32_t device_id, uint32_t TimeOut, diff --git a/bacnet-stack/include/config.h b/bacnet-stack/include/config.h index c718b0cb..130e2bde 100644 --- a/bacnet-stack/include/config.h +++ b/bacnet-stack/include/config.h @@ -70,6 +70,7 @@ /* #define MAX_APDU 1476 */ #if defined(BACDL_BIP) #define MAX_APDU 1476 +/* #define MAX_APDU 128 enable this IP for testing readrange so you get the More Follows flag set */ #elif defined (BACDL_ETHERNET) #define MAX_APDU 1476 #else diff --git a/bacnet-stack/include/device.h b/bacnet-stack/include/device.h index ca98efa4..24d6407e 100644 --- a/bacnet-stack/include/device.h +++ b/bacnet-stack/include/device.h @@ -39,6 +39,7 @@ #include "bacdef.h" #include "bacenum.h" #include "wp.h" +#include "readrange.h" typedef unsigned ( *object_count_function) ( @@ -160,6 +161,14 @@ extern "C" { BACNET_WRITE_PROPERTY_DATA * wp_data, BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code); + + bool DeviceGetRRInfo( + uint32_t Object, /* Which particular object - obviously not important for device object */ + BACNET_PROPERTY_ID Property, /* Which property */ + RR_PROP_INFO *pInfo, /* Where to put the information */ + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code); + #ifdef __cplusplus } diff --git a/bacnet-stack/include/handlers.h b/bacnet-stack/include/handlers.h index 9da291ac..dc64301d 100644 --- a/bacnet-stack/include/handlers.h +++ b/bacnet-stack/include/handlers.h @@ -34,6 +34,7 @@ #include "rp.h" #include "rpm.h" #include "wp.h" +#include "readrange.h" #include "getevent.h" @@ -78,6 +79,10 @@ extern "C" { BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data); + void handler_rr_object_set( + BACNET_OBJECT_TYPE object_type, + rr_info_function pFunction1); + void handler_read_property_object_set( BACNET_OBJECT_TYPE object_type, read_property_function pFunction1, diff --git a/bacnet-stack/include/readrange.h b/bacnet-stack/include/readrange.h index 397f1ffb..27342e7a 100644 --- a/bacnet-stack/include/readrange.h +++ b/bacnet-stack/include/readrange.h @@ -31,6 +31,11 @@ License. ------------------------------------------- ####COPYRIGHTEND####*/ +#ifndef READRANGE_H +#define READRANGE_H + +#include "bacstr.h" +#include "datetime.h" struct BACnet_Read_Range_Data; typedef struct BACnet_Read_Range_Data { @@ -42,6 +47,7 @@ typedef struct BACnet_Read_Range_Data { int application_data_len; BACNET_BIT_STRING ResultFlags; /* FIRST_ITEM, LAST_ITEM, MORE_ITEMS */ int RequestType; /* Index, sequence or time based request */ + int Overhead; /* How much space the baggage takes in the response */ uint32_t ItemCount; uint32_t FirstSequence; union { /* Pick the appropriate data type */ @@ -52,12 +58,15 @@ typedef struct BACnet_Read_Range_Data { int32_t Count; /* SIGNED value as +ve vs -ve is important */ } BACNET_READ_RANGE_DATA; -/* Defines to indicate which type of read range request it is */ +/* Defines to indicate which type of read range request it is + Not really a bit map but we do it like this to allow quick + checking of request against capabilities for the property */ -#define RR_BY_POSITION 0 -#define RR_BY_SEQUENCE 1 -#define RR_BY_TIME 2 -#define RR_READ_ALL 4 /* Read all of array - so don't send any range in the request */ +#define RR_BY_POSITION 1 +#define RR_BY_SEQUENCE 2 +#define RR_BY_TIME 4 +#define RR_READ_ALL 8 /* Read all of array - so don't send any range in the request */ +#define RR_ARRAY_OF_LISTS 16 /* For info functionality indicates array of lists if set */ /* Bit String Enumerations */ typedef enum { @@ -66,6 +75,67 @@ typedef enum { RESULT_FLAG_MORE_ITEMS = 2 } BACNET_RESULT_FLAGS; +/* Defines for ReadRange packet overheads to allow us to determine how + * much space is left for actual payload: + * + * Overhead is comprised of: + * 1. PDU Type + invoke ID + service type = 3 bytes + * 2. Object ID = 5 bytes + * 3. Object Property = 2 bytes if property is 0-255, 3 if property is + * 256-65535 – theoretical max of 5 bytes but how likely is that? + * 4. Optional array index = 2 bytes if index is 0-255, 3 if index is + * 256-65535 – theoretical max of 5 bytes but how likely is that? + * 5. Flags = 3 bytes + * 6. Opening and closing tag for data = 2 bytes + * 7. firstSequenceNumber [6] Unsigned32 OPTIONAL -- used only if 'Item Count' > 0 + * and the request was either of type 'By Sequence Number' or 'By Time' + * = minimum of 2 bytes, maximum of 5 bytes. + * + * These figures give an absolute worst-case overhead of 28 bytes. A less + * conservative value (if we assume object property is 3 bytes and array + * index is 3 bytes) is 24. */ + +/* This is the fixed part of the overhead before we check for array and + * first sequence number requirements. again if you are really paranoid + * use a value of 18 */ + +#define RR_OVERHEAD 16 +#define RR_1ST_SEQ_OVERHEAD 5 +#define RR_INDEX_OVERHEAD 3 /* or 5 if paranoid */ + +/* Define pointer to function type for handling ReadRange request. + This function will take the following parameters: + 1. A pointer to a buffer of at least MAX_APDU bytes to build the response in. + 2. A pointer to a BACNET_READ_RANGE_DATA structure with all the request + information in it. The function is responsible for applying the request + to the property in question and returning the response. + 3/4. pointers to Error Class and Error Code values to write to in the + event of a failure. */ + +typedef int (*rr_handler_function) ( + uint8_t *apdu, + BACNET_READ_RANGE_DATA *pRequest, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code); + +/* Structure to return the type of requests a given object property can + * accept and the address of the function to handle the request */ + +typedef struct rrpropertyinfo { + int RequestTypes; + rr_handler_function Handler; + } RR_PROP_INFO; + +/* Function pointer for ReadRange information retrieval function */ + +typedef bool (*rr_info_function) ( + uint32_t Object, /* Which particular object instance (we know the type implicetly) */ + BACNET_PROPERTY_ID Property, /* Which property */ + RR_PROP_INFO *pInfo, /* Where to write the response to */ + BACNET_ERROR_CLASS *error_class, /* Somewhere to write error responses to */ + BACNET_ERROR_CODE *error_code); + + int rr_encode_apdu( uint8_t * apdu, uint8_t invoke_id, @@ -89,3 +159,6 @@ int rr_ack_decode_service_request( uint8_t Send_ReadRange_Request( uint32_t device_id, /* destination device */ BACNET_READ_RANGE_DATA * read_access_data); + +#endif + diff --git a/bacnet-stack/ports/win32/bacnet/bacnet.vcproj b/bacnet-stack/ports/win32/bacnet/bacnet.vcproj index 3cbf0549..3ca1d4aa 100644 --- a/bacnet-stack/ports/win32/bacnet/bacnet.vcproj +++ b/bacnet-stack/ports/win32/bacnet/bacnet.vcproj @@ -144,7 +144,7 @@ ObjectFile=".\Debug/" ProgramDataBaseFileName=".\Debug/" BrowseInformation="1" - WarningLevel="3" + WarningLevel="4" SuppressStartupBanner="true" DebugInformationFormat="4" /> diff --git a/bacnet-stack/ports/win32/main.c b/bacnet-stack/ports/win32/main.c index dbd9ed8d..bc6f9358 100644 --- a/bacnet-stack/ports/win32/main.c +++ b/bacnet-stack/ports/win32/main.c @@ -59,14 +59,14 @@ static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; static void Init_Object( - BACNET_OBJECT_TYPE object_type, - rpm_property_lists_function rpm_list_function, - read_property_function rp_function, - object_valid_instance_function object_valid_function, - write_property_function wp_function, - object_count_function count_function, + BACNET_OBJECT_TYPE object_type, + rpm_property_lists_function rpm_list_function, + read_property_function rp_function, + object_valid_instance_function object_valid_function, + write_property_function wp_function, + object_count_function count_function, object_index_to_instance_function index_function, - object_name_function name_function) + object_name_function name_function) { handler_read_property_object_set(object_type, rp_function, object_valid_function); diff --git a/bacnet-stack/src/address.c b/bacnet-stack/src/address.c index 91bab3aa..0f3e883c 100644 --- a/bacnet-stack/src/address.c +++ b/bacnet-stack/src/address.c @@ -41,6 +41,7 @@ #include "address.h" #include "bacdef.h" #include "bacdcode.h" +#include "readrange.h" /* This module is used to handle the address binding that */ /* occurs in BACnet. A device id is bound to a MAC address. */ @@ -633,6 +634,171 @@ int address_list_encode( return (iLen); } +/**************************************************************************** + * Build a list of the current bindings for the device address binding * + * property as required for the ReadsRange functionality. * + * We assume we only get called for "Read All" or "By Position" requests. * + * * + * We need to treat the address cache as a contiguous array but in reality * + * it could be sparsely populated. We can get the count but we can only * + * extract entries by doing a linear scan starting from the first entry in * + * the cache and picking them off one by one. * + * * + * We do assume the list cannot change whilst we are accessing it so would * + * not be multithread safe if there are other tasks that change the cache. * + * * + * We take the simple approach here to filling the buffer by taking a max * + * size for a single entry and then stopping if there is less than that * + * left in the buffer. You could build each entry in a seperate buffer and * + * determine the exact length before copying but this is time consuming, * + * requires more memory and would probably only let you sqeeeze one more * + * entry in on occasion. The value is calculated as 5 bytes for the device * + * ID + 3 bytes for the network number and nine bytes for the MAC address * + * oct string to give 17 bytes (the minimum possible is 5 + 2 + 3 = 10). * + ****************************************************************************/ + +#define ACACHE_MAX_ENC 17 /* Maximum size of encoded cache entry, see above */ + +int rr_address_list_encode( + uint8_t *apdu, + BACNET_READ_RANGE_DATA *pRequest, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code) +{ + int iLen; + int32_t iTemp; + struct Address_Cache_Entry *pMatch; + BACNET_OCTET_STRING MAC_Address; + uint32_t uiTotal; /* Number of bound entries in the cache */ + uint32_t uiIndex; /* Current entry number */ + uint32_t uiFirst; /* Entry number we started encoding from */ + uint32_t uiLast; /* Entry number we finished encoding on */ + uint32_t uiTarget; /* Last entry we are required to encode */ + uint32_t uiRemaining; /* Amount of unused space in packet */ + + /* Initialise result flags to all false */ + + bitstring_init(&pRequest->ResultFlags); + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_FIRST_ITEM, false); + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_LAST_ITEM, false); + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_MORE_ITEMS, false); + + uiFirst = 0; + uiLast = 0; + iLen = 0; + uiRemaining = MAX_APDU - pRequest->Overhead; /* See how much space we have */ + + pRequest->ItemCount = 0; /* Start out with nothing */ + uiTotal = address_count(); /* What do we have to work with here ? */ + if(uiTotal == 0) /* Bail out now if nowt */ + return(0); + + if(pRequest->RequestType == RR_READ_ALL) { + /* + * Read all the array or as much as will fit in the buffer by selecting + * a range that covers the whole list and falling through to the next + * section of code + */ + pRequest->Count = uiTotal; /* Full list */ + pRequest->Range.RefIndex = 1; /* Starting at the beginning */ + } + + if(pRequest->Count < 0) { /* negative count means work from index backwards */ + /* + * Convert from end index/negative count to + * start index/positive count and then process as + * normal. This assumes that the order to return items + * is always first to last, if this is not true we will + * have to handle this differently. + * + * Note: We need to be careful about how we convert these + * values due to the mix of signed and unsigned types - don't + * try to optimise the code unless you understand all the + * implications of the data type conversions! + */ + + iTemp = pRequest->Range.RefIndex; /* pull out and convert to signed */ + iTemp += pRequest->Count + 1; /* Adjust backwards, remember count is -ve */ + if(iTemp < 1) { /* if count is too much, return from 1 to start index */ + pRequest->Count = pRequest->Range.RefIndex; + pRequest->Range.RefIndex = 1; + } + else { /* Otherwise adjust the start index and make count +ve */ + pRequest->Range.RefIndex = iTemp; + pRequest->Count = -pRequest->Count; + } + } + + /* From here on in we only have a starting point and a positive count */ + + if(pRequest->Range.RefIndex > uiTotal) /* Nothing to return as we are past the end of the list */ + return(0); + + uiTarget = pRequest->Range.RefIndex + pRequest->Count - 1; /* Index of last required entry */ + if(uiTarget > uiTotal) /* Capped at end of list if necessary */ + uiTarget = uiTotal; + + pMatch = Address_Cache; + uiIndex = 1; + while((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) != BAC_ADDR_IN_USE) /* Find first bound entry */ + pMatch++; + + /* Seek to start position */ + while(uiIndex != pRequest->Range.RefIndex) { + if((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) == BAC_ADDR_IN_USE) { /* Only count bound entries */ + pMatch++; + uiIndex++; + } + else + pMatch++; + } + + uiFirst = uiIndex; /* Record where we started from */ + while(uiIndex <= uiTarget) { + if(uiRemaining < ACACHE_MAX_ENC) { + /* + * Can't fit any more in! We just set the result flag to say there + * was more and drop out of the loop early + */ + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_MORE_ITEMS, true); + break; + } + + iTemp = encode_application_object_id(&apdu[iLen], OBJECT_DEVICE, pMatch->device_id); + iTemp += encode_application_unsigned(&apdu[iLen + iTemp], pMatch->address.net); + + /* pick the appropriate type of entry from the cache */ + + if(pMatch->address.len != 0) { + octetstring_init(&MAC_Address, pMatch->address.adr, pMatch->address.len); + iTemp += encode_application_octet_string(&apdu[iLen + iTemp], &MAC_Address); + } + else { + octetstring_init(&MAC_Address, pMatch->address.mac, pMatch->address.mac_len); + iTemp += encode_application_octet_string(&apdu[iLen + iTemp], &MAC_Address); + } + + uiRemaining -= iTemp; /* Reduce the remaining space */ + iLen += iTemp; /* and increase the length consumed */ + + uiLast = uiIndex; /* Record the last entry encoded */ + uiIndex++; /* and get ready for next one */ + pMatch++; + pRequest->ItemCount++; /* Chalk up another one for the response count */ + + while((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) != BAC_ADDR_IN_USE) /* Find next bound entry */ + pMatch++; + } + + /* Set remaining result flags if necessary */ + if(uiFirst == 1) + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_FIRST_ITEM, true); + + if(uiLast == uiTotal) + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_LAST_ITEM, true); + +return(iLen); +} /**************************************************************************** * Scan the cache and eliminate any expired entries. Should be called * diff --git a/bacnet-stack/src/readrange.c b/bacnet-stack/src/readrange.c index 939b34d9..d7c2ef26 100644 --- a/bacnet-stack/src/readrange.c +++ b/bacnet-stack/src/readrange.c @@ -172,6 +172,8 @@ int rr_decode_service_request( return -1; len += decode_enumerated(&apdu[len], len_value_type, &UnsignedTemp); rrdata->object_property = (BACNET_PROPERTY_ID) UnsignedTemp; + rrdata->Overhead = RR_OVERHEAD; /* Start with the fixed overhead */ + /* Tag 2: Optional Array Index */ rrdata->array_index = BACNET_ARRAY_ALL; /* Assuming this is the most common outcome... */ if (len < apdu_len) { @@ -183,6 +185,7 @@ int rr_decode_service_request( len += decode_unsigned(&apdu[len], len_value_type, &UnsignedTemp); rrdata->array_index = UnsignedTemp; + rrdata->Overhead += RR_INDEX_OVERHEAD; /* Allow for this in the response */ } } /* And/or optional range selection- Tags 3, 6 and 7 */ @@ -235,6 +238,7 @@ int rr_decode_service_request( len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + rrdata->Overhead += RR_1ST_SEQ_OVERHEAD; /* Allow for this in the response */ break; case 7: /* ReadRange by time stamp */ @@ -259,6 +263,7 @@ int rr_decode_service_request( len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + rrdata->Overhead += RR_1ST_SEQ_OVERHEAD; /* Allow for this in the response */ break; default: /* If we don't recognise the tag then we do nothing here and try to return