From 04c94698e5d5dd35db7e3ee7bbd6a327fd6dedda Mon Sep 17 00:00:00 2001 From: skarg Date: Wed, 1 Nov 2017 21:33:48 +0000 Subject: [PATCH] Added WritePropertyMultiple demo application. --- bacnet-stack/Makefile | 3 + bacnet-stack/demo/Makefile | 4 +- bacnet-stack/demo/handler/s_wpm.c | 39 +- bacnet-stack/demo/writepropm/Makefile | 39 ++ bacnet-stack/demo/writepropm/main.c | 522 ++++++++++++++++++++++++++ bacnet-stack/include/client.h | 4 +- 6 files changed, 591 insertions(+), 20 deletions(-) create mode 100644 bacnet-stack/demo/writepropm/Makefile create mode 100644 bacnet-stack/demo/writepropm/main.c diff --git a/bacnet-stack/Makefile b/bacnet-stack/Makefile index 94a500ff..221049b6 100644 --- a/bacnet-stack/Makefile +++ b/bacnet-stack/Makefile @@ -87,6 +87,9 @@ iam: uevent: $(MAKE) -B -C demo uevent +writepropm: + $(MAKE) -s -B -C demo writepropm + abort: $(MAKE) -B -C demo abort diff --git a/bacnet-stack/demo/Makefile b/bacnet-stack/demo/Makefile index 54f15875..9fff0730 100644 --- a/bacnet-stack/demo/Makefile +++ b/bacnet-stack/demo/Makefile @@ -57,7 +57,7 @@ LFLAGS := -Wl,$(BACNET_LIB),$(SYSTEM_LIB) SUBDIRS = readprop writeprop readfile writefile reinit server dcc \ whohas whois iam ucov scov timesync epics readpropm readrange \ - uptransfer getevent uevent abort error + writepropm uptransfer getevent uevent abort error ifeq (${BACDL_DEFINE},-DBACDL_BIP=1) SUBDIRS += whoisrouter iamrouter initrouter readbdt @@ -112,3 +112,5 @@ router: router-ipv6: $(MAKE) -b -C router-ipv6 +writepropm: + $(MAKE) -b -C writepropm diff --git a/bacnet-stack/demo/handler/s_wpm.c b/bacnet-stack/demo/handler/s_wpm.c index 867d5a0b..c5cc467a 100644 --- a/bacnet-stack/demo/handler/s_wpm.c +++ b/bacnet-stack/demo/handler/s_wpm.c @@ -32,7 +32,6 @@ #include #include #include "config.h" -#include "txbuf.h" #include "bacdef.h" #include "bacdcode.h" #include "address.h" @@ -51,12 +50,16 @@ /** @file s_wpm.c Send Write Property Multiple request. */ /** Sends a Write Property Multiple request. + * @param pdu [out] Buffer to build the outgoing message into + * @param max_pdu [in] Length of the pdu buffer. * @param device_id [in] ID of the destination device * @param write_access_data [in] Ptr to structure with the linked list of * objects and properties to be written. * @return invoke id of outgoing message, or 0 if device is not bound or no tsm available */ -uint8_t Send_Write_Property_Multiple_Request_Data( +uint8_t Send_Write_Property_Multiple_Request( + uint8_t * pdu, + size_t max_pdu, uint32_t device_id, BACNET_WRITE_ACCESS_DATA * write_access_data) { @@ -69,28 +72,26 @@ uint8_t Send_Write_Property_Multiple_Request_Data( int pdu_len = 0; int bytes_sent = 0; BACNET_NPDU_DATA npdu_data; - - /* if we are forbidden to send, don't send! */ - if (!dcc_communication_enabled()) - return 0; + /* 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) + 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); - + pdu_len = npdu_encode_pdu(&pdu[0], &dest, &my_address,&npdu_data); + /* encode the APDU portion of the packet */ len = - wpm_encode_apdu(&Handler_Transmit_Buffer[pdu_len], max_apdu, + wpm_encode_apdu(&pdu[pdu_len], max_pdu - pdu_len, invoke_id, write_access_data); - pdu_len += len; /* will it fit in the sender? @@ -100,21 +101,23 @@ uint8_t Send_Write_Property_Multiple_Request_Data( 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], (uint16_t) pdu_len); + &npdu_data, &pdu[0], (uint16_t) pdu_len); bytes_sent = datalink_send_pdu(&dest, &npdu_data, - &Handler_Transmit_Buffer[0], pdu_len); + &pdu[0], pdu_len); #if PRINT_ENABLED - if (bytes_sent <= 0) - fprintf(stderr, "Failed to Send WriteProperty Request (%s)!\n", + if (bytes_sent <= 0) { + fprintf(stderr, + "Failed to Send WritePropertyMultiple Request (%s)!\n", strerror(errno)); + } #endif } else { tsm_free_invoke_id(invoke_id); invoke_id = 0; #if PRINT_ENABLED fprintf(stderr, - "Failed to Send WriteProperty Request " + "Failed to Send WritePropertyMultiple Request " "(exceeds destination maximum APDU)!\n"); #endif } diff --git a/bacnet-stack/demo/writepropm/Makefile b/bacnet-stack/demo/writepropm/Makefile new file mode 100644 index 00000000..064dc687 --- /dev/null +++ b/bacnet-stack/demo/writepropm/Makefile @@ -0,0 +1,39 @@ +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +# Executable file name +TARGET = bacwpm + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend diff --git a/bacnet-stack/demo/writepropm/main.c b/bacnet-stack/demo/writepropm/main.c new file mode 100644 index 00000000..e7e3a9be --- /dev/null +++ b/bacnet-stack/demo/writepropm/main.c @@ -0,0 +1,522 @@ +/************************************************************************* +* Copyright (C) 2017 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 +#include /* for time */ + +#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" +#include "version.h" +/* some demo stuff needed */ +#include "rpm.h" +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" + +/* 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 BACNET_WRITE_ACCESS_DATA *Write_Access_Data; +/* needed to filter incoming messages */ +static uint8_t Request_Invoke_ID = 0; +static BACNET_ADDRESS Target_Address; +/* needed for return value of main application */ +static bool Error_Detected = false; +/* Used for verbose */ +static bool Verbose = false; + +static void MyErrorHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Error: %s: %s\n", + bactext_error_class_name((int) error_class), + bactext_error_code_name((int) error_code)); + Error_Detected = true; + /* FIXME: WPM error has extra data for first failed write. */ + } +} + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + (void) server; + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Abort: %s\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 */ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Reject: %s\n", + bactext_reject_reason_name((int) reject_reason)); + Error_Detected = true; + } +} + +void MyWritePropertyMultipleSimpleAckHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("\nWriteProperty Acknowledged!\n"); + } +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* 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); + /* handle the data coming back from confirmed requests */ + apdu_set_confirmed_simple_ack_handler(SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE, + MyWritePropertyMultipleSimpleAckHandler); + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +void cleanup( + void) +{ + BACNET_WRITE_ACCESS_DATA *wpm_object; + BACNET_WRITE_ACCESS_DATA *old_wpm_object; + BACNET_PROPERTY_VALUE *wpm_property; + BACNET_PROPERTY_VALUE *old_wpm_property; + + wpm_object = Write_Access_Data; + old_wpm_object = wpm_object; + while (wpm_object) { + wpm_property = wpm_object->listOfProperties; + while (wpm_property) { + old_wpm_property = wpm_property; + wpm_property = wpm_property->next; + free(old_wpm_property); + } + old_wpm_object = wpm_object; + wpm_object = wpm_object->next; + free(old_wpm_object); + } +} + +static void print_usage(char *filename) +{ + printf("Usage: %s device-instance object-type object-instance " + "property[index] priority tag value [property[index] priority tag value]\n", + filename); + printf(" [--version][--help]\n"); +} + +static void print_help(char *filename) +{ + printf("Write one or more properties to one or more objects\n" + "in a BACnet device.\n" + "device-instance:\n" + "BACnet Device Object Instance number that you are\n" + "trying to communicate to. This number will be used\n" + "to try and bind with the device using Who-Is and\n" + "I-Am services. For example, if you were writing\n" + "Device Object 123, the device-instance would be 123.\n" + "\nobject-type:\n" + "The object type is the integer value of the enumeration\n" + "BACNET_OBJECT_TYPE in bacenum.h. It is the object\n" + "that you are writing. For example if you were\n" + "writing Analog Output 2, the object-type would be 1.\n" + "\nobject-instance:\n" + "This is the object instance number of the object that\n" + "you are writing. For example, if you were writing\n" + "Analog Output 2, the object-instance would be 2.\n" + "\nproperty:\n" + "The property is an integer value of the enumeration\n" + "BACNET_PROPERTY_ID in bacenum.h. It is the property\n" + "you are writing. For example, if you were writing the\n" + "Present Value property, use 85 as the property.\n" + "priority:\n" + "This parameter is used for setting the priority of the\n" + "write. If Priority 0 is given, no priority is sent. The BACnet \n" + "standard states that the value is written at the lowest \n" + "priority (16) if the object property supports priorities\n" + "when no priority is sent.\n" "\n" + "index\n" + "This integer parameter is the index number of an array.\n" + "If the property is an array, individual elements can be written\n" + "to if supported. If this parameter is -1, the index is ignored.\n" + "\n" "tag:\n" + "Tag is the integer value of the enumeration BACNET_APPLICATION_TAG \n" + "in bacenum.h. It is the data type of the value that you are\n" + "writing. For example, if you were writing a REAL value, you would \n" + "use a tag of 4.\n" + "Context tags are created using two tags in a row. The context tag\n" + "is preceded by a C. Ctag tag. C2 4 creates a context 2 tagged REAL.\n" + "\n" "value:\n" + "The value is an ASCII representation of some type of data that you\n" + "are writing. It is encoded using the tag information provided. For\n" + "example, if you were writing a REAL value of 100.0, you would use \n" + "100.0 as the value.\n" "\n" + "Here is a brief overview of BACnet property and tags:\n" + "Certain properties are expected to be written with certain \n" + "application tags, so you probably need to know which ones to use\n" + "with each property of each object. It is almost safe to say that\n" + "given a property and an object and a table, the tag could be looked\n" + "up automatically. There may be a few exceptions to this, such as\n" + "the Any property type in the schedule object and the Present Value\n" + "accepting REAL, BOOLEAN, NULL, etc. Perhaps it would be simpler for\n" + "the demo to use this kind of table - but I also wanted to be able\n" + "to do negative testing by passing the wrong tag and have the server\n" + "return a reject message.\n\n" ); + printf("Example:\n" + "If you want send a value of 100 to the Present-Value in\n" + "Analog Output 44 and 45 of Device 123 at priority 16,\n" + "send the following command:\n" + "%s 123 1 44 85 16 4 100 1 45 85 16 4 100\n", + filename); +} + +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; + int args_remaining = 0, tag_value_arg = 0, arg_sets = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + bool found = false; + uint8_t buffer[MAX_PDU] = { + 0 + }; + BACNET_WRITE_ACCESS_DATA *wpm_object; + BACNET_PROPERTY_VALUE *wpm_property; + char *value_string = NULL; + bool status = false; + BACNET_APPLICATION_TAG property_tag; + uint8_t context_tag = 0; + unsigned property_id = 0; + unsigned property_array_index = 0; + int scan_count = 0; + int argi = 0; + char *filename = NULL; + + filename = filename_remove_path(argv[0]); + for (argi = 1; argi < argc; argi++) { + if (strcmp(argv[argi], "--help") == 0) { + print_usage(filename); + print_help(filename); + return 0; + } + if (strcmp(argv[argi], "--version") == 0) { + printf("%s %s\n", filename, BACNET_VERSION_TEXT); + printf("Copyright (C) 2017 by Steve Karg and others.\n" + "This is free software; see the source for copying conditions.\n" + "There is NO warranty; not even for MERCHANTABILITY or\n" + "FITNESS FOR A PARTICULAR PURPOSE.\n"); + return 0; + } + } + if (argc < 9) { + print_usage(filename); + return 0; + } + /* decode the command line parameters */ + Target_Device_Object_Instance = strtol(argv[1], NULL, 0); + if (Target_Device_Object_Instance >= BACNET_MAX_INSTANCE) { + fprintf(stderr, "device-instance=%u - it must be less than %u\n", + Target_Device_Object_Instance, BACNET_MAX_INSTANCE); + return 1; + } + atexit(cleanup); + Write_Access_Data = calloc(1, sizeof(BACNET_WRITE_ACCESS_DATA)); + wpm_object = Write_Access_Data; + args_remaining = (argc - 2); + arg_sets = 0; + while (wpm_object) { + tag_value_arg = 2 + (arg_sets * 6); + wpm_object->object_type = strtol(argv[tag_value_arg], NULL, 0); + tag_value_arg++; + args_remaining--; + if (Verbose) { + printf("object-type=%u\n", wpm_object->object_type); + } + if (args_remaining <= 0) { + fprintf(stderr, "Error: not enough object property quadruples.\n"); + return 1; + } + if (wpm_object->object_type >= MAX_BACNET_OBJECT_TYPE) { + fprintf(stderr, "object-type=%u - it must be less than %u\n", + wpm_object->object_type, MAX_BACNET_OBJECT_TYPE); + return 1; + } + wpm_object->object_instance = strtol(argv[tag_value_arg], NULL, 0); + tag_value_arg++; + args_remaining--; + if (Verbose) { + printf("object-instance=%u\n", wpm_object->object_instance); + } + if (args_remaining <= 0) { + fprintf(stderr, "Error: not enough object property quadruples.\n"); + return 1; + } + if (wpm_object->object_instance > BACNET_MAX_INSTANCE) { + fprintf(stderr, "object-instance=%u - it must be less than %u\n", + wpm_object->object_instance, BACNET_MAX_INSTANCE + 1); + return 1; + } + do { + wpm_property = calloc(1, sizeof(BACNET_PROPERTY_VALUE)); + wpm_object->listOfProperties = wpm_property; + if (wpm_property) { + /* Property[index] */ + scan_count = + sscanf(argv[tag_value_arg], "%u[%u]", &property_id, + &property_array_index); + tag_value_arg++; + args_remaining--; + if (scan_count > 0) { + wpm_property->propertyIdentifier = property_id; + if (Verbose) { + printf("property-identifier=%u, array-index=%u\n", + property_id, property_array_index); + } + if (wpm_property->propertyIdentifier > MAX_BACNET_PROPERTY_ID) { + fprintf(stderr, + "property=%u - it must be less than %u\n", + wpm_property->propertyIdentifier, + MAX_BACNET_PROPERTY_ID + 1); + return 1; + } + } + if (scan_count > 1) { + wpm_property->propertyArrayIndex = property_array_index; + } else { + wpm_property->propertyArrayIndex = BACNET_ARRAY_ALL; + } + if (args_remaining <= 0) { + fprintf(stderr, + "Error: missing priority and tag value pair.\n"); + return 1; + } + /* Priority */ + wpm_property->priority = (uint8_t) + strtol(argv[tag_value_arg], NULL, 0); + tag_value_arg++; + args_remaining--; + if (Verbose) { + printf("priority=%u\n", wpm_property->priority); + } + if (args_remaining <= 0) { + fprintf(stderr, + "Error: missing tag value pair.\n"); + return 1; + } + /* Tag + Value */ + /* special case for context tagged values */ + if (toupper(argv[tag_value_arg][0]) == 'C') { + context_tag = (uint8_t) strtol(&argv[tag_value_arg][1], NULL, 0); + tag_value_arg++; + args_remaining--; + wpm_property->value.context_tag = context_tag; + wpm_property->value.context_specific = true; + } else { + wpm_property->value.context_specific = false; + } + property_tag = strtol(argv[tag_value_arg], NULL, 0); + tag_value_arg++; + args_remaining--; + if (args_remaining <= 0) { + fprintf(stderr, "Error: missing value for tag-value pair\n"); + return 1; + } + value_string = argv[tag_value_arg]; + tag_value_arg++; + args_remaining--; + if (Verbose) { + printf("tag=%u value=%s\n", property_tag, value_string); + } + if (property_tag >= MAX_BACNET_APPLICATION_TAG) { + fprintf(stderr, "Error: tag=%u - it must be less than %u\n", + property_tag, MAX_BACNET_APPLICATION_TAG); + return 1; + } + status = + bacapp_parse_application_data(property_tag, value_string, + &wpm_property->value); + if (!status) { + /* FIXME: show the expected entry format for the tag */ + fprintf(stderr, "Error: unable to parse the tag value\n"); + return 1; + } + wpm_property->value.next = NULL; + /* we only support a single property value */ + break; + } + } while (wpm_property); + if (args_remaining) { + arg_sets++; + wpm_object->next = calloc(1, sizeof(BACNET_WRITE_ACCESS_DATA)); + wpm_object = wpm_object->next; + } else { + break; + } + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + address_init(); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); + /* try to bind with the device */ + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + if (found) { + if (Verbose) { + printf("Found Device %u in address_cache.\n", + Target_Device_Object_Instance); + } + } else { + Send_WhoIs(Target_Device_Object_Instance, + Target_Device_Object_Instance); + } + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + + /* 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 (Request_Invoke_ID == 0) { + if (Verbose) { + printf("Sending WritePropertyMultiple to Device %u.\n", + Target_Device_Object_Instance); + } + Request_Invoke_ID = + Send_Write_Property_Multiple_Request(&buffer[0], + sizeof(buffer), Target_Device_Object_Instance, + Write_Access_Data); + } else if (tsm_invoke_id_free(Request_Invoke_ID)) { + break; + } else if (tsm_invoke_id_failed(Request_Invoke_ID)) { + fprintf(stderr, "\rError: TSM Timeout!\n"); + tsm_free_invoke_id(Request_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!\n"); + Error_Detected = true; + break; + } + } + /* 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); + } + + /* keep track of time for next check */ + last_seconds = current_seconds; + } + if (Error_Detected) { + return 1; + } + + return 0; +} diff --git a/bacnet-stack/include/client.h b/bacnet-stack/include/client.h index 3e9f95be..d11734cc 100644 --- a/bacnet-stack/include/client.h +++ b/bacnet-stack/include/client.h @@ -164,7 +164,9 @@ extern "C" { int application_data_len, uint8_t priority, uint32_t array_index); - uint8_t Send_Write_Property_Multiple_Request_Data( + uint8_t Send_Write_Property_Multiple_Request( + uint8_t * pdu, + size_t max_pdu, uint32_t device_id, BACNET_WRITE_ACCESS_DATA * write_access_data);