From 9735ca07509e03817024e6f9715a486a3803afb4 Mon Sep 17 00:00:00 2001 From: skarg Date: Sun, 11 Sep 2011 13:34:37 +0000 Subject: [PATCH] Added UnconfirmedPrivateTransfer demo application. --- bacnet-stack/demo/Makefile | 3 +- bacnet-stack/demo/uptransfer/Makefile | 43 +++ bacnet-stack/demo/uptransfer/main.c | 423 ++++++++++++++++++++++ bacnet-stack/demo/uptransfer/makefile.b32 | 142 ++++++++ bacnet-stack/lib/Makefile | 1 + 5 files changed, 611 insertions(+), 1 deletion(-) create mode 100644 bacnet-stack/demo/uptransfer/Makefile create mode 100644 bacnet-stack/demo/uptransfer/main.c create mode 100644 bacnet-stack/demo/uptransfer/makefile.b32 diff --git a/bacnet-stack/demo/Makefile b/bacnet-stack/demo/Makefile index 39d803f4..e55e925d 100644 --- a/bacnet-stack/demo/Makefile +++ b/bacnet-stack/demo/Makefile @@ -46,7 +46,8 @@ LFLAGS = -Wl,$(LIBRARIES) .EXPORT_ALL_VARIABLES: SUBDIRS = readprop writeprop readfile writefile reinit server dcc \ - whohas whois ucov timesync epics readpropm mstpcap \ + whohas whois ucov timesync epics readpropm \ + mstpcap ptransfer uptransfer \ whoisrouter iamrouter initrouter ifneq (,$(findstring -DBAC_ROUTING,$(BACNET_DEFINES))) diff --git a/bacnet-stack/demo/uptransfer/Makefile b/bacnet-stack/demo/uptransfer/Makefile new file mode 100644 index 00000000..eb43cc87 --- /dev/null +++ b/bacnet-stack/demo/uptransfer/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 = bacupt + +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/uptransfer/main.c b/bacnet-stack/demo/uptransfer/main.c new file mode 100644 index 00000000..cedb4b4d --- /dev/null +++ b/bacnet-stack/demo/uptransfer/main.c @@ -0,0 +1,423 @@ +/************************************************************************* +* 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; +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 */ +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; + } +} + +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) +{ + 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 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); + 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; + 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 < 6) { + filename = filename_remove_path(argv[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); + } + return 0; + } + /* decode the command line parameters */ + Target_Device_Object_Instance = strtol(argv[1], NULL, 0); + 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; + } + 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 (args_remaining > 0) { + fprintf(stderr, "Error: Exceeded %d tag-value pairs.\r\n", + MAX_PROPERTY_VALUES); + 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(); + /* 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(((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 (!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)); + sent_message = true; + } else { + if (elapsed_seconds > timeout_seconds) { + printf("\rError: APDU Timeout!\r\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); + } + if (Error_Detected) { + break; + } + /* 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 new file mode 100644 index 00000000..16bec5df --- /dev/null +++ b/bacnet-stack/demo/uptransfer/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 = bacupt +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/lib/Makefile b/bacnet-stack/lib/Makefile index 0bcd6e8c..1ca179d5 100644 --- a/bacnet-stack/lib/Makefile +++ b/bacnet-stack/lib/Makefile @@ -98,6 +98,7 @@ HANDLER_SRC = \ $(BACNET_HANDLER)/s_ihave.c \ $(BACNET_HANDLER)/s_iam.c \ $(BACNET_HANDLER)/s_cov.c \ + $(BACNET_HANDLER)/s_ptransfer.c \ $(BACNET_HANDLER)/s_rd.c \ $(BACNET_HANDLER)/s_router.c \ $(BACNET_HANDLER)/s_rp.c \