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.
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -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 <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
+137
-114
@@ -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 {
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
ObjectFile=".\Debug/"
|
||||
ProgramDataBaseFileName=".\Debug/"
|
||||
BrowseInformation="1"
|
||||
WarningLevel="3"
|
||||
WarningLevel="4"
|
||||
SuppressStartupBanner="true"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 *
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user