From e75eaf09738e9ccac2150530aae9d3ae00553fbb Mon Sep 17 00:00:00 2001 From: skarg Date: Thu, 21 Aug 2008 22:30:27 +0000 Subject: [PATCH] Adding router inquiry demos. --- bacnet-stack/Makefile | 9 +- bacnet-stack/demo/handler/s_router.c | 24 +- bacnet-stack/demo/iamrouter/Makefile | 66 ++++++ bacnet-stack/demo/iamrouter/main.c | 243 ++++++++++++++++++++ bacnet-stack/demo/initrouter/Makefile | 66 ++++++ bacnet-stack/demo/initrouter/main.c | 314 ++++++++++++++++++++++++++ bacnet-stack/demo/whoisrouter/main.c | 74 +++++- bacnet-stack/include/client.h | 10 + 8 files changed, 785 insertions(+), 21 deletions(-) create mode 100644 bacnet-stack/demo/iamrouter/Makefile create mode 100644 bacnet-stack/demo/iamrouter/main.c create mode 100644 bacnet-stack/demo/initrouter/Makefile create mode 100644 bacnet-stack/demo/initrouter/main.c diff --git a/bacnet-stack/Makefile b/bacnet-stack/Makefile index 67a036c4..35ba57fc 100644 --- a/bacnet-stack/Makefile +++ b/bacnet-stack/Makefile @@ -1,6 +1,6 @@ all: library readprop writeprop readfile writefile reinit server dcc \ whohas whois ucov timesync epics mstpcap \ - whoisrouter iamrouter + whoisrouter iamrouter initrouter @echo "utilities are in the bin directory" clean: lib/Makefile\ @@ -17,6 +17,8 @@ clean: lib/Makefile\ demo/timesync/Makefile \ demo/epics/Makefile \ demo/whoisrouter/Makefile \ + demo/iamrouter/Makefile \ + demo/initrouter/Makefile \ demo/mstpcap/Makefile ( cd lib ; make clean ) ( cd demo/readprop ; make clean ) @@ -32,6 +34,8 @@ clean: lib/Makefile\ ( cd demo/timesync ; make clean ) ( cd demo/epics ; make clean ) ( cd demo/whoisrouter ; make clean ) + ( cd demo/iamrouter ; make clean ) + ( cd demo/initrouter ; make clean ) ( cd demo/mstpcap ; make clean ) library: lib/Makefile @@ -82,3 +86,6 @@ whoisrouter: demo/whoisrouter/Makefile iamrouter: demo/iamrouter/Makefile ( cd demo/iamrouter ; make ; cp baciamr ../../bin ) +initrouter: demo/initrouter/Makefile + ( cd demo/initrouter ; make ; cp bacinitr ../../bin ) + diff --git a/bacnet-stack/demo/handler/s_router.c b/bacnet-stack/demo/handler/s_router.c index 0db6f548..e51640b6 100644 --- a/bacnet-stack/demo/handler/s_router.c +++ b/bacnet-stack/demo/handler/s_router.c @@ -59,17 +59,18 @@ static void npdu_encode_npdu_network( /* find a specific router, or use -1 for limit if you want unlimited */ void Send_Who_Is_Router_To_Network( + BACNET_ADDRESS *dst, int dnet) { int len = 0; int pdu_len = 0; - BACNET_ADDRESS dest; int bytes_sent = 0; BACNET_NPDU_DATA npdu_data; npdu_encode_npdu_network(&npdu_data, NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK, MESSAGE_PRIORITY_NORMAL); + /* fixme: should dnet/dlen/dadr be set in NPDU? */ pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], NULL, NULL, &npdu_data); /* encode the optional DNET portion of the packet */ @@ -84,10 +85,8 @@ void Send_Who_Is_Router_To_Network( fprintf(stderr, "Send Who-Is-Router-To-Network message\n"); #endif } - /* Who-Is-Router-To-Network may be unicast or broadcast */ - datalink_get_broadcast_address(&dest); bytes_sent = - datalink_send_pdu(&dest, &npdu_data, &Handler_Transmit_Buffer[0], + datalink_send_pdu(dst, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len); #if PRINT_ENABLED if (bytes_sent <= 0) @@ -95,11 +94,11 @@ void Send_Who_Is_Router_To_Network( strerror(errno)); #endif } - + /* pDNET_list: list of networks for which I am a router, terminated with -1 */ void Send_I_Am_Router_To_Network( - const int *pDNET_list) + const int DNET_list[]) { int len = 0; int pdu_len = 0; @@ -107,6 +106,7 @@ void Send_I_Am_Router_To_Network( int bytes_sent = 0; BACNET_NPDU_DATA npdu_data; uint16_t dnet = 0; + unsigned index = 0; npdu_encode_npdu_network(&npdu_data, NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, @@ -117,11 +117,11 @@ void Send_I_Am_Router_To_Network( #if PRINT_ENABLED fprintf(stderr, "Send I-Am-Router-To-Network message to:\n"); #endif - while ((*pDNET_list) != -1) { - dnet = (*pDNET_list); + while (DNET_list[index] != -1) { + dnet = DNET_list[index]; len = encode_unsigned16(&Handler_Transmit_Buffer[pdu_len], dnet); pdu_len += len; - pDNET_list++; + index++; #if PRINT_ENABLED fprintf(stderr, "%u\n",dnet); #endif @@ -194,14 +194,12 @@ void Send_Initialize_Routing_Table( /* */ void Send_Initialize_Routing_Table_Ack( - const int *port_info) + BACNET_ROUTER_PORT *router_port_list) { - int len = 0; int pdu_len = 0; BACNET_ADDRESS dest; int bytes_sent = 0; BACNET_NPDU_DATA npdu_data; - uint16_t dnet = 0; npdu_encode_npdu_network(&npdu_data, NETWORK_MESSAGE_INIT_RT_TABLE_ACK, @@ -215,7 +213,7 @@ void Send_Initialize_Routing_Table_Ack( pdu_len); #if PRINT_ENABLED if (bytes_sent <= 0) - fprintf(stderr, "Failed to Send I-Am-Router-To-Network message (%s)!\n", + fprintf(stderr, "Failed to Send Initialize-Routing-Table-Ack message (%s)!\n", strerror(errno)); #endif } diff --git a/bacnet-stack/demo/iamrouter/Makefile b/bacnet-stack/demo/iamrouter/Makefile new file mode 100644 index 00000000..678753ab --- /dev/null +++ b/bacnet-stack/demo/iamrouter/Makefile @@ -0,0 +1,66 @@ +#Makefile to build BACnet Application for the Linux Port +CC = gcc + +TARGET = baciamr + +# Configure the BACnet Datalink Layer +#BACDL_DEFINE = -DBACDL_ETHERNET=1 +#BACDL_DEFINE = -DBACDL_ARCNET=1 +#BACDL_DEFINE = -DBACDL_MSTP=1 +BACDL_DEFINE = -DBACDL_BIP=1 +BACNET_DEFINES = -DBACFILE=1 -DPRINT_ENABLED=1 -DBACAPP_ALL +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +# Directories +BACNET_PORT = linux +BACNET_PORT_DIR = ../../ports/${BACNET_PORT} +BACNET_INCLUDE = ../../include + +# BACnet Library +BACNET_LIB_DIR = ../../lib +BACNET_LIB_NAME = bacnet +BACNET_LIB_TARGET = $(BACNET_LIB_DIR)/lib$(BACNET_LIB_NAME).a +# Compiler Setup +INCLUDES = -I$(BACNET_INCLUDE) -I$(BACNET_PORT_DIR) +ifeq (${BACNET_PORT},linux) +PFLAGS = -pthread +TARGET_BIN = ${TARGET} +LIBRARIES=-lc,-lgcc,-lm,-L=$(BACNET_LIB_DIR),-l$(BACNET_LIB_NAME) +endif +ifeq (${BACNET_PORT},win32) +TARGET_BIN = ${TARGET}.exe +LIBRARY1=-L=$(BACNET_LIB_DIR),-l$(BACNET_LIB_NAME) +LIBRARY2=-lws2_32,-lgcc,-lm,-liphlpapi +LIBRARIES=$(LIBRARY1),$(LIBRARY2) +endif +DEBUGGING = -g +OPTIMIZATION = -O0 +CFLAGS = -Wall $(DEBUGGING) $(OPTIMIZATION) $(INCLUDES) $(DEFINES) +LFLAGS = -Wl,-Map=$(TARGET).map,$(LIBRARIES) + +SRCS = main.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + size ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + +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} + +include: .depend diff --git a/bacnet-stack/demo/iamrouter/main.c b/bacnet-stack/demo/iamrouter/main.c new file mode 100644 index 00000000..f37cb74b --- /dev/null +++ b/bacnet-stack/demo/iamrouter/main.c @@ -0,0 +1,243 @@ +/************************************************************************** +* +* Copyright (C) 2008 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 /* 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" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#if defined(BACDL_MSTP) +#include "rs485.h" +#endif + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* global variables used in this file */ +#define MAX_ROUTER_DNETS 64 +static int Target_Router_Networks[MAX_ROUTER_DNETS] = { -1 }; + +static bool Error_Detected = false; + +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; +} + +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) +{ + /* 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); +} + +static void Init_DataLink( + void) +{ + char *pEnv = NULL; +#if defined(BACDL_BIP) && BBMD_ENABLED + long bbmd_port = 0xBAC0; + long bbmd_address = 0; + long bbmd_timetolive_seconds = 60000; +#endif + +#if defined(BACDL_ALL) + pEnv = getenv("BACNET_DATALINK"); + if (pEnv) { + datalink_set(pEnv)); + } else { + datalink_set(NULL); + } +#endif + +#if defined(BACDL_BIP) + pEnv = getenv("BACNET_IP_PORT"); + if (pEnv) { + bip_set_port(strtol(pEnv, NULL, 0)); + } else { + bip_set_port(0xBAC0); + } +#elif defined(BACDL_MSTP) + pEnv = getenv("BACNET_MAX_INFO_FRAMES"); + if (pEnv) { + dlmstp_set_max_info_frames(strtol(pEnv, NULL, 0)); + } else { + dlmstp_set_max_info_frames(1); + } + pEnv = getenv("BACNET_MAX_MASTER"); + if (pEnv) { + dlmstp_set_max_master(strtol(pEnv, NULL, 0)); + } else { + dlmstp_set_max_master(127); + } + pEnv = getenv("BACNET_MSTP_BAUD"); + if (pEnv) { + RS485_Set_Baud_Rate(strtol(pEnv, NULL, 0)); + } else { + RS485_Set_Baud_Rate(38400); + } + pEnv = getenv("BACNET_MSTP_MAC"); + if (pEnv) { + dlmstp_set_mac_address(strtol(pEnv, NULL, 0)); + } else { + dlmstp_set_mac_address(127); + } +#endif + if (!datalink_init(getenv("BACNET_IFACE"))) { + exit(1); + } +#if defined(BACDL_BIP) && BBMD_ENABLED + pEnv = getenv("BACNET_BBMD_PORT"); + if (pEnv) { + bbmd_port = strtol(pEnv, NULL, 0); + if (bbmd_port > 0xFFFF) { + bbmd_port = 0xBAC0; + } + } + pEnv = getenv("BACNET_BBMD_TIMETOLIVE"); + if (pEnv) { + bbmd_timetolive_seconds = strtol(pEnv, NULL, 0); + if (bbmd_timetolive_seconds > 0xFFFF) { + bbmd_timetolive_seconds = 0xFFFF; + } + } + pEnv = getenv("BACNET_BBMD_ADDRESS"); + if (pEnv) { + bbmd_address = bip_getaddrbyname(pEnv); + if (bbmd_address) { + struct in_addr addr; + addr.s_addr = bbmd_address; + printf("WhoIs: Registering with BBMD at %s:%ld for %ld seconds\n", + inet_ntoa(addr), bbmd_port, bbmd_timetolive_seconds); + bvlc_register_with_bbmd(bbmd_address, bbmd_port, + bbmd_timetolive_seconds); + } + } +#endif +} + +int main(int argc, char *argv[]) { + unsigned arg_count = 0; + + if (argc < 2) { + printf("Usage: %s DNET [DNET] [DNET] [...]\r\n", + filename_remove_path(argv[0])); + return 0; + } + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + printf("Send BACnet I-Am-Router-To-Network message for \r\n" + "one or more networks.\r\n" + "\r\nDNET:\r\n" + "BACnet destination network number 0-65534\r\n" + "To send a I-Am-Router-To-Network message for DNET 86:\r\n" + "%s 86\r\n" + "To send a I-Am-Router-To-Network message for multiple DNETs\r\n" + "use the following command:\r\n" + "%s 86 42 24 14\r\n", + filename_remove_path(argv[0]), + filename_remove_path(argv[0])); + return 0; + } + /* decode the command line parameters */ + if (argc > 1) { + for (arg_count = 1; arg_count < argc; arg_count++) { + if (arg_count > MAX_ROUTER_DNETS) { + fprintf(stderr, + "Limited to %u DNETS. Sorry!\r\n", + MAX_ROUTER_DNETS); + break; + } + Target_Router_Networks[arg_count-1] = strtol(argv[arg_count], NULL, 0); + /* mark the end of list */ + Target_Router_Networks[arg_count] = -1; + /* invalid DNET? */ + if (Target_Router_Networks[arg_count-1] >= 65535) { + fprintf(stderr, + "DNET=%u - it must be less than %u\r\n", + Target_Router_Networks[arg_count-1], 65535); + return 1; + } + } + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + Init_Service_Handlers(); + address_init(); + Init_DataLink(); + /* send the request */ + Send_I_Am_Router_To_Network(Target_Router_Networks); + + return 0; +} diff --git a/bacnet-stack/demo/initrouter/Makefile b/bacnet-stack/demo/initrouter/Makefile new file mode 100644 index 00000000..a290485b --- /dev/null +++ b/bacnet-stack/demo/initrouter/Makefile @@ -0,0 +1,66 @@ +#Makefile to build BACnet Application for the Linux Port +CC = gcc + +TARGET = bacinitr + +# Configure the BACnet Datalink Layer +#BACDL_DEFINE = -DBACDL_ETHERNET=1 +#BACDL_DEFINE = -DBACDL_ARCNET=1 +#BACDL_DEFINE = -DBACDL_MSTP=1 +BACDL_DEFINE = -DBACDL_BIP=1 +BACNET_DEFINES = -DBACFILE=1 -DPRINT_ENABLED=1 -DBACAPP_ALL +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +# Directories +BACNET_PORT = linux +BACNET_PORT_DIR = ../../ports/${BACNET_PORT} +BACNET_INCLUDE = ../../include + +# BACnet Library +BACNET_LIB_DIR = ../../lib +BACNET_LIB_NAME = bacnet +BACNET_LIB_TARGET = $(BACNET_LIB_DIR)/lib$(BACNET_LIB_NAME).a +# Compiler Setup +INCLUDES = -I$(BACNET_INCLUDE) -I$(BACNET_PORT_DIR) +ifeq (${BACNET_PORT},linux) +PFLAGS = -pthread +TARGET_BIN = ${TARGET} +LIBRARIES=-lc,-lgcc,-lm,-L=$(BACNET_LIB_DIR),-l$(BACNET_LIB_NAME) +endif +ifeq (${BACNET_PORT},win32) +TARGET_BIN = ${TARGET}.exe +LIBRARY1=-L=$(BACNET_LIB_DIR),-l$(BACNET_LIB_NAME) +LIBRARY2=-lws2_32,-lgcc,-lm,-liphlpapi +LIBRARIES=$(LIBRARY1),$(LIBRARY2) +endif +DEBUGGING = -g +OPTIMIZATION = -O0 +CFLAGS = -Wall $(DEBUGGING) $(OPTIMIZATION) $(INCLUDES) $(DEFINES) +LFLAGS = -Wl,-Map=$(TARGET).map,$(LIBRARIES) + +SRCS = main.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + size ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + +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} + +include: .depend diff --git a/bacnet-stack/demo/initrouter/main.c b/bacnet-stack/demo/initrouter/main.c new file mode 100644 index 00000000..90d6e8e7 --- /dev/null +++ b/bacnet-stack/demo/initrouter/main.c @@ -0,0 +1,314 @@ +/************************************************************************** +* +* Copyright (C) 2008 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 /* 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" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#if defined(BACDL_MSTP) +#include "rs485.h" +#endif + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +static bool Error_Detected = false; + +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; +} + +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) +{ + /* 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); +} + +static void Init_DataLink( + void) +{ + char *pEnv = NULL; +#if defined(BACDL_BIP) && BBMD_ENABLED + long bbmd_port = 0xBAC0; + long bbmd_address = 0; + long bbmd_timetolive_seconds = 60000; +#endif + +#if defined(BACDL_ALL) + pEnv = getenv("BACNET_DATALINK"); + if (pEnv) { + datalink_set(pEnv)); + } else { + datalink_set(NULL); + } +#endif + +#if defined(BACDL_BIP) + pEnv = getenv("BACNET_IP_PORT"); + if (pEnv) { + bip_set_port(strtol(pEnv, NULL, 0)); + } else { + bip_set_port(0xBAC0); + } +#elif defined(BACDL_MSTP) + pEnv = getenv("BACNET_MAX_INFO_FRAMES"); + if (pEnv) { + dlmstp_set_max_info_frames(strtol(pEnv, NULL, 0)); + } else { + dlmstp_set_max_info_frames(1); + } + pEnv = getenv("BACNET_MAX_MASTER"); + if (pEnv) { + dlmstp_set_max_master(strtol(pEnv, NULL, 0)); + } else { + dlmstp_set_max_master(127); + } + pEnv = getenv("BACNET_MSTP_BAUD"); + if (pEnv) { + RS485_Set_Baud_Rate(strtol(pEnv, NULL, 0)); + } else { + RS485_Set_Baud_Rate(38400); + } + pEnv = getenv("BACNET_MSTP_MAC"); + if (pEnv) { + dlmstp_set_mac_address(strtol(pEnv, NULL, 0)); + } else { + dlmstp_set_mac_address(127); + } +#endif + if (!datalink_init(getenv("BACNET_IFACE"))) { + exit(1); + } +#if defined(BACDL_BIP) && BBMD_ENABLED + pEnv = getenv("BACNET_BBMD_PORT"); + if (pEnv) { + bbmd_port = strtol(pEnv, NULL, 0); + if (bbmd_port > 0xFFFF) { + bbmd_port = 0xBAC0; + } + } + pEnv = getenv("BACNET_BBMD_TIMETOLIVE"); + if (pEnv) { + bbmd_timetolive_seconds = strtol(pEnv, NULL, 0); + if (bbmd_timetolive_seconds > 0xFFFF) { + bbmd_timetolive_seconds = 0xFFFF; + } + } + pEnv = getenv("BACNET_BBMD_ADDRESS"); + if (pEnv) { + bbmd_address = bip_getaddrbyname(pEnv); + if (bbmd_address) { + struct in_addr addr; + addr.s_addr = bbmd_address; + printf("WhoIs: Registering with BBMD at %s:%ld for %ld seconds\n", + inet_ntoa(addr), bbmd_port, bbmd_timetolive_seconds); + bvlc_register_with_bbmd(bbmd_address, bbmd_port, + bbmd_timetolive_seconds); + } + } +#endif +} + +static void address_parse(BACNET_ADDRESS *dst, int argc, char *argv[]) +{ + long device_id = 0; + int dnet = 0; + int max_apdu = 0; + unsigned mac[6]; + int count = 0; + int index = 0; + + if (argc > 0) { + count = + sscanf(argv[0], "%x:%x:%x:%x:%x:%x", &mac[0], + &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); + dst->mac_len = count; + for (index = 0; index < MAX_MAC_LEN; index++) { + if (index < count) { + dst->mac[index] = mac[index]; + } else { + dst->mac[index] = 0; + } + } + } + if (argc > 1) { + count = sscanf(argv[1], "%d", &dnet); + dst->net = dnet; + } + if (dnet) { + if (argc > 2) { + count = + sscanf(argv[2], "%x:%x:%x:%x:%x:%x", &mac[0], + &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); + dst->len = count; + for (index = 0; index < MAX_MAC_LEN; index++) { + if (index < count) { + dst->adr[index] = mac[index]; + } else { + dst->adr[index] = 0; + } + } + } else { + fprintf(stderr,"A non-zero DNET requires a DADR.\r\n"); + } + } else { + src.len = 0; + for (index = 0; index < MAX_MAC_LEN; index++) { + src.adr[index] = 0; + } + } +} + +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; + + if (argc < 2) { + printf("Usage: %s number-of-ports\r\n", + filename_remove_path(argv[0])); + return 0; + } + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + printf("Send BACnet Initialize-Routing-Table message to a network\r\n" + "and wait for responses. Displays their network information.\r\n" + "\r\nDNET:\r\n" + "BACnet destination network number 0-65534\r\n" + "To send a Who-Is-Router-To-Network request to DNET 86:\r\n" + "%s 86\r\n" + "To send a Who-Is-Router-To-Network request to all devices\r\n" + "use the following command:\r\n" + "%s -1\r\n", + filename_remove_path(argv[0]), + filename_remove_path(argv[0])); + return 0; + } + /* decode the command line parameters */ + if (argc > 1) { + Target_Router_Network = strtol(argv[1], NULL, 0); + if (Target_Router_Network >= 65535) { + fprintf(stderr, + "DNET=%u - it must be less than %u\r\n", + Target_Router_Network, 65535); + return 1; + } + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + Init_Service_Handlers(); + address_init(); + Init_DataLink(); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = apdu_timeout() / 1000; + /* send the request */ + Send_Who_Is_Router_To_Network(Target_Router_Network); + /* 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/whoisrouter/main.c b/bacnet-stack/demo/whoisrouter/main.c index 46b3ec81..d931dd93 100644 --- a/bacnet-stack/demo/whoisrouter/main.c +++ b/bacnet-stack/demo/whoisrouter/main.c @@ -53,6 +53,7 @@ static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; /* global variables used in this file */ static int32_t Target_Router_Network = 0; +static BACNET_ADDRESS Target_Router_Address; static bool Error_Detected = false; @@ -187,6 +188,54 @@ static void Init_DataLink( #endif } +static void address_parse(BACNET_ADDRESS *dst, int argc, char *argv[]) +{ + int dnet = 0; + unsigned mac[6]; + int count = 0; + int index = 0; + + if (argc > 0) { + count = + sscanf(argv[0], "%x:%x:%x:%x:%x:%x", &mac[0], + &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); + dst->mac_len = count; + for (index = 0; index < MAX_MAC_LEN; index++) { + if (index < count) { + dst->mac[index] = mac[index]; + } else { + dst->mac[index] = 0; + } + } + } + if (argc > 1) { + count = sscanf(argv[1], "%d", &dnet); + dst->net = dnet; + } + if (dnet) { + if (argc > 2) { + count = + sscanf(argv[2], "%x:%x:%x:%x:%x:%x", &mac[0], + &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); + dst->len = count; + for (index = 0; index < MAX_MAC_LEN; index++) { + if (index < count) { + dst->adr[index] = mac[index]; + } else { + dst->adr[index] = 0; + } + } + } else { + fprintf(stderr,"A non-zero DNET requires a DADR.\r\n"); + } + } else { + dst->len = 0; + for (index = 0; index < MAX_MAC_LEN; index++) { + dst->adr[index] = 0; + } + } +} + int main(int argc, char *argv[]) { BACNET_ADDRESS src = { 0}; /* address where message came from */ @@ -199,19 +248,23 @@ int main(int argc, char *argv[]) { time_t timeout_seconds = 0; if (argc < 2) { - printf("Usage: %s DNET\r\n", + printf("Usage: %s DNET [MAC]\r\n", filename_remove_path(argv[0])); return 0; } if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { - printf("Send BACnet Who-Is-Router-To-Network request to a network\r\n" - "and wait for responses. Displays their network information.\r\n" - "\r\nDNET:\r\n" + printf("Send BACnet Who-Is-Router-To-Network message to a network.\r\n" + "\r\n" + "DNET:\r\n" "BACnet destination network number 0-65534\r\n" + "MAC:\r\n" + "Optional MAC address of router for unicast message\r\n" + "Format: xx[:xx:xx:xx:xx:xx] [dnet xx[:xx:xx:xx:xx:xx]]\r\n" + "Use hexidecimal MAC addresses.\r\n" + "\r\n" "To send a Who-Is-Router-To-Network request to DNET 86:\r\n" "%s 86\r\n" - "To send a Who-Is-Router-To-Network request to all devices\r\n" - "use the following command:\r\n" + "To send a Who-Is-Router-To-Network request to all devices:\r\n" "%s -1\r\n", filename_remove_path(argv[0]), filename_remove_path(argv[0])); @@ -227,6 +280,11 @@ int main(int argc, char *argv[]) { return 1; } } + if (argc > 2) { + address_parse(&Target_Router_Address, argc-2, &argv[2]); + } else { + datalink_get_broadcast_address(&Target_Router_Address); + } /* setup my info */ Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); Init_Service_Handlers(); @@ -236,7 +294,9 @@ int main(int argc, char *argv[]) { last_seconds = time(NULL); timeout_seconds = apdu_timeout() / 1000; /* send the request */ - Send_Who_Is_Router_To_Network(Target_Router_Network); + Send_Who_Is_Router_To_Network( + &Target_Router_Address, + Target_Router_Network); /* loop forever */ for (;;) { /* increment timer - exit if timed out */ diff --git a/bacnet-stack/include/client.h b/bacnet-stack/include/client.h index bfc50061..88998d2b 100644 --- a/bacnet-stack/include/client.h +++ b/bacnet-stack/include/client.h @@ -109,7 +109,17 @@ extern "C" { BACNET_OCTET_STRING * fileData); void Send_Who_Is_Router_To_Network( + BACNET_ADDRESS *dst, int dnet); + void Send_I_Am_Router_To_Network( + const int DNET_list[]); + void Send_Initialize_Routing_Table( + BACNET_ROUTER_PORT *router_port_list); + void Send_Initialize_Routing_Table_Ack( + BACNET_ROUTER_PORT *router_port_list); + + + #ifdef __cplusplus }