From 6078373f125393b51672a7474c24e0f169fd14de Mon Sep 17 00:00:00 2001 From: skarg Date: Sat, 4 Jun 2005 22:31:37 +0000 Subject: [PATCH] corrected code and added unit testing for Atomic Write File. Fixed the encode of octet strings, and added unit testing. --- bacnet-stack/awf.c | 378 +++++++++++++--------------------------- bacnet-stack/awf.h | 19 +- bacnet-stack/bacdcode.c | 53 +++++- 3 files changed, 182 insertions(+), 268 deletions(-) diff --git a/bacnet-stack/awf.c b/bacnet-stack/awf.c index b4e44c32..3421ac0f 100644 --- a/bacnet-stack/awf.c +++ b/bacnet-stack/awf.c @@ -36,7 +36,7 @@ #include "bacdcode.h" #include "bacdef.h" #include "device.h" -#include "wp.h" +#include "awf.h" // Atomic Write File @@ -44,7 +44,7 @@ int awf_encode_apdu( uint8_t *apdu, uint8_t invoke_id, - BACNET_WRITE_PROPERTY_DATA *data) + BACNET_ATOMIC_WRITE_FILE_DATA *data) { int apdu_len = 0; // total length of the apdu, return value @@ -53,7 +53,7 @@ int awf_encode_apdu( apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; apdu[1] = encode_max_segs_max_apdu(0, Device_Max_APDU_Length_Accepted()); apdu[2] = invoke_id; - apdu[3] = SERVICE_CONFIRMED_WRITE_PROPERTY; // service choice + apdu[3] = SERVICE_CONFIRMED_ATOMIC_WRITE_FILE; // service choice apdu_len = 4; apdu_len += encode_context_object_id(&apdu[apdu_len], 0, data->object_type, data->object_instance); @@ -64,20 +64,19 @@ int awf_encode_apdu( { case FILE_RECORD_ACCESS: apdu_len += encode_tagged_signed(&apdu[apdu_len], - data->fileStartRecord); + data->type.record.fileStartRecord); apdu_len += encode_tagged_unsigned(&apdu[apdu_len], - data->returnedRecordCount); - apdu_len += encode_tagged_octet_string(&apdu[apdu_len], - data->fileData, data->fileDataLength); + data->type.record.returnedRecordCount); break; case FILE_STREAM_ACCESS: - default: apdu_len += encode_tagged_signed(&apdu[apdu_len], - data->fileStartPosition); - apdu_len += encode_tagged_octet_string(&apdu[apdu_len], - data->fileData, data->fileDataLength); + data->type.stream.fileStartPosition); + break; + default: break; } + apdu_len += encode_tagged_octet_string(&apdu[apdu_len], + data->fileData, data->fileDataLength); apdu_len += encode_closing_tag(&apdu[apdu_len], 2); } @@ -88,19 +87,14 @@ int awf_encode_apdu( int awf_decode_service_request( uint8_t *apdu, unsigned apdu_len, - BACNET_WRITE_PROPERTY_DATA *data) + BACNET_ATOMIC_WRITE_FILE_DATA *data) { int len = 0; int tag_len = 0; uint8_t tag_number = 0; uint32_t len_value_type = 0; int type = 0; // for decoding - int property = 0; // for decoding - unsigned unsigned_value = 0; - int hour, min, sec, hundredths; - int year, month, day, wday; - int object_type = 0; - uint32_t instance = 0; + int access = 0; // for decoding // check for value pointers if (apdu_len && data) @@ -110,120 +104,79 @@ int awf_decode_service_request( return -1; len += decode_object_id(&apdu[len], &type, &data->object_instance); data->object_type = type; - // Tag 1: Property ID + // Tag 1: Access len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); if (tag_number != 1) return -1; - len += decode_enumerated(&apdu[len], len_value_type, &property); - data->object_property = property; - // Tag 2: Optional Array Index - // note: decode without incrementing len so we can check for opening tag - tag_len = decode_tag_number_and_value(&apdu[len], - &tag_number, &len_value_type); - if (tag_number == 2) - { - len += tag_len; - len += decode_unsigned(&apdu[len], len_value_type, - &unsigned_value); - data->array_index = unsigned_value; - } - else - data->array_index = BACNET_ARRAY_ALL; - // Tag 3: opening context tag */ - if (!decode_is_opening_tag_number(&apdu[len], 3)) + len += decode_enumerated(&apdu[len], len_value_type, &access); + data->access = access; + // Tag 2: Opening Context Tag + if (!decode_is_opening_tag_number(&apdu[len], 2)) return -1; - // a tag number of 3 is not extended so only one octet + // a tag number of 2 is not extended so only one octet len++; - tag_len = decode_tag_number_and_value(&apdu[len], - &tag_number, &len_value_type); - if (tag_len) - { - len += tag_len; - if (tag_number == BACNET_APPLICATION_TAG_NULL) - { - data->value.tag = tag_number; - } - else if (tag_number == BACNET_APPLICATION_TAG_BOOLEAN) - { - data->value.tag = tag_number; - data->value.type.Boolean = decode_boolean(len_value_type); - } - else if (tag_number == BACNET_APPLICATION_TAG_UNSIGNED_INT) - { - data->value.tag = tag_number; - len += decode_unsigned(&apdu[len], - len_value_type, - &data->value.type.Unsigned_Int); - } - else if (tag_number == BACNET_APPLICATION_TAG_SIGNED_INT) - { - data->value.tag = tag_number; - len += decode_signed(&apdu[len], - len_value_type, - &data->value.type.Signed_Int); - } - else if (tag_number == BACNET_APPLICATION_TAG_REAL) - { - data->value.tag = tag_number; - len += decode_real(&apdu[len],&(data->value.type.Real)); - } - else if (tag_number == BACNET_APPLICATION_TAG_ENUMERATED) - { - data->value.tag = tag_number; - len += decode_enumerated(&apdu[len], - len_value_type, - &data->value.type.Enumerated); - } - else if (tag_number == BACNET_APPLICATION_TAG_DATE) - { - data->value.tag = tag_number; - len += decode_date(&apdu[len], &year, &month, &day, &wday); - data->value.type.Date.year = year; - data->value.type.Date.month = month; - data->value.type.Date.day = day; - data->value.type.Date.wday = wday; - } - else if (tag_number == BACNET_APPLICATION_TAG_TIME) - { - data->value.tag = tag_number; - len += decode_bacnet_time(&apdu[len], &hour, &min, &sec, &hundredths); - data->value.type.Time.hour = hour; - data->value.type.Time.min = min; - data->value.type.Time.sec = sec; - data->value.type.Time.hundredths = hundredths; - } - else if (tag_number == BACNET_APPLICATION_TAG_OBJECT_ID) - { - data->value.tag = tag_number; - len += decode_object_id(&apdu[len], - &object_type, - &instance); - data->value.type.Object_ID.type = object_type; - data->value.type.Object_ID.instance = instance; - } - else - return -1; - } - else - return -1; - if (!decode_is_closing_tag_number(&apdu[len], 3)) - return -1; - // a tag number of 3 is not extended so only one octet - len++; - // Tag 4: optional Priority - data->priority = BACNET_MAX_PRIORITIES; - if ((unsigned)len < apdu_len) + if (access == FILE_RECORD_ACCESS) { + // fileStartRecord tag_len = decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); - if (tag_number == 4) - { - len += tag_len; - len = decode_unsigned(&apdu[len], len_value_type, &unsigned_value); - data->priority = unsigned_value; - } + len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) + return -1; + len += decode_signed(&apdu[len], + len_value_type, + &data->type.record.fileStartRecord); + // returnedRecordCount + tag_len = decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value_type); + len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) + return -1; + len += decode_unsigned(&apdu[len], + len_value_type, + &data->type.record.returnedRecordCount); + // fileData + tag_len = decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value_type); + len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) + return -1; + len += decode_octet_string(&apdu[len], + len_value_type, + data->fileData, + data->fileDataLength); + data->fileDataLength = len_value_type; } + else if (access == FILE_STREAM_ACCESS) + { + // fileStartPosition + tag_len = decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value_type); + len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) + return -1; + len += decode_signed(&apdu[len], + len_value_type, + &data->type.stream.fileStartPosition); + // fileData + tag_len = decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value_type); + len += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) + return -1; + len += decode_octet_string(&apdu[len], + len_value_type, + data->fileData, + data->fileDataLength); + data->fileDataLength = len_value_type; + } + else + return -1; + if (!decode_is_closing_tag_number(&apdu[len], 2)) + return -1; + // a tag number of 2 is not extended so only one octet + len++; } return len; @@ -233,7 +186,7 @@ int awf_decode_apdu( uint8_t *apdu, unsigned apdu_len, uint8_t *invoke_id, - BACNET_WRITE_PROPERTY_DATA *data) + BACNET_ATOMIC_WRITE_FILE_DATA *data) { int len = 0; unsigned offset = 0; @@ -245,7 +198,7 @@ int awf_decode_apdu( return -1; // apdu[1] = encode_max_segs_max_apdu(0, Device_Max_APDU_Length_Accepted()); *invoke_id = apdu[2]; /* invoke id - filled in by net layer */ - if (apdu[3] != SERVICE_CONFIRMED_WRITE_PROPERTY) + if (apdu[3] != SERVICE_CONFIRMED_ATOMIC_WRITE_FILE) return -1; offset = 4; @@ -265,16 +218,17 @@ int awf_decode_apdu( #include #include "ctest.h" -void testWritePropertyTag(Test * pTest, - BACNET_WRITE_PROPERTY_DATA *data) +void testAtomicWriteFileAccess(Test * pTest, + BACNET_ATOMIC_WRITE_FILE_DATA *data) { - BACNET_WRITE_PROPERTY_DATA test_data = {0}; + BACNET_ATOMIC_WRITE_FILE_DATA test_data = {0}; uint8_t apdu[480] = {0}; int len = 0; int apdu_len = 0; uint8_t invoke_id = 128; uint8_t test_invoke_id = 0; - + uint8_t test_octet_string[128] = "bingo!"; + len = awf_encode_apdu( &apdu[0], invoke_id, @@ -282,6 +236,8 @@ void testWritePropertyTag(Test * pTest, ct_test(pTest, len != 0); apdu_len = len; + test_data.fileData = test_octet_string; + test_data.fileDataLength = sizeof(test_octet_string); len = awf_decode_apdu( &apdu[0], apdu_len, @@ -290,141 +246,53 @@ void testWritePropertyTag(Test * pTest, ct_test(pTest, len != -1); ct_test(pTest, test_data.object_type == data->object_type); ct_test(pTest, test_data.object_instance == data->object_instance); - ct_test(pTest, test_data.object_property == data->object_property); - ct_test(pTest, test_data.array_index == data->array_index); - ct_test(pTest, test_data.value.tag == data->value.tag); - switch (test_data.value.tag) + ct_test(pTest, test_data.access == data->access); + if (test_data.access == FILE_STREAM_ACCESS) { - case BACNET_APPLICATION_TAG_NULL: - break; - case BACNET_APPLICATION_TAG_BOOLEAN: - ct_test(pTest, test_data.value.type.Boolean == - data->value.type.Boolean); - break; - case BACNET_APPLICATION_TAG_UNSIGNED_INT: - ct_test(pTest, test_data.value.type.Unsigned_Int == - data->value.type.Unsigned_Int); - break; - case BACNET_APPLICATION_TAG_SIGNED_INT: - ct_test(pTest, test_data.value.type.Signed_Int == - data->value.type.Signed_Int); - break; - case BACNET_APPLICATION_TAG_REAL: - ct_test(pTest, test_data.value.type.Real == - data->value.type.Real); - break; - case BACNET_APPLICATION_TAG_ENUMERATED: - ct_test(pTest, test_data.value.type.Enumerated == - data->value.type.Enumerated); - break; - case BACNET_APPLICATION_TAG_DATE: - ct_test(pTest, test_data.value.type.Date.year == - data->value.type.Date.year); - ct_test(pTest, test_data.value.type.Date.month == - data->value.type.Date.month); - ct_test(pTest, test_data.value.type.Date.day == - data->value.type.Date.day); - ct_test(pTest, test_data.value.type.Date.wday == - data->value.type.Date.wday); - break; - case BACNET_APPLICATION_TAG_TIME: - ct_test(pTest, test_data.value.type.Time.hour == - data->value.type.Time.hour); - ct_test(pTest, test_data.value.type.Time.min == - data->value.type.Time.min); - ct_test(pTest, test_data.value.type.Time.sec == - data->value.type.Time.sec); - ct_test(pTest, test_data.value.type.Time.hundredths == - data->value.type.Time.hundredths); - break; - case BACNET_APPLICATION_TAG_OBJECT_ID: - ct_test(pTest, test_data.value.type.Object_ID.type == - data->value.type.Object_ID.type); - ct_test(pTest, test_data.value.type.Object_ID.instance == - data->value.type.Object_ID.instance); - break; - default: - break; + ct_test(pTest, test_data.type.stream.fileStartPosition == + data->type.stream.fileStartPosition); } + else if (test_data.access == FILE_RECORD_ACCESS) + { + ct_test(pTest, test_data.type.record.fileStartRecord == + data->type.record.fileStartRecord); + ct_test(pTest, test_data.type.record.returnedRecordCount == + data->type.record.returnedRecordCount); + } + ct_test(pTest, test_data.fileDataLength == + data->fileDataLength); + ct_test(pTest, memcmp( + &test_data.fileData[0], + &data->fileData[0], + test_data.fileDataLength) == 0); } -void testWriteProperty(Test * pTest) +void testAtomicWriteFile(Test * pTest) { - BACNET_WRITE_PROPERTY_DATA data = {0}; - - data.value.tag = BACNET_APPLICATION_TAG_NULL; - testWritePropertyTag(pTest, &data); - - data.value.tag = BACNET_APPLICATION_TAG_BOOLEAN; - data.value.type.Boolean = true; - testWritePropertyTag(pTest, &data); - data.value.type.Boolean = false; - testWritePropertyTag(pTest, &data); - - data.value.tag = BACNET_APPLICATION_TAG_UNSIGNED_INT; - data.value.type.Unsigned_Int = 0; - testWritePropertyTag(pTest, &data); - data.value.type.Unsigned_Int = 0xFFFF; - testWritePropertyTag(pTest, &data); - data.value.type.Unsigned_Int = 0xFFFFFFFF; - testWritePropertyTag(pTest, &data); - - data.value.tag = BACNET_APPLICATION_TAG_SIGNED_INT; - data.value.type.Signed_Int = 0; - testWritePropertyTag(pTest, &data); - data.value.type.Signed_Int = -1; - testWritePropertyTag(pTest, &data); - data.value.type.Signed_Int = 32768; - testWritePropertyTag(pTest, &data); - data.value.type.Signed_Int = -32768; - testWritePropertyTag(pTest, &data); + BACNET_ATOMIC_WRITE_FILE_DATA data = {0}; + uint8_t *test_octet_string = "Joshua-Mary-Anna-Christopher"; - data.value.tag = BACNET_APPLICATION_TAG_REAL; - data.value.type.Real = 0.0; - testWritePropertyTag(pTest, &data); - data.value.type.Real = -1.0; - testWritePropertyTag(pTest, &data); - data.value.type.Real = 1.0; - testWritePropertyTag(pTest, &data); - data.value.type.Real = 3.14159; - testWritePropertyTag(pTest, &data); - data.value.type.Real = -3.14159; - testWritePropertyTag(pTest, &data); - - data.value.tag = BACNET_APPLICATION_TAG_ENUMERATED; - data.value.type.Enumerated = 0; - testWritePropertyTag(pTest, &data); - data.value.type.Enumerated = 0xFFFF; - testWritePropertyTag(pTest, &data); - data.value.type.Enumerated = 0xFFFFFFFF; - testWritePropertyTag(pTest, &data); + data.object_type = OBJECT_FILE; + data.object_instance = 1; + data.access = FILE_STREAM_ACCESS; + data.type.stream.fileStartPosition = 0; + data.fileData = test_octet_string; + data.fileDataLength = strlen(test_octet_string); + testAtomicWriteFileAccess(pTest, &data); + + data.object_type = OBJECT_FILE; + data.object_instance = 1; + data.access = FILE_RECORD_ACCESS; + data.type.record.fileStartRecord = 1; + data.type.record.returnedRecordCount = 2; + data.fileData = test_octet_string; + data.fileDataLength = strlen(test_octet_string); + testAtomicWriteFileAccess(pTest, &data); - data.value.tag = BACNET_APPLICATION_TAG_DATE; - data.value.type.Date.year = 5; - data.value.type.Date.month = 5; - data.value.type.Date.day = 22; - data.value.type.Date.wday = 1; - testWritePropertyTag(pTest, &data); - - data.value.tag = BACNET_APPLICATION_TAG_TIME; - data.value.type.Time.hour = 23; - data.value.type.Time.min = 59; - data.value.type.Time.sec = 59; - data.value.type.Time.hundredths = 12; - testWritePropertyTag(pTest, &data); - - data.value.tag = BACNET_APPLICATION_TAG_OBJECT_ID; - data.value.type.Object_ID.type = OBJECT_ANALOG_INPUT; - data.value.type.Object_ID.instance = 0; - testWritePropertyTag(pTest, &data); - data.value.type.Object_ID.type = OBJECT_LIFE_SAFETY_ZONE; - data.value.type.Object_ID.instance = BACNET_MAX_INSTANCE; - testWritePropertyTag(pTest, &data); - return; } -#ifdef TEST_WRITE_PROPERTY +#ifdef TEST_ATOMIC_WRITE_FILE uint16_t Device_Max_APDU_Length_Accepted(void) { return MAX_APDU; @@ -435,9 +303,9 @@ int main(void) Test *pTest; bool rc; - pTest = ct_create("BACnet WriteProperty", NULL); + pTest = ct_create("BACnet AtomicWriteFile", NULL); /* individual tests */ - rc = ct_addTestFunction(pTest, testWriteProperty); + rc = ct_addTestFunction(pTest, testAtomicWriteFile); assert(rc); ct_setStream(pTest, stdout); diff --git a/bacnet-stack/awf.h b/bacnet-stack/awf.h index 7324c8d7..165c92b0 100644 --- a/bacnet-stack/awf.h +++ b/bacnet-stack/awf.h @@ -48,36 +48,37 @@ typedef struct BACnet_Atomic_Write_File_Data struct { int fileStartPosition; - uint8_t *fileData; - unsigned fileDataLength; } stream; struct { int fileStartRecord; - uint8_t *fileData; - unsigned fileDataLength; unsigned returnedRecordCount; } record; - } -} BACNET_WRITE_PROPERTY_DATA; + } type; + // note: set the file data to an empty buffer + // and set the DataLength to the size of the empty buffer + // before decoding the data. + uint8_t *fileData; + unsigned fileDataLength; +} BACNET_ATOMIC_WRITE_FILE_DATA; // encode service int awf_encode_apdu( uint8_t *apdu, uint8_t invoke_id, - BACNET_WRITE_PROPERTY_DATA *data); + BACNET_ATOMIC_WRITE_FILE_DATA *data); // decode the service request only int awf_decode_service_request( uint8_t *apdu, unsigned apdu_len, - BACNET_WRITE_PROPERTY_DATA *data); + BACNET_ATOMIC_WRITE_FILE_DATA *data); int awf_decode_apdu( uint8_t *apdu, unsigned apdu_len, uint8_t *invoke_id, - BACNET_WRITE_PROPERTY_DATA *data); + BACNET_ATOMIC_WRITE_FILE_DATA *data); #ifdef TEST #include "ctest.h" diff --git a/bacnet-stack/bacdcode.c b/bacnet-stack/bacdcode.c index 5bbd5174..0dcfee0f 100644 --- a/bacnet-stack/bacdcode.c +++ b/bacnet-stack/bacdcode.c @@ -865,7 +865,7 @@ int encode_octet_string(uint8_t * apdu, const uint8_t *octet_string, if (len > (MAX_APDU - 6)) len = MAX_APDU - 6; for (i = 0; i < len; i++) { - apdu[i] = char_string[i]; + apdu[i] = octet_string[i]; } return len; @@ -882,7 +882,7 @@ int encode_tagged_octet_string(uint8_t * apdu, const uint8_t *octet_string, apdu_len = encode_tag(&apdu[0], BACNET_APPLICATION_TAG_OCTET_STRING, false, len); if ((apdu_len + len) < MAX_APDU) - apdu_len += encode_octet_string(&apdu[len], octet_string, len); + apdu_len += encode_octet_string(&apdu[apdu_len], octet_string, len); else apdu_len = 0; @@ -1554,7 +1554,50 @@ void testBACDCodeSigned(Test * pTest) return; } -void testBACDCodeString(Test * pTest) +void testBACDCodeOctetString(Test * pTest) +{ + uint8_t array[MAX_APDU] = { 0 }; + uint8_t encoded_array[MAX_APDU] = { 0 }; + char test_string[MAX_APDU] = { "" }; + char decoded_string[MAX_APDU] = { "" }; + int i; // for loop counter + int apdu_len; + int len; + char *test_string0 = ""; + uint8_t tag_number = 0; + uint32_t len_value = 0; + + apdu_len = encode_tagged_octet_string(&array[0], &test_string0[0], 0); + len = decode_tag_number_and_value(&array[0], &tag_number, &len_value); + len += decode_octet_string(&array[len], len_value, + &decoded_string[0], sizeof(decoded_string)); + ct_test(pTest, apdu_len == len); + ct_test(pTest, memcmp(&test_string0[0], &decoded_string[0], + len_value) == 0); + for (i = 0; i < (MAX_APDU - 6); i++) { + test_string[i] = '0' + (i % 10); + apdu_len = + encode_tagged_octet_string(&encoded_array[0], + &test_string[0], i); + len = decode_tag_number_and_value(&encoded_array[0], + &tag_number, &len_value); + len += decode_octet_string(&encoded_array[len], len_value, + &decoded_string[0], sizeof(decoded_string)); + if (apdu_len != len) { + printf("test octet string=#%d\n", i); + } + ct_test(pTest, apdu_len == len); + if (memcmp(&test_string[0], &decoded_string[0], len_value) != 0) { + printf("test octet string=#%d\n", i); + } + ct_test(pTest, memcmp(&test_string[0], &decoded_string[0], + len_value) == 0); + } + + return; +} + +void testBACDCodeCharacterString(Test * pTest) { uint8_t array[MAX_APDU] = { 0 }; uint8_t encoded_array[MAX_APDU] = { 0 }; @@ -1721,7 +1764,9 @@ int main(void) assert(rc); rc = ct_addTestFunction(pTest, testBACDCodeEnumerated); assert(rc); - rc = ct_addTestFunction(pTest, testBACDCodeString); + rc = ct_addTestFunction(pTest, testBACDCodeOctetString); + assert(rc); + rc = ct_addTestFunction(pTest, testBACDCodeCharacterString); assert(rc); rc = ct_addTestFunction(pTest, testBACDCodeObject); assert(rc);