adding BACnet application and context-specfic data parsing for WriteProperty.
This commit is contained in:
+290
-4
@@ -110,7 +110,7 @@ int bacapp_encode_application_data(uint8_t * apdu,
|
||||
}
|
||||
|
||||
int bacapp_decode_application_data(uint8_t * apdu,
|
||||
uint8_t apdu_len, BACNET_APPLICATION_DATA_VALUE * value)
|
||||
int max_apdu_len, BACNET_APPLICATION_DATA_VALUE * value)
|
||||
{
|
||||
int len = 0;
|
||||
int tag_len = 0;
|
||||
@@ -119,8 +119,8 @@ int bacapp_decode_application_data(uint8_t * apdu,
|
||||
int object_type = 0;
|
||||
uint32_t instance = 0;
|
||||
|
||||
/* FIXME: use apdu_len! */
|
||||
(void) apdu_len;
|
||||
/* FIXME: use max_apdu_len! */
|
||||
(void) max_apdu_len;
|
||||
if (apdu) {
|
||||
tag_len = decode_tag_number_and_value(&apdu[0],
|
||||
&tag_number, &len_value_type);
|
||||
@@ -171,6 +171,84 @@ int bacapp_decode_application_data(uint8_t * apdu,
|
||||
return len;
|
||||
}
|
||||
|
||||
int bacapp_encode_context_data(uint8_t * apdu,
|
||||
BACNET_APPLICATION_DATA_VALUE * value,
|
||||
BACNET_PROPERTY_ID property)
|
||||
{
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
|
||||
if (value && apdu) {
|
||||
switch (property) {
|
||||
case PROP_REQUESTED_SHED_LEVEL:
|
||||
switch (value->tag) {
|
||||
case 0:
|
||||
case 1:
|
||||
apdu_len = encode_tagged_unsigned(&apdu[0],
|
||||
value->type.Unsigned_Int);
|
||||
break;
|
||||
case 2:
|
||||
apdu_len = encode_tagged_real(&apdu[0],
|
||||
value->type.Real);
|
||||
break;
|
||||
default:
|
||||
apdu_len = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_context_data(uint8_t * apdu,
|
||||
int max_apdu_len, BACNET_APPLICATION_DATA_VALUE * value,
|
||||
BACNET_PROPERTY_ID property)
|
||||
{
|
||||
int len = 0;
|
||||
int tag_len = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t len_value_type = 0;
|
||||
int object_type = 0;
|
||||
uint32_t instance = 0;
|
||||
|
||||
/* FIXME: use max_apdu_len! */
|
||||
(void) max_apdu_len;
|
||||
if (apdu) {
|
||||
tag_len = decode_tag_number_and_value(&apdu[0],
|
||||
&tag_number, &len_value_type);
|
||||
if (tag_len) {
|
||||
len += tag_len;
|
||||
value->tag = tag_number;
|
||||
switch (property) {
|
||||
case PROP_REQUESTED_SHED_LEVEL:
|
||||
switch (tag_number) {
|
||||
case 0:
|
||||
case 1:
|
||||
len += decode_unsigned(&apdu[len],
|
||||
len_value_type,
|
||||
&(value->type.Unsigned_Int));
|
||||
break;
|
||||
case 2:
|
||||
len += decode_real(&apdu[len],
|
||||
&(value->type.Real));
|
||||
break;
|
||||
default:
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
bool bacapp_copy(BACNET_APPLICATION_DATA_VALUE * dest_value,
|
||||
BACNET_APPLICATION_DATA_VALUE * src_value)
|
||||
{
|
||||
@@ -236,6 +314,63 @@ bool bacapp_copy(BACNET_APPLICATION_DATA_VALUE * dest_value,
|
||||
return status;
|
||||
}
|
||||
|
||||
/* returns the length of data between an opening tag and a closing tag.
|
||||
Expects that the first octet contain the opening tag.
|
||||
Include a value property identifier for context specific data
|
||||
such as the value received in a WriteProperty request */
|
||||
int bacapp_data_len(uint8_t *apdu, int max_apdu_len,
|
||||
BACNET_PROPERTY_ID property)
|
||||
{
|
||||
int len = 0;
|
||||
int total_len = 0;
|
||||
int apdu_len = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint8_t opening_tag_number = 0;
|
||||
uint8_t opening_tag_number_counter = 0;
|
||||
uint32_t value = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE application_value;
|
||||
|
||||
if (decode_is_opening_tag(&apdu[0])) {
|
||||
len = decode_tag_number_and_value(&apdu[apdu_len],
|
||||
&tag_number, &value);
|
||||
apdu_len += len;
|
||||
opening_tag_number = tag_number;
|
||||
opening_tag_number_counter = 1;
|
||||
while (opening_tag_number_counter) {
|
||||
if (decode_is_opening_tag(&apdu[apdu_len])) {
|
||||
len = decode_tag_number_and_value(&apdu[apdu_len],
|
||||
&tag_number, &value);
|
||||
if (tag_number == opening_tag_number)
|
||||
opening_tag_number_counter++;
|
||||
} else if (decode_is_closing_tag(&apdu[apdu_len])) {
|
||||
len = decode_tag_number_and_value(&apdu[apdu_len],
|
||||
&tag_number, &value);
|
||||
if (tag_number == opening_tag_number)
|
||||
opening_tag_number_counter--;
|
||||
} else if (decode_is_context_specific(&apdu[apdu_len])) {
|
||||
/* context-specific tagged data */
|
||||
len = bacapp_decode_context_data(&apdu[apdu_len],
|
||||
max_apdu_len - apdu_len, &application_value, property);
|
||||
} else {
|
||||
/* application tagged data */
|
||||
len = bacapp_decode_application_data(&apdu[apdu_len],
|
||||
max_apdu_len - apdu_len, &application_value);
|
||||
}
|
||||
apdu_len += len;
|
||||
if (opening_tag_number_counter)
|
||||
total_len += len;
|
||||
/* ERROR! */
|
||||
if (apdu_len > max_apdu_len)
|
||||
{
|
||||
total_len = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return total_len;
|
||||
}
|
||||
|
||||
#ifdef BACAPP_PRINT_ENABLED
|
||||
bool bacapp_print_value(FILE * stream,
|
||||
BACNET_APPLICATION_DATA_VALUE * value, BACNET_PROPERTY_ID property)
|
||||
@@ -448,6 +583,156 @@ bool bacapp_parse_application_data(BACNET_APPLICATION_TAG tag_number,
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
void testBACnetApplicationDataLength(Test * pTest)
|
||||
{
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
int len = 0; /* total length of the apdu, return value */
|
||||
int test_len = 0; /* length of the data */
|
||||
uint8_t apdu[480] = { 0 };
|
||||
BACNET_TIME local_time;
|
||||
BACNET_DATE local_date;
|
||||
|
||||
/* create some constructed data */
|
||||
/* 1. zero elements */
|
||||
test_len = 0;
|
||||
apdu_len = 0;
|
||||
len = encode_opening_tag(&apdu[apdu_len], 3);
|
||||
apdu_len += len;
|
||||
len = encode_closing_tag(&apdu[apdu_len], 3);
|
||||
apdu_len += len;
|
||||
/* verify the length of the data inside the opening/closing tags */
|
||||
len = constructed_data_len(&apdu[0], apdu_len,
|
||||
PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES);
|
||||
ct_test(pTest, test_len == len);
|
||||
|
||||
/* 2. application tagged data, one element */
|
||||
test_len = 0;
|
||||
apdu_len = 0;
|
||||
len = encode_opening_tag(&apdu[apdu_len], 3);
|
||||
apdu_len += len;
|
||||
len = encode_tagged_unsigned(&apdu[apdu_len], 4194303);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_closing_tag(&apdu[apdu_len], 3);
|
||||
apdu_len += len;
|
||||
/* verify the length of the data inside the opening/closing tags */
|
||||
len = constructed_data_len(&apdu[0], apdu_len,
|
||||
PROP_OBJECT_IDENTIFIER);
|
||||
ct_test(pTest, test_len == len);
|
||||
|
||||
/* 3. application tagged data, multiple elements */
|
||||
test_len = 0;
|
||||
apdu_len = 0;
|
||||
len = encode_opening_tag(&apdu[apdu_len], 3);
|
||||
apdu_len += len;
|
||||
len = encode_tagged_null(&apdu[apdu_len]);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_tagged_null(&apdu[apdu_len]);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_tagged_unsigned(&apdu[apdu_len], 1);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_tagged_unsigned(&apdu[apdu_len], 42);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_tagged_unsigned(&apdu[apdu_len], 91);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_tagged_null(&apdu[apdu_len]);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_tagged_null(&apdu[apdu_len]);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_tagged_null(&apdu[apdu_len]);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_tagged_null(&apdu[apdu_len]);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_tagged_null(&apdu[apdu_len]);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_tagged_null(&apdu[apdu_len]);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_tagged_null(&apdu[apdu_len]);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_tagged_null(&apdu[apdu_len]);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_tagged_null(&apdu[apdu_len]);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_tagged_null(&apdu[apdu_len]);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_tagged_null(&apdu[apdu_len]);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_closing_tag(&apdu[apdu_len], 3);
|
||||
apdu_len += len;
|
||||
/* verify the length of the data inside the opening/closing tags */
|
||||
len = constructed_data_len(&apdu[0], apdu_len,
|
||||
PROP_PRIORITY_ARRAY);
|
||||
ct_test(pTest, test_len == len);
|
||||
|
||||
/* 4. complex datatype - one element */
|
||||
test_len = 0;
|
||||
apdu_len = 0;
|
||||
len = encode_opening_tag(&apdu[apdu_len], 3);
|
||||
apdu_len += len;
|
||||
len = encode_opening_tag(&apdu[apdu_len], 3);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
local_date.year = 2006; /* AD */
|
||||
local_date.month = 4; /* 1=Jan */
|
||||
local_date.day = 1; /* 1..31 */
|
||||
local_date.wday = 6; /* 1=Monday */
|
||||
len = encode_tagged_date(&apdu[apdu_len], &local_date);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
local_time.hour = 7;
|
||||
local_time.min = 0;
|
||||
local_time.sec = 3;
|
||||
local_time.hundredths = 1;
|
||||
len = encode_tagged_time(&apdu[apdu_len], &local_time);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_closing_tag(&apdu[apdu_len], 3);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_closing_tag(&apdu[apdu_len], 3);
|
||||
apdu_len += len;
|
||||
/* verify the length of the data inside the opening/closing tags */
|
||||
len = constructed_data_len(&apdu[0], apdu_len,
|
||||
PROP_START_TIME);
|
||||
ct_test(pTest, test_len == len);
|
||||
|
||||
/* 5. complex datatype - multiple elements */
|
||||
|
||||
|
||||
|
||||
/* 6. context tagged data, one element */
|
||||
test_len = 0;
|
||||
apdu_len = 0;
|
||||
len = encode_opening_tag(&apdu[apdu_len], 3);
|
||||
apdu_len += len;
|
||||
len = encode_context_unsigned(&apdu[apdu_len], 1, 91);
|
||||
test_len += len;
|
||||
apdu_len += len;
|
||||
len = encode_closing_tag(&apdu[apdu_len], 3);
|
||||
apdu_len += len;
|
||||
/* verify the length of the data inside the opening/closing tags */
|
||||
len = constructed_data_len(&apdu[0], apdu_len,
|
||||
PROP_REQUESTED_SHED_LEVEL);
|
||||
ct_test(pTest, test_len == len);
|
||||
}
|
||||
|
||||
/* returns true if matching or same, false if different */
|
||||
bool bacapp_same_date(BACNET_DATE * date1, BACNET_DATE * date2)
|
||||
{
|
||||
@@ -722,7 +1007,8 @@ int main(void)
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testBACnetApplicationData);
|
||||
assert(rc);
|
||||
|
||||
rc = ct_addTestFunction(pTest, testBACnetApplicationDataLength);
|
||||
assert(rc);
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void) ct_report(pTest);
|
||||
|
||||
+17
-1
@@ -64,14 +64,29 @@ extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int bacapp_decode_application_data(uint8_t * apdu,
|
||||
uint8_t apdu_len, BACNET_APPLICATION_DATA_VALUE * value);
|
||||
int max_apdu_len, BACNET_APPLICATION_DATA_VALUE * value);
|
||||
|
||||
int bacapp_encode_application_data(uint8_t * apdu,
|
||||
BACNET_APPLICATION_DATA_VALUE * value);
|
||||
|
||||
int bacapp_decode_context_data(uint8_t * apdu,
|
||||
int max_apdu_len, BACNET_APPLICATION_DATA_VALUE * value,
|
||||
BACNET_PROPERTY_ID property);
|
||||
|
||||
int bacapp_encode_context_data(uint8_t * apdu,
|
||||
BACNET_APPLICATION_DATA_VALUE * value,
|
||||
BACNET_PROPERTY_ID property);
|
||||
|
||||
bool bacapp_copy(BACNET_APPLICATION_DATA_VALUE * dest_value,
|
||||
BACNET_APPLICATION_DATA_VALUE * src_value);
|
||||
|
||||
/* returns the length of data between an opening tag and a closing tag.
|
||||
Expects that the first octet contain the opening tag.
|
||||
Include a value property identifier for context specific data
|
||||
such as the value received in a WriteProperty request */
|
||||
int bacapp_data_len(uint8_t *apdu, int max_apdu_len,
|
||||
BACNET_PROPERTY_ID property);
|
||||
|
||||
#if PRINT_ENABLED
|
||||
#define BACAPP_PRINT_ENABLED
|
||||
#else
|
||||
@@ -99,6 +114,7 @@ extern "C" {
|
||||
bool bacapp_same_value(BACNET_APPLICATION_DATA_VALUE * value,
|
||||
BACNET_APPLICATION_DATA_VALUE * test_value);
|
||||
|
||||
void testBACnetApplicationDataLength(Test * pTest);
|
||||
void testBACnetApplicationData(Test * pTest);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -618,20 +618,18 @@ int decode_tag_number(uint8_t * apdu, uint8_t * tag_number)
|
||||
return len;
|
||||
}
|
||||
|
||||
static bool decode_is_opening_tag(uint8_t * apdu)
|
||||
bool decode_is_opening_tag(uint8_t * apdu)
|
||||
{
|
||||
return ((apdu[0] & 0x07) == 6);
|
||||
}
|
||||
|
||||
/* from clause 20.2.1.3.2 Constructed Data */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
static bool decode_is_closing_tag(uint8_t * apdu)
|
||||
bool decode_is_closing_tag(uint8_t * apdu)
|
||||
{
|
||||
return ((apdu[0] & 0x07) == 7);
|
||||
}
|
||||
|
||||
/* from clause 20.2.1.3.2 Constructed Data */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
/* from clause 20.2.1.3.2 Constructed Data */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int decode_tag_number_and_value(uint8_t * apdu,
|
||||
|
||||
@@ -53,6 +53,7 @@ extern "C" {
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_opening_tag(uint8_t * apdu, uint8_t tag_number);
|
||||
int encode_closing_tag(uint8_t * apdu, uint8_t tag_number);
|
||||
int decode_tag_number(uint8_t * apdu, uint8_t * tag_number);
|
||||
int decode_tag_number_and_value(uint8_t * apdu, uint8_t * tag_number,
|
||||
uint32_t * value);
|
||||
/* returns true if the tag is context specific */
|
||||
@@ -63,6 +64,10 @@ extern "C" {
|
||||
bool decode_is_closing_tag_number(uint8_t * apdu, uint8_t tag_number);
|
||||
/* returns true if the tag is context specific and matches */
|
||||
bool decode_is_context_tag(uint8_t * apdu, uint8_t tag_number);
|
||||
/* returns true if the tag is an opening tag */
|
||||
bool decode_is_opening_tag(uint8_t * apdu);
|
||||
/* returns true if the tag is a closing tag */
|
||||
bool decode_is_closing_tag(uint8_t * apdu);
|
||||
|
||||
/* from clause 20.2.2 Encoding of a Null Value */
|
||||
int encode_tagged_null(uint8_t * apdu);
|
||||
|
||||
@@ -252,25 +252,20 @@ typedef enum {
|
||||
PROP_STRUCTURED_OBJECT_LIST = 209,
|
||||
PROP_SUBORDINATE_ANNOTATIONS = 210,
|
||||
PROP_SUBORDINATE_LIST = 211,
|
||||
/* enumerations 212-225 are used in Addendum e to
|
||||
/* enumerations 212-225 are used in Addendum e to
|
||||
ANSI/ASHRAE 135-2004 */
|
||||
PROP_ACTUAL_SHED_LEVEL = 212,
|
||||
PROP_DUTY_WINDOW = 213,
|
||||
PROP_EXPECTED_SHED_LEVEL = 214,
|
||||
PROP_FULL_DUTY_BASELINE = 215,
|
||||
/* FIXME: is this the right enumeration? */
|
||||
/* PROP_NODE_SUBTYPE = 216, */
|
||||
/* PROP_NODE_TYPE = 217, */
|
||||
/* note: missing enumerations for now */
|
||||
PROP_REQUESTED_SHED_LEVEL = 218,
|
||||
PROP_SHED_DURATION = 219,
|
||||
PROP_SHED_LEVEL_DESCRIPTIONS = 220,
|
||||
PROP_SHED_LEVELS = 221,
|
||||
PROP_STATE_DESCRIPTION = 222,
|
||||
/* FIXME: is this the right enumeration? */
|
||||
/* PROP_STRUCTURED_OBJECT_LIST = 223, */
|
||||
/* PROP_SUBORDINATE_ANNOTATIONS = 224, */
|
||||
/* PROP_SUBORDINATE_LIST = 225, */
|
||||
/* enumerations 226-235 are used in Addendum f to
|
||||
/* note: missing enumerations for now */
|
||||
/* enumerations 226-235 are used in Addendum f to
|
||||
ANSI/ASHRAE 135-2004 */
|
||||
PROP_LOG_DEVICE_OBJECT_PROPERTIES = 236,
|
||||
PROP_LOG_MULTIPLE_BUFFER = 237,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
+7
-3
@@ -67,8 +67,9 @@ int wp_encode_apdu(uint8_t * apdu,
|
||||
/* propertyValue */
|
||||
len = encode_opening_tag(&apdu[apdu_len], 3);
|
||||
apdu_len += len;
|
||||
len =
|
||||
bacapp_encode_application_data(&apdu[apdu_len], &data->value);
|
||||
for (len = 0; len < data->application_data_len; len++) {
|
||||
apdu[apdu_len++] = data->application_data[len];
|
||||
}
|
||||
apdu_len += len;
|
||||
len = encode_closing_tag(&apdu[apdu_len], 3);
|
||||
apdu_len += len;
|
||||
@@ -127,10 +128,13 @@ int wp_decode_service_request(uint8_t * apdu,
|
||||
return -1;
|
||||
/* a tag number of 3 is not extended so only one octet */
|
||||
len++;
|
||||
/* determine the length of the data blob */
|
||||
|
||||
|
||||
/* FIXME: decode the length of the context specific tag value */
|
||||
if (decode_is_context_specific(&apdu[len]))
|
||||
return -2;
|
||||
/* FIXME: what if the length is more than 255 */
|
||||
/* FIXME: what if the length is more than 255 */
|
||||
len += bacapp_decode_application_data(&apdu[len],
|
||||
(uint8_t)(apdu_len - len), &data->value);
|
||||
/* FIXME: check the return value; abort if no valid data? */
|
||||
|
||||
+4
-2
@@ -41,13 +41,15 @@
|
||||
|
||||
/* write property can have application tagged data, or context tagged data,
|
||||
or even complex data types (i.e. opening and closing tag around data).
|
||||
It could also have more than one value. */
|
||||
It could also have more than one value or element. */
|
||||
|
||||
typedef struct BACnet_Write_Property_Data {
|
||||
BACNET_OBJECT_TYPE object_type;
|
||||
uint32_t object_instance;
|
||||
BACNET_PROPERTY_ID object_property;
|
||||
int32_t array_index; /* use BACNET_ARRAY_ALL when not setting */
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
uint8_t *application_data;
|
||||
int application_data_len;
|
||||
uint8_t priority; /* use BACNET_NO_PRIORITY if no priority */
|
||||
} BACNET_WRITE_PROPERTY_DATA;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user