From 7dec29d213cf55b1a3dabe9e594bece3386f0501 Mon Sep 17 00:00:00 2001 From: skarg Date: Sun, 5 Jun 2005 00:35:10 +0000 Subject: [PATCH] Added the Atomic Read File Ack encoding and decoding and unit tests. Corrected the Atomic Read File encoding and decoding. --- bacnet-stack/arf.c | 306 ++++++++++++++++++++++++++++++++++++++++----- bacnet-stack/arf.h | 9 +- 2 files changed, 279 insertions(+), 36 deletions(-) diff --git a/bacnet-stack/arf.c b/bacnet-stack/arf.c index b75be735..a23201bc 100644 --- a/bacnet-stack/arf.c +++ b/bacnet-stack/arf.c @@ -55,29 +55,29 @@ int arf_encode_apdu( apdu[2] = invoke_id; apdu[3] = SERVICE_CONFIRMED_ATOMIC_READ_FILE; // service choice apdu_len = 4; - apdu_len += encode_context_object_id(&apdu[apdu_len], 0, + apdu_len += encode_tagged_object_id(&apdu[apdu_len], data->object_type, data->object_instance); - apdu_len += encode_context_enumerated(&apdu[apdu_len], 1, - data->access); - apdu_len += encode_opening_tag(&apdu[apdu_len], 2); switch (data->access) { case FILE_STREAM_ACCESS: + apdu_len += encode_opening_tag(&apdu[apdu_len], 0); apdu_len += encode_tagged_signed(&apdu[apdu_len], data->type.stream.fileStartPosition); apdu_len += encode_tagged_unsigned(&apdu[apdu_len], data->type.stream.requestedOctetCount); + apdu_len += encode_closing_tag(&apdu[apdu_len], 0); break; case FILE_RECORD_ACCESS: + apdu_len += encode_opening_tag(&apdu[apdu_len], 1); apdu_len += encode_tagged_signed(&apdu[apdu_len], data->type.record.fileStartRecord); apdu_len += encode_tagged_unsigned(&apdu[apdu_len], - data->type.record.requestedRecordCount); + data->type.record.RecordCount); + apdu_len += encode_closing_tag(&apdu[apdu_len], 1); break; default: break; } - apdu_len += encode_closing_tag(&apdu[apdu_len], 2); } return apdu_len; @@ -94,30 +94,20 @@ int arf_decode_service_request( uint8_t tag_number = 0; uint32_t len_value_type = 0; int type = 0; // for decoding - int access = 0; // for decoding // check for value pointers if (apdu_len && data) { - // Tag 0: Object ID - if (!decode_is_context_tag(&apdu[len++], 0)) + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value_type); + if (tag_number != BACNET_APPLICATION_TAG_OBJECT_ID) return -1; len += decode_object_id(&apdu[len], &type, &data->object_instance); data->object_type = type; - // 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, &access); - data->access = access; - // Tag 2: Opening Context Tag - if (!decode_is_opening_tag_number(&apdu[len], 2)) - return -1; - // a tag number of 2 is not extended so only one octet - len++; - if (access == FILE_STREAM_ACCESS) + if (decode_is_opening_tag_number(&apdu[len], 0)) { + data->access = FILE_STREAM_ACCESS; + // a tag number is not extended so only one octet + len++; // fileStartPosition tag_len = decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); @@ -136,9 +126,16 @@ int arf_decode_service_request( len += decode_unsigned(&apdu[len], len_value_type, &data->type.stream.requestedOctetCount); + if (!decode_is_closing_tag_number(&apdu[len], 0)) + return -1; + // a tag number is not extended so only one octet + len++; } - else if (access == FILE_RECORD_ACCESS) + else if (decode_is_opening_tag_number(&apdu[len], 1)) { + data->access = FILE_RECORD_ACCESS; + // a tag number is not extended so only one octet + len++; // fileStartRecord tag_len = decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); @@ -148,7 +145,7 @@ int arf_decode_service_request( len += decode_signed(&apdu[len], len_value_type, &data->type.record.fileStartRecord); - // requestedRecordCount + // RecordCount tag_len = decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); len += tag_len; @@ -156,14 +153,14 @@ int arf_decode_service_request( return -1; len += decode_unsigned(&apdu[len], len_value_type, - &data->type.record.requestedRecordCount); + &data->type.record.RecordCount); + if (!decode_is_closing_tag_number(&apdu[len], 1)) + return -1; + // a tag number is not extended so only one octet + len++; } 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; @@ -200,11 +197,254 @@ int arf_decode_apdu( return len; } +// encode service +int arf_ack_encode_apdu( + uint8_t *apdu, + uint8_t invoke_id, + BACNET_ATOMIC_READ_FILE_DATA *data) +{ + int apdu_len = 0; // total length of the apdu, return value + + if (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_ATOMIC_READ_FILE; // service choice + apdu_len = 4; + // endOfFile + apdu_len += encode_tagged_boolean(&apdu[apdu_len], data->endOfFile); + switch (data->access) + { + case FILE_STREAM_ACCESS: + apdu_len += encode_opening_tag(&apdu[apdu_len], 0); + apdu_len += encode_tagged_signed(&apdu[apdu_len], + data->type.stream.fileStartPosition); + apdu_len += encode_tagged_octet_string(&apdu[apdu_len], + data->fileData, data->fileDataLength); + apdu_len += encode_closing_tag(&apdu[apdu_len], 0); + break; + case FILE_RECORD_ACCESS: + apdu_len += encode_opening_tag(&apdu[apdu_len], 1); + apdu_len += encode_tagged_signed(&apdu[apdu_len], + data->type.record.fileStartRecord); + apdu_len += encode_tagged_unsigned(&apdu[apdu_len], + data->type.record.RecordCount); + apdu_len += encode_tagged_octet_string(&apdu[apdu_len], + data->fileData, data->fileDataLength); + apdu_len += encode_closing_tag(&apdu[apdu_len], 1); + break; + default: + break; + } + } + + return apdu_len; +} + +// decode the service request only +int arf_ack_decode_service_request( + uint8_t *apdu, + unsigned apdu_len, + BACNET_ATOMIC_READ_FILE_DATA *data) +{ + int len = 0; + int tag_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + + // check for value pointers + if (apdu_len && data) + { + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value_type); + if (tag_number != BACNET_APPLICATION_TAG_BOOLEAN) + return -1; + data->endOfFile = decode_boolean(len_value_type); + if (decode_is_opening_tag_number(&apdu[len], 0)) + { + data->access = FILE_STREAM_ACCESS; + // a tag number is not extended so only one octet + len++; + // 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; + if (!decode_is_closing_tag_number(&apdu[len], 0)) + return -1; + // a tag number is not extended so only one octet + len++; + } + else if (decode_is_opening_tag_number(&apdu[len], 1)) + { + data->access = FILE_RECORD_ACCESS; + // a tag number is not extended so only one octet + len++; + // fileStartRecord + 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.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.RecordCount); + // 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; + if (!decode_is_closing_tag_number(&apdu[len], 1)) + return -1; + // a tag number is not extended so only one octet + len++; + } + else + return -1; + } + + return len; +} + +int arf_ack_decode_apdu( + uint8_t *apdu, + unsigned apdu_len, + uint8_t *invoke_id, + BACNET_ATOMIC_READ_FILE_DATA *data) +{ + int len = 0; + unsigned offset = 0; + + if (!apdu) + return -1; + // optional checking - most likely was already done prior to this call + if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) + 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_ATOMIC_READ_FILE) + return -1; + offset = 4; + + if (apdu_len > offset) + { + len = arf_ack_decode_service_request( + &apdu[offset], + apdu_len - offset, + data); + } + + return len; +} + + #ifdef TEST #include #include #include "ctest.h" +void testAtomicReadFileAckAccess(Test * pTest, + BACNET_ATOMIC_READ_FILE_DATA *data) +{ + BACNET_ATOMIC_READ_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 = arf_ack_encode_apdu( + &apdu[0], + invoke_id, + data); + ct_test(pTest, len != 0); + apdu_len = len; + + test_data.fileData = test_octet_string; + test_data.fileDataLength = sizeof(test_octet_string); + len = arf_ack_decode_apdu( + &apdu[0], + apdu_len, + &test_invoke_id, + &test_data); + ct_test(pTest, len != -1); + ct_test(pTest, test_data.endOfFile == data->endOfFile); + ct_test(pTest, test_data.access == data->access); + if (test_data.access == FILE_STREAM_ACCESS) + { + 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.RecordCount == + data->type.record.RecordCount); + } + ct_test(pTest, test_data.fileDataLength == + data->fileDataLength); + ct_test(pTest, memcmp( + &test_data.fileData[0], + &data->fileData[0], + test_data.fileDataLength) == 0); +} + +void testAtomicReadFileAck(Test * pTest) +{ + BACNET_ATOMIC_READ_FILE_DATA data = {0}; + uint8_t *test_octet_string = "Joshua-Mary-Anna-Christopher"; + + + data.endOfFile = true; + data.access = FILE_STREAM_ACCESS; + data.type.stream.fileStartPosition = 0; + data.fileData = test_octet_string; + data.fileDataLength = strlen(test_octet_string); + testAtomicReadFileAckAccess(pTest, &data); + + data.endOfFile = false; + data.access = FILE_RECORD_ACCESS; + data.type.record.fileStartRecord = 1; + data.type.record.RecordCount = 2; + data.fileData = test_octet_string; + data.fileDataLength = strlen(test_octet_string); + testAtomicReadFileAckAccess(pTest, &data); + + return; +} + void testAtomicReadFileAccess(Test * pTest, BACNET_ATOMIC_READ_FILE_DATA *data) { @@ -242,8 +482,8 @@ void testAtomicReadFileAccess(Test * pTest, { ct_test(pTest, test_data.type.record.fileStartRecord == data->type.record.fileStartRecord); - ct_test(pTest, test_data.type.record.requestedRecordCount == - data->type.record.requestedRecordCount); + ct_test(pTest, test_data.type.record.RecordCount == + data->type.record.RecordCount); } } @@ -262,7 +502,7 @@ void testAtomicReadFile(Test * pTest) data.object_instance = 2; data.access = FILE_RECORD_ACCESS; data.type.record.fileStartRecord = 1; - data.type.record.requestedRecordCount = 2; + data.type.record.RecordCount = 2; testAtomicReadFileAccess(pTest, &data); return; @@ -283,6 +523,8 @@ int main(void) /* individual tests */ rc = ct_addTestFunction(pTest, testAtomicReadFile); assert(rc); + rc = ct_addTestFunction(pTest, testAtomicReadFileAck); + assert(rc); ct_setStream(pTest, stdout); ct_run(pTest); diff --git a/bacnet-stack/arf.h b/bacnet-stack/arf.h index a624cd35..17a7bcbe 100644 --- a/bacnet-stack/arf.h +++ b/bacnet-stack/arf.h @@ -53,14 +53,15 @@ typedef struct BACnet_Atomic_Read_File_Data struct { int fileStartRecord; - unsigned requestedRecordCount; + // requested or returned record count + unsigned RecordCount; } record; } 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. + // These are used for the ACK portion + // Set them to an unused buffer for the decode to fill uint8_t *fileData; unsigned fileDataLength; + bool endOfFile; } BACNET_ATOMIC_READ_FILE_DATA; // encode service