diff --git a/bacnet-stack/demo/Makefile b/bacnet-stack/demo/Makefile index b29db1ed..a05b7d9e 100644 --- a/bacnet-stack/demo/Makefile +++ b/bacnet-stack/demo/Makefile @@ -48,7 +48,7 @@ LFLAGS = -Wl,$(LIBRARIES) SUBDIRS = readprop writeprop readfile writefile reinit server dcc \ whohas whois ucov scov timesync epics readpropm \ mstpcap mstpcrc uptransfer \ - whoisrouter iamrouter initrouter + whoisrouter iamrouter initrouter readbdt #ifeq (${BACNET_PORT},linux) #SUBDIRS += router #endif diff --git a/bacnet-stack/demo/readbdt/Makefile b/bacnet-stack/demo/readbdt/Makefile new file mode 100644 index 00000000..40893a9b --- /dev/null +++ b/bacnet-stack/demo/readbdt/Makefile @@ -0,0 +1,38 @@ +#Makefile to build BACnet Application for the GCC Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +TARGET = bacrbdt + +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/readbdt/main.c b/bacnet-stack/demo/readbdt/main.c new file mode 100644 index 00000000..d9649ea5 --- /dev/null +++ b/bacnet-stack/demo/readbdt/main.c @@ -0,0 +1,201 @@ +/************************************************************************** +* +* Copyright (C) 2012 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 BVLC message, and displays the reply */ +#include +#include +#include +#include +#include /* for time */ +#include +#include "bactext.h" +#include "iam.h" +#include "address.h" +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "datalink.h" +#include "bvlc.h" +/* some demo stuff needed */ +#define DEBUG_ENABLED 0 +#include "debug.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 }; + +/* targets interpreted from the command line options */ +static uint32_t Target_BBMD_Address; +static uint16_t Target_BBMD_Port; + +static bool Error_Detected = false; + +static 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(abort_reason)); + Error_Detected = true; +} + +static 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(reject_reason)); + Error_Detected = true; +} + +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); + /* 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 reply (request) coming back */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_add); + /* handle any errors coming back */ + 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 */ + time_t total_seconds = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + long port = 0; + + if (argc < 2) { + printf("Usage: %s IP [port]\r\n", filename_remove_path(argv[0])); + return 0; + } + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + printf("Send a Read-Broadcast-Distribution-Table message to a BBMD.\r\n" + "\r\n" + "IP:\r\n" + "IP address of the BBMD in dotted decimal notation\r\n" + "[port]\r\n" + "optional BACnet/IP port number (default=47808=0xBAC0)\r\n" + "\r\n" + "To send a Read-Broadcast-Distribution-Table message to a BBMD\r\n" + "at 192.168.0.1 using port 47808:\r\n" + "%s 192.168.0.1 47808\r\n", + filename_remove_path(argv[0])); + return 0; + } + /* decode the command line parameters */ + if (argc > 1) { + Target_BBMD_Address = inet_addr(argv[1]); + if (Target_BBMD_Address == (-1)) { + fprintf(stderr, "IP=%s - failed to convert address.\r\n", + argv[1]); + return 1; + } + } + if (argc > 2) { + port = strtol(argv[2], NULL, 0); + if ((port > 0) && (port <= 65535)) { + Target_BBMD_Port = htons(port); + } else { + fprintf(stderr, "port=%ld - port must be between 0-65535.\r\n", + port); + return 1; + } + } else { + Target_BBMD_Port = htons(47808); + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + Init_Service_Handlers(); + address_init(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = apdu_timeout() / 1000; + /* send the request */ + bvlc_bbmd_read_bdt(Target_BBMD_Address, Target_BBMD_Port); + /* 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); + } + if (Error_Detected) + break; + /* increment timer - exit if timed out */ + elapsed_seconds = current_seconds - last_seconds; + if (elapsed_seconds) { +#if defined(BACDL_BIP) && BBMD_ENABLED + bvlc_maintenance_timer(elapsed_seconds); +#endif + } + total_seconds += elapsed_seconds; + if (total_seconds > timeout_seconds) + break; + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + return 0; +} diff --git a/bacnet-stack/demo/readbdt/makefile.b32 b/bacnet-stack/demo/readbdt/makefile.b32 new file mode 100644 index 00000000..6840a4bc --- /dev/null +++ b/bacnet-stack/demo/readbdt/makefile.b32 @@ -0,0 +1,140 @@ +# +# 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 + +# target +PRODUCT = bacrbdt +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 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c \ + $(BACNET_OBJECT)\device-client.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/include/bvlc.h b/bacnet-stack/include/bvlc.h index 3a805ccd..b68025aa 100644 --- a/bacnet-stack/include/bvlc.h +++ b/bacnet-stack/include/bvlc.h @@ -31,6 +31,8 @@ #include "bacdef.h" #include "npdu.h" +struct sockaddr_in; /* Defined elsewhere, needed here. */ + #ifdef __cplusplus extern "C" { @@ -55,12 +57,15 @@ extern "C" { uint8_t * pdu, /* any data to be sent - may be null */ unsigned pdu_len); + int bvlc_send_mpdu( + struct sockaddr_in *dest, + uint8_t * mtu, + uint16_t mtu_len); + #if defined(BBMD_CLIENT_ENABLED) && BBMD_CLIENT_ENABLED int bvlc_encode_write_bdt_init( uint8_t * pdu, unsigned entries); - int bvlc_encode_read_bdt( - uint8_t * pdu); int bvlc_encode_read_fdt( uint8_t * pdu); int bvlc_encode_delete_fdt_entry( @@ -76,6 +81,11 @@ extern "C" { uint8_t * npdu, unsigned npdu_length); #endif + int bvlc_encode_read_bdt( + uint8_t * pdu); + int bvlc_bbmd_read_bdt( + uint32_t bbmd_address, + uint16_t bbmd_port); /* registers with a bbmd as a foreign device */ int bvlc_register_with_bbmd( @@ -83,8 +93,6 @@ extern "C" { uint16_t bbmd_port, /* in network byte order */ uint16_t time_to_live_seconds); - struct sockaddr_in; /* Defined elsewhere, needed here. */ - /* Note any BVLC_RESULT code, or NAK the BVLL message in the unsupported cases. */ int bvlc_for_non_bbmd( struct sockaddr_in *sout, diff --git a/bacnet-stack/src/bvlc.c b/bacnet-stack/src/bvlc.c index 32907cb1..5a8228f4 100644 --- a/bacnet-stack/src/bvlc.c +++ b/bacnet-stack/src/bvlc.c @@ -248,7 +248,6 @@ int bvlc_encode_write_bdt_init( } #endif -#if defined(BBMD_CLIENT_ENABLED) && BBMD_CLIENT_ENABLED int bvlc_encode_read_bdt( uint8_t * pdu) { @@ -266,8 +265,34 @@ int bvlc_encode_read_bdt( return len; } -#endif +/** + * Read the The Read-Broadcast-Distribution-Table of a BBMD + * + * @param bbmd_address - IPv4 address (long) of BBMD to read, + * in network byte order. + * @param bbmd_port - Network port of BBMD to read, in network byte order + * @return Upon successful completion, returns the number of bytes sent. + * Otherwise, -1 shall be returned and errno set to indicate the error. + */ +int bvlc_bbmd_read_bdt( + uint32_t bbmd_address, + uint16_t bbmd_port) +{ + uint8_t mtu[MAX_MPDU] = { 0 }; + uint16_t mtu_len = 0; + int rv = 0; + struct sockaddr_in bbmd = { 0 }; + + mtu_len = bvlc_encode_read_bdt(mtu); + if (mtu_len > 0) { + bbmd.sin_addr.s_addr = bbmd_address; + bbmd.sin_port = bbmd_port; + rv = bvlc_send_mpdu(&bbmd, &mtu[0], mtu_len); + } + + return rv; +} #if defined(BBMD_ENABLED) && BBMD_ENABLED static int bvlc_encode_read_bdt_ack_init( @@ -621,12 +646,23 @@ static bool bvlc_delete_foreign_device( } #endif -/** The common send function for bvlc functions, using b/ip. */ -static int bvlc_send_mpdu( - struct sockaddr_in *dest, /* the destination address */ - uint8_t * mtu, /* the data */ +/** + * The common send function for bvlc functions, using b/ip. + * + * @param dest - Points to a sockaddr_in structure containing the + * destination address. The length and format of the address depend + * on the address family of the socket (AF_INET). + * The address is in network byte order. + * @param mtu - the bytes of data to send + * @param mtu_len - the number of bytes of data to send + * @return Upon successful completion, returns the number of bytes sent. + * Otherwise, -1 shall be returned and errno set to indicate the error. + */ +int bvlc_send_mpdu( + struct sockaddr_in *dest, + uint8_t * mtu, uint16_t mtu_len) -{ /* amount of data to send */ +{ struct sockaddr_in bvlc_dest = { 0 }; /* assumes that the driver has already been initialized */ @@ -643,7 +679,6 @@ static int bvlc_send_mpdu( (struct sockaddr *) &bvlc_dest, sizeof(struct sockaddr)); } - #if defined(BBMD_ENABLED) && BBMD_ENABLED static void bvlc_bdt_forward_npdu( struct sockaddr_in *sin, /* source address in network order */