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
+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