diff --git a/bacnet-stack/Makefile b/bacnet-stack/Makefile
index 7d205bcb..2d5517c2 100644
--- a/bacnet-stack/Makefile
+++ b/bacnet-stack/Makefile
@@ -1,5 +1,5 @@
all: library readprop writeprop readfile writefile reinit server dcc \
- whohas whois ucov timesync epics mstpcap
+ whohas whois whoisrouter ucov timesync epics mstpcap
@echo "utilities are in the bin directory"
clean: lib/Makefile\
@@ -15,6 +15,7 @@ clean: lib/Makefile\
demo/ucov/Makefile \
demo/timesync/Makefile \
demo/epics/Makefile \
+ demo/whoisrouter/Makefile \
demo/mstpcap/Makefile
( cd lib ; make clean )
( cd demo/readprop ; make clean )
@@ -29,6 +30,7 @@ clean: lib/Makefile\
( cd demo/ucov ; make clean )
( cd demo/timesync ; make clean )
( cd demo/epics ; make clean )
+ ( cd demo/whoisrouter ; make clean )
( cd demo/mstpcap ; make clean )
library: lib/Makefile
@@ -70,6 +72,9 @@ ucov: demo/ucov/Makefile
whois: demo/whois/Makefile
( cd demo/whois ; make ; cp bacwi ../../bin )
+whoisrouter: demo/whoisrouter/Makefile
+ ( cd demo/whoisrouter ; make ; cp bacwir ../../bin )
+
mstpcap: demo/mstpcap/Makefile
( cd demo/mstpcap ; make clean all; cp mstpcap ../../bin )
diff --git a/bacnet-stack/build.bat b/bacnet-stack/build.bat
index 077b4022..94640c41 100644
--- a/bacnet-stack/build.bat
+++ b/bacnet-stack/build.bat
@@ -1,5 +1,5 @@
@echo off
-echo Build with MinGW
+echo Build with MinGW mingw.sourceforge.net
make BACNET_PORT=win32 OPTIMIZATION=-Os DEBUGGING= clean all
rem Build for MinGW debug
rem make BACNET_PORT=win32 clean all
diff --git a/bacnet-stack/demo/whoisrouter/Makefile b/bacnet-stack/demo/whoisrouter/Makefile
new file mode 100644
index 00000000..b9068bc3
--- /dev/null
+++ b/bacnet-stack/demo/whoisrouter/Makefile
@@ -0,0 +1,66 @@
+#Makefile to build BACnet Application for the Linux Port
+CC = gcc
+
+TARGET = bacwir
+
+# 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/whoisrouter/main.c b/bacnet-stack/demo/whoisrouter/main.c
new file mode 100644
index 00000000..56720afd
--- /dev/null
+++ b/bacnet-stack/demo/whoisrouter/main.c
@@ -0,0 +1,306 @@
+/**************************************************************************
+*
+* 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 /* 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 */
+static int32_t Target_Router_Network = 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 print_address_cache(
+ void)
+{
+ int i, j;
+ BACNET_ADDRESS address;
+ uint32_t device_id = 0;
+ unsigned max_apdu = 0;
+
+ printf("%-7s %-14s %-4s %-5s %-14s\n", "Device", "MAC", "APDU", "SNET",
+ "SADR");
+ printf("------- -------------- ---- ----- --------------\n");
+ for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
+ if (address_get_by_index(i, &device_id, &max_apdu, &address)) {
+ printf("%7u ", device_id);
+ for (j = 0; j < 7; j++) {
+ if (j < address.mac_len) {
+ printf("%02X", address.mac[j]);
+ } else {
+ printf(" ");
+ }
+ }
+ printf(" %4hu ", max_apdu);
+ printf("%5hu ", address.net);
+ if (address.net) {
+ for (j = 0; j < 7; j++) {
+ if (j < address.len) {
+ printf("%02X", address.adr[j]);
+ } else {
+ printf(" ");
+ }
+ printf(" ");
+ }
+ }
+ printf("\n");
+ }
+ }
+}
+
+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[]) {
+ 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 DNET\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"
+ "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_WhoIsRouterToNetwork(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;
+ }
+ print_address_cache();
+
+ return 0;
+}
diff --git a/bacnet-stack/include/client.h b/bacnet-stack/include/client.h
index e57b6cbd..06fb15ce 100644
--- a/bacnet-stack/include/client.h
+++ b/bacnet-stack/include/client.h
@@ -108,6 +108,8 @@ extern "C" {
int fileStartPosition,
BACNET_OCTET_STRING * fileData);
+ void Send_WhoIsRouterToNetwork(
+ int dnet);
#ifdef __cplusplus
}
diff --git a/bacnet-stack/lib/Makefile b/bacnet-stack/lib/Makefile
index 3f3fc82d..f71f8ad8 100644
--- a/bacnet-stack/lib/Makefile
+++ b/bacnet-stack/lib/Makefile
@@ -94,6 +94,7 @@ HANDLER_SRC = \
$(BACNET_HANDLER)/s_ts.c \
$(BACNET_HANDLER)/s_whohas.c \
$(BACNET_HANDLER)/s_whois.c \
+ $(BACNET_HANDLER)/s_whoisrt.c \
$(BACNET_HANDLER)/s_wp.c
OBJECT_SRC = \