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:
petermcs
2009-11-10 15:37:11 +00:00
parent eb370fa972
commit b8251f314f
12 changed files with 499 additions and 147 deletions
+43 -20
View File
@@ -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");
+40
View File
@@ -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
View File
@@ -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 {
+7
View File
@@ -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,
+1
View File
@@ -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
+9
View File
@@ -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
}
+5
View File
@@ -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,
+78 -5
View File
@@ -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"
/>
+7 -7
View File
@@ -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);
+166
View File
@@ -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 *
+5
View File
@@ -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