From 8d80d25322f43ca8ae297eea38dff03591e340bd Mon Sep 17 00:00:00 2001 From: petermcs Date: Fri, 4 Sep 2009 11:25:47 +0000 Subject: [PATCH] Added sample Confirmed Private Transfer implementation files and demo for same. --- bacnet-stack/demo/handler/h_pt.c | 255 +++++ bacnet-stack/demo/handler/h_pt_a.c | 226 ++++ bacnet-stack/demo/handler/s_ptransfer.c | 140 +++ bacnet-stack/demo/ptransfer/main.c | 371 +++++++ bacnet-stack/demo/ptransfer/makefile.b32 | 134 +++ bacnet-stack/demo/ptransfer/ptransfer.sln | 20 + bacnet-stack/demo/ptransfer/rdproperty.vcproj | 967 ++++++++++++++++++ bacnet-stack/doc/README.subversion | 2 + bacnet-stack/include/handlers.h | 13 +- bacnet-stack/include/mydata.h | 24 + bacnet-stack/lib/makefile.b32 | 4 + bacnet-stack/src/apdu.c | 37 +- bacnet-stack/src/bacstr.c | 1 + 13 files changed, 2183 insertions(+), 11 deletions(-) create mode 100644 bacnet-stack/demo/handler/h_pt.c create mode 100644 bacnet-stack/demo/handler/h_pt_a.c create mode 100644 bacnet-stack/demo/handler/s_ptransfer.c create mode 100644 bacnet-stack/demo/ptransfer/main.c create mode 100644 bacnet-stack/demo/ptransfer/makefile.b32 create mode 100644 bacnet-stack/demo/ptransfer/ptransfer.sln create mode 100644 bacnet-stack/demo/ptransfer/rdproperty.vcproj create mode 100644 bacnet-stack/include/mydata.h diff --git a/bacnet-stack/demo/handler/h_pt.c b/bacnet-stack/demo/handler/h_pt.c new file mode 100644 index 00000000..d5d6e1bb --- /dev/null +++ b/bacnet-stack/demo/handler/h_pt.c @@ -0,0 +1,255 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +//#include "arf.h" +/* demo objects */ +#include "device.h" +#include "ai.h" +#include "ao.h" +#if defined(BACFILE) +#include "bacfile.h" +#endif +#include "mydata.h" +#include "ptransfer.h" + +#define MYMAXSTR 32 +#define MYMAXBLOCK 8 + +DATABLOCK MyData[MYMAXBLOCK]; + +uint8_t IOBufferPT[MAX_APDU]; // Buffer for building response in + +void ProcessPT(BACNET_PRIVATE_TRANSFER_DATA *data) + +{ + int iLen; // Index to current location in data + char cBlockNumber; + uint32_t ulTemp; + int tag_len; + uint8_t tag_number; + uint32_t len_value_type; + BACNET_CHARACTER_STRING bsTemp; + + iLen = 0; + + // Decode the block number + + tag_len = decode_tag_number_and_value(&data->serviceParameters[iLen], &tag_number, &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) { // Bail out early if wrong type + data->serviceParametersLen = 0; // and signal unexpected error + return; + } + + iLen += decode_unsigned(&data->serviceParameters[iLen], len_value_type, &ulTemp); + cBlockNumber = (char)ulTemp; + if(cBlockNumber < MY_MAX_BLOCK) { + if(data->serviceNumber == MY_SVC_READ) { + // Read Response is an unsigned int with 0 for success or a non 0 error code + // For a successful read the 0 success code is followed by the block number + // and then the block contents which consist of 2 unsigned ints (in 0 to 255 + // range as they are really chars) a single precision real and a string which + // will be up to 32 chars + a nul + + iLen = 0; + + iLen += encode_application_unsigned(&IOBufferPT[iLen], MY_ERR_OK); // Signal success + iLen += encode_application_unsigned(&IOBufferPT[iLen], cBlockNumber); // Followed by the block number + iLen += encode_application_unsigned(&IOBufferPT[iLen], MyData[cBlockNumber].cMyByte1); // And Then the block contents + iLen += encode_application_unsigned(&IOBufferPT[iLen], MyData[cBlockNumber].cMyByte2); + iLen += encode_application_real(&IOBufferPT[iLen], MyData[cBlockNumber].fMyReal); + characterstring_init_ansi(&bsTemp, MyData[cBlockNumber].sMyString); + iLen += encode_application_character_string(&IOBufferPT[iLen], &bsTemp); + } + else { // Write operation + // Write block consists of the block number followed by the block contents as + // described above for the read operation. The returned result is an unsigned + // response which is 0 for success and a non 0 error code otherwise. + + tag_len = decode_tag_number_and_value(&data->serviceParameters[iLen], &tag_number, &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) { + data->serviceParametersLen = 0; + return; + } + iLen += decode_unsigned(&data->serviceParameters[iLen], len_value_type, &ulTemp); + MyData[cBlockNumber].cMyByte1 = (char)ulTemp; + + tag_len = decode_tag_number_and_value(&data->serviceParameters[iLen], &tag_number, &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) { + data->serviceParametersLen = 0; + return; + } + iLen += decode_unsigned(&data->serviceParameters[iLen], len_value_type, &ulTemp); + MyData[cBlockNumber].cMyByte2 = (char)ulTemp; + + tag_len = decode_tag_number_and_value(&data->serviceParameters[iLen], &tag_number, &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_REAL) { + data->serviceParametersLen = 0; + return; + } + iLen += decode_real(&data->serviceParameters[iLen], &MyData[cBlockNumber].fMyReal); + + tag_len = decode_tag_number_and_value(&data->serviceParameters[iLen], &tag_number, &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_CHARACTER_STRING) { + data->serviceParametersLen = 0; + return; + } + decode_character_string(&data->serviceParameters[iLen], len_value_type, &bsTemp); + strncpy(MyData[cBlockNumber].sMyString, characterstring_value(&bsTemp), MY_MAX_STR); // Only copy as much as we can accept + MyData[cBlockNumber].sMyString[MY_MAX_STR] = '\0'; // Make sure it is nul terminated + + iLen = encode_application_unsigned(&IOBufferPT[0], MY_ERR_OK); // Signal success + } + } + else { + iLen = encode_application_unsigned(&IOBufferPT[0], MY_ERR_BAD_INDEX); // Signal bad index + } + data->serviceParametersLen = iLen; + data->serviceParameters = IOBufferPT; +} + +/* + * This is called when we receive a private transfer packet. + * We parse the data, send the private part for processing and then send the + * response which the application generates. + * If there are any BACnet level errors we send an error response from here. + * If there are any application level errors they will be packeged up in the + * response block which we send back to the originator of the request. + * + */ + + +void handler_conf_private_trans( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + BACNET_PRIVATE_TRANSFER_DATA data; + int len; + int pdu_len; + bool error; + int bytes_sent; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; + + len = 0; + pdu_len = 0; + error = false; + bytes_sent = 0; + error_class = ERROR_CLASS_OBJECT; + error_code = ERROR_CODE_UNKNOWN_OBJECT; + +#if PRINT_ENABLED + fprintf(stderr,"Received Confirmed Private Transfer Request!\n"); +#endif + // encode the NPDU portion of the response packet as it will be needed + // no matter what the outcome. + + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, &npdu_data); + + if (service_data->segmented_message) + { + len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); +#if PRINT_ENABLED + fprintf(stderr,"CPT: Segmented Message. Sending Abort!\n"); +#endif + goto CPT_ABORT; + } + + len = ptransfer_decode_service_request(service_request, service_len, &data); + /* bad decoding - send an abort */ + if (len < 0) + { + len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, ABORT_REASON_OTHER, true); +#if PRINT_ENABLED + fprintf(stderr,"CPT: Bad Encoding. Sending Abort!\n"); +#endif + goto CPT_ABORT; + } + +// Simple example with service number of 0 for read block and 1 for write block +// We also only support our own vendor ID. In theory we could support others +// for compatability purposes but these interfaces are rarely documented... + + if((data.vendorID == BACNET_VENDOR_ID) && (data.serviceNumber <= MY_SVC_WRITE)){ // We only try to understand our own IDs and service numbers + ProcessPT(&data); // Will either return a result block or an app level status block + if(data.serviceParametersLen == 0){ // No respopnse means fatal error + error = true; + error_class = ERROR_CLASS_SERVICES; + error_code = ERROR_CODE_OTHER; + #if PRINT_ENABLED + fprintf(stderr,"CPT: Error servicing request!\n"); + #endif + } + len = ptransfer_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, &data); + } +else // Not our vendor ID or bad service parameter + { + error = true; + error_class = ERROR_CLASS_SERVICES; + error_code = ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED; +#if PRINT_ENABLED + fprintf(stderr,"CPT: Not our Vendor ID or invalid service code!\n"); +#endif + } + +if (error){ + len = ptransfer_error_encode_apdu(&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, error_class, error_code, &data); + } +CPT_ABORT: +pdu_len += len; +bytes_sent = datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len); + +#if PRINT_ENABLED +if (bytes_sent <= 0) + { + fprintf(stderr,"Failed to send PDU (%s)!\n", + strerror(errno)); + } +#endif + +return; +} + diff --git a/bacnet-stack/demo/handler/h_pt_a.c b/bacnet-stack/demo/handler/h_pt_a.c new file mode 100644 index 00000000..9818cc5b --- /dev/null +++ b/bacnet-stack/demo/handler/h_pt_a.c @@ -0,0 +1,226 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +//#include "arf.h" +/* demo objects */ +#include "device.h" +#include "ai.h" +#include "ao.h" +#include "ptransfer.h" +#include "mydata.h" +#if defined(BACFILE) +#include "bacfile.h" +#endif + +extern uint8_t IOBufferPT[300]; // Somewhere to build the encoded result block for Private Transfers + + +void DecodeBlock(char cBlockNum, uint8_t *pData) + +{ + int iLen; + uint32_t ulTemp; + int tag_len; + uint8_t tag_number; + uint32_t len_value_type; + BACNET_CHARACTER_STRING bsName; + DATABLOCK Response; + + iLen = 0; + + if(cBlockNum >= MY_MAX_BLOCK) + return; + + tag_len = decode_tag_number_and_value(&pData[iLen], &tag_number, &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) + return; + + iLen += decode_unsigned(&pData[iLen], len_value_type, &ulTemp); + Response.cMyByte1 = (char)ulTemp; + + tag_len = decode_tag_number_and_value(&pData[iLen], &tag_number, &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) + return; + + iLen += decode_unsigned(&pData[iLen], len_value_type, &ulTemp); + Response.cMyByte2 = (char)ulTemp; + + tag_len = decode_tag_number_and_value(&pData[iLen], &tag_number, &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_REAL) + return; + + iLen += decode_real(&pData[iLen], &Response.fMyReal); + + tag_len = decode_tag_number_and_value(&pData[iLen], &tag_number, &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_CHARACTER_STRING) + return; + + iLen += decode_character_string(&pData[iLen], len_value_type, &bsName); + strncpy(Response.sMyString, characterstring_value(&bsName), MY_MAX_STR); + Response.sMyString[MY_MAX_STR] = '\0'; // Make sure it is nul terminated + + printf("Private Transfer Read Block Response\n"); + printf("Data Block: %d\n", (int)cBlockNum); + printf(" First Byte : %d\n", (int)Response.cMyByte1); + printf(" Second Byte : %d\n", (int)Response.cMyByte2); + printf(" Real : %f\n", Response.fMyReal); + printf(" String : %s\n\n", Response.sMyString); +} + + + +void ProcessPTA(BACNET_PRIVATE_TRANSFER_DATA *data) + +{ + int iLen; // Index to current location in data + uint32_t uiErrorCode; + char cBlockNumber; + uint32_t ulTemp; + int tag_len; + uint8_t tag_number; + uint32_t len_value_type; + + iLen = 0; + + // Error code is returned for read and write operations + + tag_len = decode_tag_number_and_value(&data->serviceParameters[iLen], &tag_number, &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) { +#if PRINT_ENABLED + printf("CPTA: Bad Encoding!\n"); +#endif + return; + } + iLen += decode_unsigned(&data->serviceParameters[iLen], len_value_type, &uiErrorCode); + + if(data->serviceNumber == MY_SVC_READ) { // Read I/O block so should be full block of data or error + // Decode the error type and if necessary block number and then fetch the info + + if(uiErrorCode == MY_ERR_OK) { + // Block Number + tag_len = decode_tag_number_and_value(&data->serviceParameters[iLen], &tag_number, &len_value_type); + iLen += tag_len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) { +#if PRINT_ENABLED + printf("CPTA: Bad Encoding!\n"); +#endif + return; + } + + iLen += decode_unsigned(&data->serviceParameters[iLen], len_value_type, &ulTemp); + cBlockNumber = (char)ulTemp; + DecodeBlock(cBlockNumber, &data->serviceParameters[iLen]); + } + else { // Read error + printf("Private Transfer read operation returned error code: %u\n", uiErrorCode); + return; + } + } + else { // Write I/O block - should just be an OK type message + printf("Private Transfer write operation returned error code: %u\n", uiErrorCode); + } +} + + + +/* + * This is called when we receive a private transfer packet ack. + * We parse the response which the remote application generated + * and decide what to do next... + */ + +void handler_conf_private_trans_ack( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data) +{ + BACNET_PRIVATE_TRANSFER_DATA data; + int len; + +/* + * Note: + * We currently don't look at the source address and service data + * but we probably should to verify that the ack is oneit is what + * we were expecting. But this is just to silence some compiler + * warnings from Borland. + */ + src = src; + service_data = service_data; + + len = 0; + + + +#if PRINT_ENABLED + printf("Received Confirmed Private Transfer Ack!\n"); +#endif + + len = ptransfer_decode_service_request(service_request, service_len, &data); // Same decode for ack as for service request! + if (len < 0) + { +#if PRINT_ENABLED + printf("cpta: Bad Encoding!\n"); +#endif + } + + ProcessPTA(&data); // See what to do with the response + +return; +} + +#if 0 +void PTErrorHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Error: %s: %s\r\n", + bactext_error_class_name((int) error_class), + bactext_error_code_name((int) error_code)); + Error_Detected = true; +} +#endif + diff --git a/bacnet-stack/demo/handler/s_ptransfer.c b/bacnet-stack/demo/handler/s_ptransfer.c new file mode 100644 index 00000000..50d99152 --- /dev/null +++ b/bacnet-stack/demo/handler/s_ptransfer.c @@ -0,0 +1,140 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "address.h" +#include "tsm.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "dcc.h" +#include "ptransfer.h" +/* some demo stuff needed */ +#include "handlers.h" +#include "txbuf.h" +#include "mydata.h" + + +uint8_t Send_Private_Transfer_Request( + uint32_t device_id, + uint16_t vendor_id, + uint32_t service_number, + char block_number, + DATABLOCK *block + ) + +{ /* NULL=optional */ + BACNET_ADDRESS dest; + BACNET_ADDRESS my_address; + unsigned max_apdu = 0; + uint8_t invoke_id = 0; + bool status = false; + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + static uint8_t pt_req_buffer[300]; // Somewhere to build the request packet + BACNET_PRIVATE_TRANSFER_DATA pt_block; + BACNET_CHARACTER_STRING bsTemp; + + /* if we are forbidden to send, don't send! */ + if (!dcc_communication_enabled()) + return 0; + + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + /* is there a tsm available? */ + if (status) + invoke_id = tsm_next_free_invokeID(); + if (invoke_id) { + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address, + &npdu_data); + /* encode the APDU portion of the packet */ + + pt_block.vendorID = vendor_id; + pt_block.serviceNumber = service_number; + if(service_number == MY_SVC_READ) + { + len += encode_application_unsigned(&pt_req_buffer[len], block_number); // The block number we want to retrieve + } + else + { + len += encode_application_unsigned(&pt_req_buffer[len], block_number); // The block number + len += encode_application_unsigned(&pt_req_buffer[len], block->cMyByte1); // And Then the block contents + len += encode_application_unsigned(&pt_req_buffer[len], block->cMyByte2); + len += encode_application_real(&pt_req_buffer[len], block->fMyReal); + characterstring_init_ansi(&bsTemp, block->sMyString); + len += encode_application_character_string(&pt_req_buffer[len], &bsTemp); + } + + pt_block.serviceParameters = &pt_req_buffer[0]; + pt_block.serviceParametersLen = len; + len = ptransfer_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id, &pt_block); + pdu_len += len; + + /* will it fit in the sender? + note: if there is a bottleneck router in between + us and the destination, we won't know unless + we have a way to check for that and update the + max_apdu in the address binding table. */ + + if ((unsigned) pdu_len < max_apdu) { + tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest, + &npdu_data, &Handler_Transmit_Buffer[0], pdu_len); + bytes_sent = + datalink_send_pdu(&dest, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, + "Failed to Send Private Transfer Request (%s)!\n", + strerror(errno)); +#endif + } else { + tsm_free_invoke_id(invoke_id); + invoke_id = 0; +#if PRINT_ENABLED + fprintf(stderr, + "Failed to Send Private Transfer Request " + "(exceeds destination maximum APDU)!\n"); +#endif + } + } + + return invoke_id; +} diff --git a/bacnet-stack/demo/ptransfer/main.c b/bacnet-stack/demo/ptransfer/main.c new file mode 100644 index 00000000..bb9381c6 --- /dev/null +++ b/bacnet-stack/demo/ptransfer/main.c @@ -0,0 +1,371 @@ +/************************************************************************* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include +#include /* for time */ +#include +#define PRINT_ENABLED 1 + +#include "bacdef.h" +#include "config.h" +#include "bactext.h" +#include "bacerror.h" +#include "iam.h" +#include "arf.h" +#include "tsm.h" +#include "address.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "whois.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" +#include "mydata.h" + +#if defined(__BORLANDC__) +#define _kbhit kbhit +#define _stricmp stricmp +#endif + +uint8_t Send_Private_Transfer_Request( + uint32_t device_id, + uint16_t vendor_id, + uint32_t service_number, + char block_number, + DATABLOCK *block); + + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* global variables used in this file */ +static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; + +static int Target_Mode = 0; + +static BACNET_ADDRESS Target_Address; +static bool Error_Detected = false; + +static void MyErrorHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Error: %s: %s\r\n", + bactext_error_class_name((int) error_class), + bactext_error_code_name((int) error_code)); +// Error_Detected = true; +} + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + (void) server; + printf("BACnet Abort: %s\r\n", + bactext_abort_reason_name((int) abort_reason)); + Error_Detected = true; +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Reject: %s\r\n", + bactext_reject_reason_name((int) reject_reason)); + Error_Detected = true; +} + +static void Init_Service_Handlers( + void) +{ + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* handle i-am to support binding to other devices */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + + apdu_set_confirmed_handler(SERVICE_CONFIRMED_PRIVATE_TRANSFER, + handler_conf_private_trans); + /* handle the data coming back from confirmed requests */ + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property_ack); + + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_PRIVATE_TRANSFER, + handler_conf_private_trans_ack); + + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler); + apdu_set_error_handler(SERVICE_CONFIRMED_PRIVATE_TRANSFER, MyErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + unsigned max_apdu = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + uint8_t invoke_id = 0; + bool found = false; + DATABLOCK NewData; + int iCount = 0; + int iType = 0; + int iKey; + static int iLimit[3] = {7, 11, 7}; + + if (((argc != 2) && (argc != 3)) || ((argc >= 2) && (strcmp(argv[1], "--help") == 0))) { + printf("%s\n",argv[0]); + printf("Usage: %s server local-device-instance\r\n or\r\n" + " %s remote-device-instance\r\n" + , filename_remove_path(argv[0]), filename_remove_path(argv[0])); + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + printf("\r\nServer mode:\r\n\r\n" + "local-device-instance determins the device id of the application\r\n" + "when running as the server end of a test set up.\r\n\r\n" + "Non server:\r\n\r\n" + "remote-device-instance indicates the device id of the server\r\n" + "instance of the application.\r\n" + "The non server application will write a series of blocks to the\r\n" + "server and then retrieve them for display locally\r\n" + "First it writes all 8 blocks plus a 9th which should trigger\r\n" + "an out of range error response. Then it reads all the blocks\r\n" + "including the ninth and finally it repeats the read operation\r\n" + "with some deliberate errors to trigger a nack response\r\n"); + } + return 0; + } + /* decode the command line parameters */ + if(_stricmp(argv[1], "server") == 0) + Target_Mode = 1; + else + Target_Mode = 0; + + Target_Device_Object_Instance = strtol(argv[1 + Target_Mode], NULL, 0); + + if (Target_Device_Object_Instance > BACNET_MAX_INSTANCE) { + fprintf(stderr, "device-instance=%u - it must be less than %u\r\n", + Target_Device_Object_Instance, BACNET_MAX_INSTANCE); + return 1; + } + + /* setup my info */ + if(Target_Mode) + Device_Set_Object_Instance_Number(Target_Device_Object_Instance); + else + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + + address_init(); + Init_Service_Handlers(); + dlenv_init(); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); + + if(Target_Mode) { + printf("Entering server mode. press q to quit program\r\n\r\n"); + + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + if(current_seconds != last_seconds) { + } + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + /* at least one second has passed */ + if (current_seconds != last_seconds) { + putchar('.'); // Just to show that time is passing... + last_seconds = current_seconds; + tsm_timer_milliseconds(((current_seconds - last_seconds) * 1000)); + } + + if(_kbhit()) { + iKey = toupper(_getch()); + if(iKey == 'Q') { + printf("\r\nExiting program now\r\n"); + exit(0); + } + } + } + } + else { + + /* try to bind with the device */ + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + if (!found) { + Send_WhoIs(Target_Device_Object_Instance, + Target_Device_Object_Instance); + } + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + /* at least one second has passed */ + if (current_seconds != last_seconds) + tsm_timer_milliseconds(((current_seconds - last_seconds) * 1000)); + if (Error_Detected) + break; + /* wait until the device is bound, or timeout and quit */ + if(!found) + found = address_bind_request(Target_Device_Object_Instance, &max_apdu, &Target_Address); + if (found) + { + if (invoke_id == 0) { // Safe to send a new request + switch(iType) { + case 0: // Write blocks to server + NewData.cMyByte1 = iCount; + NewData.cMyByte2 = 255 - iCount; + NewData.fMyReal = (float)iCount; + strcpy(NewData.sMyString, "Test Data - [x]"); + NewData.sMyString[13] = 'a' + iCount; + printf("Sending block %d\n", iCount); + invoke_id = Send_Private_Transfer_Request(Target_Device_Object_Instance, BACNET_VENDOR_ID, 1, iCount, &NewData); + break; + + case 1: // Read blocks from server + printf("Requesting block %d\n", iCount); + invoke_id = Send_Private_Transfer_Request(Target_Device_Object_Instance, BACNET_VENDOR_ID, 0, iCount, &NewData); + break; + + case 2: // Generate some error responses + switch(iCount) { + case 0: // Bad service number i.e. 2 + case 2: + case 4: + case 6: + case 8: + printf("Requesting block %d with bad service number\n", iCount); + invoke_id = Send_Private_Transfer_Request(Target_Device_Object_Instance, BACNET_VENDOR_ID, 2, iCount, &NewData); + break; + + case 1: // Bad vendor ID number + case 3: + case 5: + case 7: + printf("Requesting block %d with invalid Vendor ID\n", iCount); + invoke_id = Send_Private_Transfer_Request(Target_Device_Object_Instance, BACNET_VENDOR_ID + 1, 0, iCount, &NewData); + break; + } + + break; + } + } + else if (tsm_invoke_id_free(invoke_id)) { + if(iCount != MY_MAX_BLOCK) { + iCount++; + invoke_id = 0; + } + else { + iType++; + iCount = 0; + invoke_id = 0; + + if(iType > 2) + break; + } + } + else if (tsm_invoke_id_failed(invoke_id)) + { + fprintf(stderr, "\rError: TSM Timeout!\r\n"); + tsm_free_invoke_id(invoke_id); + Error_Detected = true; + /* try again or abort? */ + break; + } + } + else + { + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) { + printf("\rError: APDU Timeout!\r\n"); + Error_Detected = true; + break; + } + } + /* keep track of time for next check */ + last_seconds = current_seconds; + } + } + + if (Error_Detected) + return 1; + return 0; +} diff --git a/bacnet-stack/demo/ptransfer/makefile.b32 b/bacnet-stack/demo/ptransfer/makefile.b32 new file mode 100644 index 00000000..eaa8c08d --- /dev/null +++ b/bacnet-stack/demo/ptransfer/makefile.b32 @@ -0,0 +1,134 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = ptransfer +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| + $(CFLAGS) + -c + -y #include line numbers in OBJ's + -v #include debug info + -w+ #turn on all warnings + -Od #disable all optimizations + #-a4 #32 bit data alignment + #-M # generate link map + #-ls # linker options + #-WM- #not multithread + -WM #multithread + -w-aus # ignore warning assigned a value that is never used + -w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/bacnet-stack/demo/ptransfer/ptransfer.sln b/bacnet-stack/demo/ptransfer/ptransfer.sln new file mode 100644 index 00000000..aba19789 --- /dev/null +++ b/bacnet-stack/demo/ptransfer/ptransfer.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rdproperty", "rdproperty.vcproj", "{7AC60281-278A-4060-B900-A1343E438701}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7AC60281-278A-4060-B900-A1343E438701}.Debug|Win32.ActiveCfg = Debug|Win32 + {7AC60281-278A-4060-B900-A1343E438701}.Debug|Win32.Build.0 = Debug|Win32 + {7AC60281-278A-4060-B900-A1343E438701}.Release|Win32.ActiveCfg = Release|Win32 + {7AC60281-278A-4060-B900-A1343E438701}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/bacnet-stack/demo/ptransfer/rdproperty.vcproj b/bacnet-stack/demo/ptransfer/rdproperty.vcproj new file mode 100644 index 00000000..7c62a204 --- /dev/null +++ b/bacnet-stack/demo/ptransfer/rdproperty.vcproj @@ -0,0 +1,967 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bacnet-stack/doc/README.subversion b/bacnet-stack/doc/README.subversion index b1d12e73..cbab4aba 100644 --- a/bacnet-stack/doc/README.subversion +++ b/bacnet-stack/doc/README.subversion @@ -73,6 +73,8 @@ Makefile = svn:eol-style=native *.java = svn:mime-type=text/plain;svn:eol-style=native *.properties = svn:mime-type=text/plain;svn:eol-style=native *.sql = svn:mime-type=text/plain;svn:eol-style=native +*.sln = svn:eol-style=CRLF +*.vcproj = svn:eol-style=CRLF To test the properties of a file: $ svn proplist diff --git a/bacnet-stack/include/handlers.h b/bacnet-stack/include/handlers.h index 5c42d94e..deec27d8 100644 --- a/bacnet-stack/include/handlers.h +++ b/bacnet-stack/include/handlers.h @@ -200,7 +200,18 @@ extern "C" { BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data); - + void handler_conf_private_trans( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + + void handler_conf_private_trans_ack( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data); +# #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/bacnet-stack/include/mydata.h b/bacnet-stack/include/mydata.h new file mode 100644 index 00000000..1c35ea36 --- /dev/null +++ b/bacnet-stack/include/mydata.h @@ -0,0 +1,24 @@ +/* Sample data structure for confirmed private transfer + We have a simple data structure which can be written to + and read from by sending a confirmed private transfer + request with the appropriate parameters. + */ + +#define MY_MAX_STR 32 +#define MY_MAX_BLOCK 8 + +#define MY_SVC_READ 0 +#define MY_SVC_WRITE 1 + +#define MY_ERR_OK 0 +#define MY_ERR_BAD_INDEX 1 + +typedef struct MyData { + uint8_t cMyByte1; + uint8_t cMyByte2; + float fMyReal; + int8_t sMyString[MY_MAX_STR+1]; // A little extra for the nul +} DATABLOCK; + + + diff --git a/bacnet-stack/lib/makefile.b32 b/bacnet-stack/lib/makefile.b32 index b20a5820..c71c1bd0 100644 --- a/bacnet-stack/lib/makefile.b32 +++ b/bacnet-stack/lib/makefile.b32 @@ -65,6 +65,7 @@ CORE3_SRC = $(BACNET_CORE)\arf.c \ $(BACNET_CORE)\dcc.c \ $(BACNET_CORE)\iam.c \ $(BACNET_CORE)\ihave.c \ + $(BACNET_CORE)\ptransfer.c \ $(BACNET_CORE)\rd.c \ $(BACNET_CORE)\rp.c \ $(BACNET_CORE)\rpm.c \ @@ -109,6 +110,9 @@ HANDLER_SRC = \ $(BACNET_HANDLER)\s_uevent.c \ $(BACNET_HANDLER)\s_whohas.c \ $(BACNET_HANDLER)\s_whois.c \ + $(BACNET_HANDLER)\s_ptransfer.c \ + $(BACNET_HANDLER)\h_pt.c \ + $(BACNET_HANDLER)\h_pt_a.c \ $(BACNET_HANDLER)\s_wp.c OBJECT_SRC = $(BACNET_OBJECT)\device.c \ diff --git a/bacnet-stack/src/apdu.c b/bacnet-stack/src/apdu.c index 2f8b3a5f..3083d48f 100644 --- a/bacnet-stack/src/apdu.c +++ b/bacnet-stack/src/apdu.c @@ -453,16 +453,33 @@ void apdu_handler( invoke_id = apdu[1]; service_choice = apdu[2]; len = 3; - len += - decode_tag_number_and_value(&apdu[len], &tag_number, - &len_value); - /* FIXME: we could validate that the tag is enumerated... */ - len += decode_enumerated(&apdu[len], len_value, &error_class); - len += - decode_tag_number_and_value(&apdu[len], &tag_number, - &len_value); - /* FIXME: we could validate that the tag is enumerated... */ - len += decode_enumerated(&apdu[len], len_value, &error_code); + + /* FIXME: Currently special case for C_P_T but there are others which may + need consideration such as ChangeList-Error, CreateObject-Error, + WritePropertyMultiple-Error and VTClose_Error but they may be left as + is for now until support for these services is added */ + + if(service_choice = SERVICE_CONFIRMED_PRIVATE_TRANSFER) { /* skip over opening tag 0 */ + if (decode_is_opening_tag_number(&apdu[len], 0)) { + len++; /* a tag number of 0 is not extended so only one octet */ + } + } + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + /* FIXME: we could validate that the tag is enumerated... */ + len += decode_enumerated(&apdu[len], len_value, &error_class); + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + /* FIXME: we could validate that the tag is enumerated... */ + len += decode_enumerated(&apdu[len], len_value, &error_code); + + if(service_choice = SERVICE_CONFIRMED_PRIVATE_TRANSFER) { /* skip over closing tag 0 */ + if (decode_is_closing_tag_number(&apdu[len], 0)) { + len++; /* a tag number of 0 is not extended so only one octet */ + } + } if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) { if (Error_Function[service_choice]) Error_Function[service_choice] (src, invoke_id, diff --git a/bacnet-stack/src/bacstr.c b/bacnet-stack/src/bacstr.c index e3909da8..76a78a83 100644 --- a/bacnet-stack/src/bacstr.c +++ b/bacnet-stack/src/bacstr.c @@ -35,6 +35,7 @@ #include #include #include /* for strlen */ +#include "config.h" #include "bacstr.h" #include "bits.h"