From e9142bfc54a8e84946c71ebc91361f22434f3df5 Mon Sep 17 00:00:00 2001 From: skarg Date: Wed, 14 Sep 2011 20:47:00 +0000 Subject: [PATCH] Added Added SubscribeCOV demo application (bacscov) for testing (and fixed subversion mess up that I did by copying a directory). --- bacnet-stack/demo/scov/Makefile | 43 +++ bacnet-stack/demo/scov/main.c | 339 ++++++++++++++++++++++ bacnet-stack/demo/scov/makefile.b32 | 142 +++++++++ bacnet-stack/demo/uptransfer/Makefile | 2 +- bacnet-stack/demo/uptransfer/main.c | 312 ++++++++++++-------- bacnet-stack/demo/uptransfer/makefile.b32 | 2 +- 6 files changed, 724 insertions(+), 116 deletions(-) create mode 100644 bacnet-stack/demo/scov/Makefile create mode 100644 bacnet-stack/demo/scov/main.c create mode 100644 bacnet-stack/demo/scov/makefile.b32 diff --git a/bacnet-stack/demo/scov/Makefile b/bacnet-stack/demo/scov/Makefile new file mode 100644 index 00000000..4dcaafc4 --- /dev/null +++ b/bacnet-stack/demo/scov/Makefile @@ -0,0 +1,43 @@ +#Makefile to build BACnet Application using GCC compiler + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc +# AR = ar +# MAKE = make +# SIZE = size +# +# Assumes rm and cp are available + +# Executable file name +TARGET = bacscov + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.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 -rf core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} + +include: .depend diff --git a/bacnet-stack/demo/scov/main.c b/bacnet-stack/demo/scov/main.c new file mode 100644 index 00000000..96c663d8 --- /dev/null +++ b/bacnet-stack/demo/scov/main.c @@ -0,0 +1,339 @@ +/************************************************************************* +* 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 /* for toupper */ + +#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" + +/* All included BACnet objects */ +static object_functions_t Object_Table[] = { + {DEVICE_OBJ_FUNCTIONS}, + {MAX_BACNET_OBJECT_TYPE, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL} +}; + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* converted command line arguments */ +static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; +/* Process identifier for matching replies */ +static uint32_t Target_Device_Process_Identifier = 0; +/* buffer for service parameters */ +static uint8_t Service_Parameters[MAX_APDU]; +/* the invoke id is needed to filter incoming messages */ +static uint8_t Request_Invoke_ID = 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) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_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) +{ + (void) server; + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + 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) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Reject: %s\r\n", + bactext_reject_reason_name((int) reject_reason)); + Error_Detected = true; + } +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(&Object_Table[0]); + /* 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 COV subscriptions */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_COV_NOTIFICATION, + handler_ccov_notification); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_COV_NOTIFICATION, + handler_ucov_notification); + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, 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; + time_t delta_seconds = 0; + bool found = false; + char *filename = NULL; + BACNET_SUBSCRIBE_COV_DATA cov_data = {0}; + bool print_usage_terse = false; + bool print_usage_verbose = false; + + if (argc < 5) { + print_usage_terse = true; + } + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + print_usage_terse = true; + print_usage_verbose = true; + } + if (print_usage_terse) { + filename = filename_remove_path(argv[0]); + /* note: priority 16 and 0 should produce the same end results... */ + printf("Usage: %s device-id object-type object-instance " + "process-id lifetime [issueConfirmedNotifications]\r\n", + filename); + if (!print_usage_verbose) { + return 0; + } + } + if (print_usage_verbose) { + printf("\r\n" + "device-id:\r\n" + "The subscriber BACnet Device Object Instance number.\r\n" + "\r\n" + "object-type:\r\n" + "The monitored object type is the integer value of the\r\n" + "enumeration BACNET_OBJECT_TYPE in bacenum.h. For example,\r\n" + "if you were monitoring Analog Output 2, the object-type\r\n" + "would be 1.\r\n" + "\r\n" + "object-instance:\r\n" + "The monitored object instance number.\r\n" + "\r\n" + "process-id:\r\n" + "Process Identifier for this COV subscription.\r\n" + "\r\n" + "lifetime:\r\n" + "Optional subscription lifetime is conveyed in seconds.\r\n" + "\r\n" + "issueConfirmedNotifications:\r\n" + "Optional flag to subscribe using Confirmed notifications.\r\n" + "Use the word \'confirmed\'.\r\n" + "\r\n" + "If both the \'issueConfirmedNotifications\' and\r\n" + "\'lifetime\' parameters are absent, then this shall\r\n" + "indicate a cancellation request.\r\n" + "\r\n" + "Example:\r\n" + "If you want subscribe to Device 123 Analog Input 9 object\r\n" + "using confirmed COV notifications for 5 minutes,\r\n" + "you could send the following command:\r\n" + "%s 123 0 9 1 600 confirmed\r\n" + "To send the same COV subscription request for unconfirmed\r\n" + "notifications, you could send the following command:\r\n" + "%s 123 0 9 1 600\r\n", + filename, filename); + return 0; + } + /* decode the command line parameters */ + Target_Device_Object_Instance = strtol(argv[1], NULL, 0); + cov_data.monitoredObjectIdentifier.type = strtol(argv[2], NULL, 0); + cov_data.monitoredObjectIdentifier.instance = strtol(argv[3], NULL, 0); + cov_data.subscriberProcessIdentifier = + Target_Device_Process_Identifier = strtol(argv[4], NULL, 0); + if (argc > 5) { + cov_data.lifetime = strtol(argv[5], NULL, 0); + cov_data.issueConfirmedNotifications = false; + } else { + cov_data.cancellationRequest = true; + } + if (argc > 6) { + if (strcmp(argv[6],"confirmed") == 0) { + cov_data.issueConfirmedNotifications = true; + } + } + 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; + } + if (cov_data.monitoredObjectIdentifier.type >= MAX_BACNET_OBJECT_TYPE) { + fprintf(stderr, "object-type=%u - it must be less than %u\r\n", + cov_data.monitoredObjectIdentifier.type, MAX_BACNET_OBJECT_TYPE); + return 1; + } + if (cov_data.monitoredObjectIdentifier.instance > BACNET_MAX_INSTANCE) { + fprintf(stderr, "object-instance=%u - it must be less than %u\r\n", + cov_data.monitoredObjectIdentifier.instance, + BACNET_MAX_INSTANCE + 1); + return 1; + } + /* setup my info */ + 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 (timeout_seconds < cov_data.lifetime) { + timeout_seconds = cov_data.lifetime; + } + /* 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); + /* at least one second has passed */ + if (current_seconds != last_seconds) { + /* increment timer - exit if timed out */ + delta_seconds = current_seconds - last_seconds; + elapsed_seconds += delta_seconds; + tsm_timer_milliseconds((delta_seconds * 1000)); + /* keep track of time for next check */ + last_seconds = current_seconds; + } + 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) { + Request_Invoke_ID = Send_COV_Subscribe( + Target_Device_Object_Instance, + &cov_data); + printf("Sent SubscribeCOV request. Waiting %u seconds.\r\n", + (unsigned)(timeout_seconds - elapsed_seconds)); + } else if (tsm_invoke_id_free(Request_Invoke_ID)) { + /* do nothing - wait for lifetime value to expire */ + } else if (tsm_invoke_id_failed(Request_Invoke_ID)) { + fprintf(stderr, "\rError: TSM Timeout!\r\n"); + tsm_free_invoke_id(Request_Invoke_ID); + Error_Detected = true; + /* try again or abort? */ + break; + } + } else { + /* exit if timed out */ + if (elapsed_seconds > timeout_seconds) { + Error_Detected = true; + printf("\rError: APDU Timeout!\r\n"); + 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); + } + if (Error_Detected) { + break; + } + /* COV - so just wait until lifetime value expires */ + if (elapsed_seconds > timeout_seconds) { + break; + } + } + if (Error_Detected) + return 1; + return 0; +} diff --git a/bacnet-stack/demo/scov/makefile.b32 b/bacnet-stack/demo/scov/makefile.b32 new file mode 100644 index 00000000..8e42bdb7 --- /dev/null +++ b/bacnet-stack/demo/scov/makefile.b32 @@ -0,0 +1,142 @@ +# +# 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 = bacscov +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 +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BACNET_OBJECT) \ + -I$(BACNET_HANDLER) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 + +# by default bacupt can handle 64 tag/value pairs +BUILD_DEFINE = -DMAX_PROPERTY_VALUES=64 + +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) $(BUILD_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/uptransfer/Makefile b/bacnet-stack/demo/uptransfer/Makefile index 4dcaafc4..eb43cc87 100644 --- a/bacnet-stack/demo/uptransfer/Makefile +++ b/bacnet-stack/demo/uptransfer/Makefile @@ -10,7 +10,7 @@ # Assumes rm and cp are available # Executable file name -TARGET = bacscov +TARGET = bacupt TARGET_BIN = ${TARGET}$(TARGET_EXT) diff --git a/bacnet-stack/demo/uptransfer/main.c b/bacnet-stack/demo/uptransfer/main.c index 96c663d8..cedb4b4d 100644 --- a/bacnet-stack/demo/uptransfer/main.c +++ b/bacnet-stack/demo/uptransfer/main.c @@ -66,8 +66,14 @@ static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; /* converted command line arguments */ static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; -/* Process identifier for matching replies */ -static uint32_t Target_Device_Process_Identifier = 0; +static uint16_t Target_Vendor_Identifier = 260; +static uint32_t Target_Service_Number = 0; +/* property value encodings */ +#ifndef MAX_PROPERTY_VALUES +#define MAX_PROPERTY_VALUES 64 +#endif +static BACNET_APPLICATION_DATA_VALUE + Target_Object_Property_Value[MAX_PROPERTY_VALUES]; /* buffer for service parameters */ static uint8_t Service_Parameters[MAX_APDU]; /* the invoke id is needed to filter incoming messages */ @@ -118,6 +124,70 @@ void MyRejectHandler( } } +void MyUnconfirmedPrivateTransferHandler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + BACNET_PRIVATE_TRANSFER_DATA private_data; + BACNET_OBJECT_PROPERTY_VALUE object_value; /* for bacapp printing */ + BACNET_APPLICATION_DATA_VALUE value; /* for decode value data */ + int len = 0; + uint8_t *application_data; + int application_data_len; + bool first_value = true; + bool print_brace = false; + + len = ptransfer_decode_service_request( + service_request, service_len, &private_data); + if (len >= 0) { + printf("PrivateTransfer:vendorID=%u\r\n", + (unsigned)private_data.vendorID); + printf("PrivateTransfer:serviceNumber=%lu\r\n", + (unsigned long)private_data.serviceNumber); + application_data = private_data.serviceParameters; + application_data_len = private_data.serviceParametersLen; + for (;;) { + len = + bacapp_decode_application_data(application_data, + (uint8_t) application_data_len, &value); + if (first_value && (len < application_data_len)) { + first_value = false; +#if PRINT_ENABLED + fprintf(stdout, "{"); +#endif + print_brace = true; + } + /* private transfer doesn't provide any clues */ + object_value.object_type = MAX_BACNET_OBJECT_TYPE; + object_value.object_instance = BACNET_MAX_INSTANCE; + object_value.object_property = MAX_BACNET_PROPERTY_ID; + object_value.array_index = BACNET_ARRAY_ALL; + object_value.value = &value; + bacapp_print_value(stdout, &object_value); + if (len > 0) { + if (len < application_data_len) { + application_data += len; + application_data_len -= len; + /* there's more! */ +#if PRINT_ENABLED + fprintf(stdout, ","); +#endif + } else { + break; + } + } else { + break; + } + } +#if PRINT_ENABLED + if (print_brace) + fprintf(stdout, "}"); + fprintf(stdout, "\r\n"); +#endif + } +} + static void Init_Service_Handlers( void) { @@ -134,11 +204,9 @@ static void Init_Service_Handlers( /* 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 COV subscriptions */ - apdu_set_confirmed_handler(SERVICE_CONFIRMED_COV_NOTIFICATION, - handler_ccov_notification); - apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_COV_NOTIFICATION, - handler_ucov_notification); + /* handle the data coming back from confirmed requests */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_PRIVATE_TRANSFER, + MyUnconfirmedPrivateTransferHandler); /* handle any errors coming back */ apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler); apdu_set_abort_handler(MyAbortHandler); @@ -162,97 +230,117 @@ int main( time_t delta_seconds = 0; bool found = false; char *filename = NULL; - BACNET_SUBSCRIBE_COV_DATA cov_data = {0}; - bool print_usage_terse = false; - bool print_usage_verbose = false; + char *value_string = NULL; + bool status = false; + int args_remaining = 0, tag_value_arg = 0, i = 0; + BACNET_APPLICATION_TAG property_tag; + uint8_t context_tag = 0; + BACNET_PRIVATE_TRANSFER_DATA private_data = {0}; + int len = 0; + bool sent_message = false; - if (argc < 5) { - print_usage_terse = true; - } - if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { - print_usage_terse = true; - print_usage_verbose = true; - } - if (print_usage_terse) { + if (argc < 6) { filename = filename_remove_path(argv[0]); - /* note: priority 16 and 0 should produce the same end results... */ - printf("Usage: %s device-id object-type object-instance " - "process-id lifetime [issueConfirmedNotifications]\r\n", - filename); - if (!print_usage_verbose) { - return 0; + printf("Usage: %s device-instance vendor-id service-number " + "tag value [tag value...]\r\n", filename); + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + printf("device-instance:\r\n" + "BACnet Device Object Instance number that you are\r\n" + "trying to communicate to. This number will be used\r\n" + "to try and bind with the device using Who-Is and\r\n" + "I-Am services. For example, if you were transferring to\r\n" + "Device Object 123, the device-instance would be 123.\r\n" + "\r\n" + "vendor_id:\r\n" + "the unique vendor identification code for the type of\r\n" + "vendor proprietary service to be performed.\r\n" + "\r\n" + "service-number (Unsigned32):\r\n" + "the desired proprietary service to be performed.\r\n" + "\r\n" + "tag:\r\n" + "Tag is the integer value of the enumeration \r\n" + "BACNET_APPLICATION_TAG in bacenum.h.\r\n" + "It is the data type of the value that you are sending.\r\n" + "For example, if you were transfering a REAL value, you would \r\n" + "use a tag of 4.\r\n" + "Context tags are created using two tags in a row.\r\n" + "The context tag is preceded by a C. Ctag tag.\r\n" + "C2 4 creates a context 2 tagged REAL.\r\n" + "\r\n" + "value:\r\n" + "The value is an ASCII representation of some type of data\r\n" + "that you are transfering.\r\n" + "It is encoded using the tag information provided.\r\n" + "For example, if you were transferring a REAL value of 100.0,\r\n" + "you would use 100.0 as the value.\r\n" + "If you were transferring an object identifier for Device 123,\r\n" + "you would use 8:123 as the value.\r\n" + "\r\n" + "Example:\r\n" + "If you want to transfer a REAL value of 1.1 to service 23 of \r\n" + "vendor 260 in Device 99, you could send the following command:\r\n" + "%s 99 260 23 4 1.1\r\n", filename); } - } - if (print_usage_verbose) { - printf("\r\n" - "device-id:\r\n" - "The subscriber BACnet Device Object Instance number.\r\n" - "\r\n" - "object-type:\r\n" - "The monitored object type is the integer value of the\r\n" - "enumeration BACNET_OBJECT_TYPE in bacenum.h. For example,\r\n" - "if you were monitoring Analog Output 2, the object-type\r\n" - "would be 1.\r\n" - "\r\n" - "object-instance:\r\n" - "The monitored object instance number.\r\n" - "\r\n" - "process-id:\r\n" - "Process Identifier for this COV subscription.\r\n" - "\r\n" - "lifetime:\r\n" - "Optional subscription lifetime is conveyed in seconds.\r\n" - "\r\n" - "issueConfirmedNotifications:\r\n" - "Optional flag to subscribe using Confirmed notifications.\r\n" - "Use the word \'confirmed\'.\r\n" - "\r\n" - "If both the \'issueConfirmedNotifications\' and\r\n" - "\'lifetime\' parameters are absent, then this shall\r\n" - "indicate a cancellation request.\r\n" - "\r\n" - "Example:\r\n" - "If you want subscribe to Device 123 Analog Input 9 object\r\n" - "using confirmed COV notifications for 5 minutes,\r\n" - "you could send the following command:\r\n" - "%s 123 0 9 1 600 confirmed\r\n" - "To send the same COV subscription request for unconfirmed\r\n" - "notifications, you could send the following command:\r\n" - "%s 123 0 9 1 600\r\n", - filename, filename); return 0; } /* decode the command line parameters */ Target_Device_Object_Instance = strtol(argv[1], NULL, 0); - cov_data.monitoredObjectIdentifier.type = strtol(argv[2], NULL, 0); - cov_data.monitoredObjectIdentifier.instance = strtol(argv[3], NULL, 0); - cov_data.subscriberProcessIdentifier = - Target_Device_Process_Identifier = strtol(argv[4], NULL, 0); - if (argc > 5) { - cov_data.lifetime = strtol(argv[5], NULL, 0); - cov_data.issueConfirmedNotifications = false; - } else { - cov_data.cancellationRequest = true; - } - if (argc > 6) { - if (strcmp(argv[6],"confirmed") == 0) { - cov_data.issueConfirmedNotifications = true; - } - } - if (Target_Device_Object_Instance >= BACNET_MAX_INSTANCE) { + Target_Vendor_Identifier = strtol(argv[2], NULL, 0); + Target_Service_Number = strtol(argv[3], 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; } - if (cov_data.monitoredObjectIdentifier.type >= MAX_BACNET_OBJECT_TYPE) { - fprintf(stderr, "object-type=%u - it must be less than %u\r\n", - cov_data.monitoredObjectIdentifier.type, MAX_BACNET_OBJECT_TYPE); - return 1; + args_remaining = (argc - (6-2)); + for (i = 0; i < MAX_PROPERTY_VALUES; i++) { + tag_value_arg = (6-2) + (i * 2); + /* special case for context tagged values */ + if (toupper(argv[tag_value_arg][0]) == 'C') { + context_tag = strtol(&argv[tag_value_arg][1], NULL, 0); + tag_value_arg++; + args_remaining--; + Target_Object_Property_Value[i].context_tag = context_tag; + Target_Object_Property_Value[i].context_specific = true; + } else { + Target_Object_Property_Value[i].context_specific = false; + } + property_tag = strtol(argv[tag_value_arg], NULL, 0); + args_remaining--; + if (args_remaining <= 0) { + fprintf(stderr, "Error: not enough tag-value pairs\r\n"); + return 1; + } + value_string = argv[tag_value_arg + 1]; + args_remaining--; + /* printf("tag[%d]=%u value[%d]=%s\r\n", + i, property_tag, i, value_string); */ + if (property_tag >= MAX_BACNET_APPLICATION_TAG) { + fprintf(stderr, "Error: tag=%u - it must be less than %u\r\n", + property_tag, MAX_BACNET_APPLICATION_TAG); + return 1; + } + status = + bacapp_parse_application_data(property_tag, value_string, + &Target_Object_Property_Value[i]); + if (!status) { + /* FIXME: show the expected entry format for the tag */ + fprintf(stderr, "Error: unable to parse the tag value\r\n"); + return 1; + } + Target_Object_Property_Value[i].next = NULL; + if (i > 0) { + Target_Object_Property_Value[i - 1].next = + &Target_Object_Property_Value[i]; + } + if (args_remaining <= 0) { + break; + } } - if (cov_data.monitoredObjectIdentifier.instance > BACNET_MAX_INSTANCE) { - fprintf(stderr, "object-instance=%u - it must be less than %u\r\n", - cov_data.monitoredObjectIdentifier.instance, - BACNET_MAX_INSTANCE + 1); + if (args_remaining > 0) { + fprintf(stderr, "Error: Exceeded %d tag-value pairs.\r\n", + MAX_PROPERTY_VALUES); return 1; } /* setup my info */ @@ -263,9 +351,6 @@ int main( /* configure the timeout values */ last_seconds = time(NULL); timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); - if (timeout_seconds < cov_data.lifetime) { - timeout_seconds = cov_data.lifetime; - } /* try to bind with the device */ found = address_bind_request(Target_Device_Object_Instance, &max_apdu, @@ -283,9 +368,7 @@ int main( /* increment timer - exit if timed out */ delta_seconds = current_seconds - last_seconds; elapsed_seconds += delta_seconds; - tsm_timer_milliseconds((delta_seconds * 1000)); - /* keep track of time for next check */ - last_seconds = current_seconds; + tsm_timer_milliseconds(((current_seconds - last_seconds) * 1000)); } if (Error_Detected) break; @@ -295,28 +378,26 @@ int main( address_bind_request(Target_Device_Object_Instance, &max_apdu, &Target_Address); } - if (found) { - if (Request_Invoke_ID == 0) { - Request_Invoke_ID = Send_COV_Subscribe( - Target_Device_Object_Instance, - &cov_data); - printf("Sent SubscribeCOV request. Waiting %u seconds.\r\n", + if (!sent_message) { + if (found) { + len = bacapp_encode_data(&Service_Parameters[0], + &Target_Object_Property_Value[0]); + private_data.serviceParameters = &Service_Parameters[0]; + private_data.serviceParametersLen = len; + private_data.vendorID = Target_Vendor_Identifier; + private_data.serviceNumber = Target_Service_Number; + Send_UnconfirmedPrivateTransfer( + &Target_Address, + &private_data); + printf("Sent PrivateTransfer. Waiting %u seconds.\r\n", (unsigned)(timeout_seconds - elapsed_seconds)); - } else if (tsm_invoke_id_free(Request_Invoke_ID)) { - /* do nothing - wait for lifetime value to expire */ - } else if (tsm_invoke_id_failed(Request_Invoke_ID)) { - fprintf(stderr, "\rError: TSM Timeout!\r\n"); - tsm_free_invoke_id(Request_Invoke_ID); - Error_Detected = true; - /* try again or abort? */ - break; - } - } else { - /* exit if timed out */ - if (elapsed_seconds > timeout_seconds) { - Error_Detected = true; - printf("\rError: APDU Timeout!\r\n"); - break; + sent_message = true; + } else { + if (elapsed_seconds > timeout_seconds) { + printf("\rError: APDU Timeout!\r\n"); + Error_Detected = true; + break; + } } } /* returns 0 bytes on timeout */ @@ -328,11 +409,14 @@ int main( if (Error_Detected) { break; } - /* COV - so just wait until lifetime value expires */ + /* unconfirmed - so just wait until our timeout value */ if (elapsed_seconds > timeout_seconds) { 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/uptransfer/makefile.b32 b/bacnet-stack/demo/uptransfer/makefile.b32 index 8e42bdb7..16bec5df 100644 --- a/bacnet-stack/demo/uptransfer/makefile.b32 +++ b/bacnet-stack/demo/uptransfer/makefile.b32 @@ -11,7 +11,7 @@ BORLAND_DIR_Not_Defined: @echo You must define environment variable BORLAND_DIR to compile. !endif -PRODUCT = bacscov +PRODUCT = bacupt PRODUCT_EXE = $(PRODUCT).exe # tools