diff --git a/bacnet-stack/bacapp.c b/bacnet-stack/bacapp.c index a6c396d6..607deb1d 100644 --- a/bacnet-stack/bacapp.c +++ b/bacnet-stack/bacapp.c @@ -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 #include #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); diff --git a/bacnet-stack/bacapp.h b/bacnet-stack/bacapp.h index 005d5078..9bae59ab 100644 --- a/bacnet-stack/bacapp.h +++ b/bacnet-stack/bacapp.h @@ -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 diff --git a/bacnet-stack/bacdcode.c b/bacnet-stack/bacdcode.c index 7c9a71fa..0cbccfb4 100644 --- a/bacnet-stack/bacdcode.c +++ b/bacnet-stack/bacdcode.c @@ -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, diff --git a/bacnet-stack/bacdcode.h b/bacnet-stack/bacdcode.h index bf3a40d1..46925e9f 100644 --- a/bacnet-stack/bacdcode.h +++ b/bacnet-stack/bacdcode.h @@ -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); diff --git a/bacnet-stack/bacenum.h b/bacnet-stack/bacenum.h index 40f0b068..0a7ac93b 100644 --- a/bacnet-stack/bacenum.h +++ b/bacnet-stack/bacenum.h @@ -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, diff --git a/bacnet-stack/demo/object/lc.h b/bacnet-stack/demo/object/lc.h index 68f46e2c..3bea467c 100644 --- a/bacnet-stack/demo/object/lc.h +++ b/bacnet-stack/demo/object/lc.h @@ -1,6 +1,6 @@ /************************************************************************** * -* Copyright (C) 2006 Steve Karg +* Copyright (C) 2007 Steve Karg * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the diff --git a/bacnet-stack/wp.c b/bacnet-stack/wp.c index 27b5b2a8..f1eb359c 100644 --- a/bacnet-stack/wp.c +++ b/bacnet-stack/wp.c @@ -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? */ diff --git a/bacnet-stack/wp.h b/bacnet-stack/wp.h index 99098ef7..edc0eb22 100644 --- a/bacnet-stack/wp.h +++ b/bacnet-stack/wp.h @@ -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;