diff --git a/bacnet-stack/Makefile b/bacnet-stack/Makefile index ce517d2f..f9dc1103 100644 --- a/bacnet-stack/Makefile +++ b/bacnet-stack/Makefile @@ -74,6 +74,9 @@ gateway: server: $(MAKE) -B -C demo server +iam: + $(MAKE) -B -C demo iam + router: $(MAKE) -s -C demo router diff --git a/bacnet-stack/bin/vendor-id.sh b/bacnet-stack/bin/vendor-id.sh new file mode 100755 index 00000000..f3047b1b --- /dev/null +++ b/bacnet-stack/bin/vendor-id.sh @@ -0,0 +1,5 @@ +#!/bin/bash +for vendor_id in {0..999} +do + ./bin/baciam 4194303 ${vendor_id} +done \ No newline at end of file diff --git a/bacnet-stack/demo/Makefile b/bacnet-stack/demo/Makefile index 4f1a0206..c6c7a166 100644 --- a/bacnet-stack/demo/Makefile +++ b/bacnet-stack/demo/Makefile @@ -56,7 +56,7 @@ LFLAGS := -Wl,$(BACNET_LIB),$(SYSTEM_LIB) .EXPORT_ALL_VARIABLES: SUBDIRS = readprop writeprop readfile writefile reinit server dcc \ - whohas whois ucov scov timesync epics readpropm readrange \ + whohas whois iam ucov scov timesync epics readpropm readrange \ uptransfer getevent ifeq (${BACDL_DEFINE},-DBACDL_BIP=1) @@ -89,5 +89,8 @@ gateway: server: $(MAKE) -b -C server +iam: + $(MAKE) -b -C iam + router: $(MAKE) -s -b -C router diff --git a/bacnet-stack/demo/handler/s_iam.c b/bacnet-stack/demo/handler/s_iam.c index 35a70755..576a5819 100644 --- a/bacnet-stack/demo/handler/s_iam.c +++ b/bacnet-stack/demo/handler/s_iam.c @@ -37,12 +37,56 @@ #include "device.h" #include "datalink.h" #include "iam.h" +#include "txbuf.h" /* some demo stuff needed */ #include "handlers.h" #include "client.h" /** @file s_iam.c Send an I-Am message. */ +/** Send a I-Am request to a remote network for a specific device. + * @param target_address [in] BACnet address of target router + * @param device_id [in] Device Instance 0 - 4194303 + * @param max_apdu [in] Max APDU 0-65535 + * @param segmentation [in] #BACNET_SEGMENTATION enumeration + * @param vendor_id [in] BACnet vendor ID 0-65535 + */ +void Send_I_Am_To_Network( + BACNET_ADDRESS * target_address, + uint32_t device_id, + unsigned int max_apdu, + int segmentation, + uint16_t vendor_id) +{ + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + + datalink_get_my_address(&my_address); + /* encode the NPDU portion of the packet */ + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], target_address, + &my_address, &npdu_data); + /* encode the APDU portion of the packet */ + /* encode the APDU portion of the packet */ + len = + iam_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + device_id, max_apdu, segmentation, vendor_id); + pdu_len += len; + bytes_sent = + datalink_send_pdu(target_address, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) + fprintf(stderr, "Failed to Send I-Am Request (%s)!\n", + strerror(errno)); +#endif +} + /** Encode an I Am message to be broadcast. * @param buffer [in,out] The buffer to use for building the message. * @param dest [out] The destination address information. diff --git a/bacnet-stack/demo/iam/Makefile b/bacnet-stack/demo/iam/Makefile new file mode 100644 index 00000000..4b14a98b --- /dev/null +++ b/bacnet-stack/demo/iam/Makefile @@ -0,0 +1,38 @@ +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +TARGET = baciam + +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/iam/main.c b/bacnet-stack/demo/iam/main.c new file mode 100644 index 00000000..2f4a36c9 --- /dev/null +++ b/bacnet-stack/demo/iam/main.c @@ -0,0 +1,254 @@ +/************************************************************************** +* +* Copyright (C) 2016 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 */ +#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 "version.h" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" + +/* parsed command line parameters */ +static uint32_t Target_Device_ID = BACNET_MAX_INSTANCE; +static uint16_t Target_Vendor_ID = BACNET_VENDOR_ID; +static unsigned int Target_Max_APDU = MAX_APDU; +static int Target_Segmentation = SEGMENTATION_NONE; +/* flag for signalling errors */ +static bool Error_Detected = false; + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + (void) src; + (void) invoke_id; + (void) server; + printf("BACnet Abort: %s\n", bactext_abort_reason_name(abort_reason)); + Error_Detected = true; +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + (void) src; + (void) invoke_id; + printf("BACnet Reject: %s\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); +} + +static void print_usage(char *filename) +{ + printf("Usage: %s [device-instance vendor-id max-apdu segmentation]\n", + filename); + printf(" [--version][--help]\n"); +} + +static void print_help(char *filename) +{ + printf("Send BACnet I-Am message for a device.\n"); + printf("--mac A\n" + "Optional BACnet mac address." + "Valid ranges are from 00 to FF (hex) for MS/TP or ARCNET,\n" + "or an IP string with optional port number like 10.1.2.3:47808\n" + "or an Ethernet MAC in hex like 00:21:70:7e:32:bb\n" + "\n" + "--dnet N\n" + "Optional BACnet network number N for directed requests.\n" + "Valid range is from 0 to 65535 where 0 is the local connection\n" + "and 65535 is network broadcast.\n" + "\n" + "--dadr A\n" + "Optional BACnet mac address on the destination BACnet network number.\n" + "Valid ranges are from 00 to FF (hex) for MS/TP or ARCNET,\n" + "or an IP string with optional port number like 10.1.2.3:47808\n" + "or an Ethernet MAC in hex like 00:21:70:7e:32:bb\n" + "\n"); + printf("device-instance:\n" + " BACnet device-ID 0..4194303\n" + "vendor-id:\n" + " Vendor Identifier 0..65535\n" + "max-apdu:\n" + " Maximum APDU size 50..65535\n" + "segmentation:\n" + " BACnet Segmentation 0=both, 1=transmit, 2=receive, 3=none\n" + "To send an I-Am message for instance=1234 vendor-id=260 max-apdu=480\n" + "%s 1234 260 480\n", + filename); +} + +int main( + int argc, + char *argv[]) +{ + long dnet = -1; + BACNET_MAC_ADDRESS mac = { 0 }; + BACNET_MAC_ADDRESS adr = { 0 }; + BACNET_ADDRESS dest = { 0 }; + bool specific_address = false; + int argi = 0; + unsigned int target_args = 0; + char *filename = NULL; + + filename = filename_remove_path(argv[0]); + for (argi = 1; argi < argc; argi++) { + if (strcmp(argv[argi], "--help") == 0) { + print_usage(filename); + print_help(filename); + return 0; + } + if (strcmp(argv[argi], "--version") == 0) { + printf("%s %s\n", filename, BACNET_VERSION_TEXT); + printf("Copyright (C) 2016 by Steve Karg and others.\n" + "This is free software; see the source for copying conditions.\n" + "There is NO warranty; not even for MERCHANTABILITY or\n" + "FITNESS FOR A PARTICULAR PURPOSE.\n"); + return 0; + } + if (strcmp(argv[argi], "--mac") == 0) { + if (++argi < argc) { + if (address_mac_from_ascii(&mac, argv[argi])) { + specific_address = true; + } + } + } else if (strcmp(argv[argi], "--dnet") == 0) { + if (++argi < argc) { + dnet = strtol(argv[argi], NULL, 0); + if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) { + specific_address = true; + } + } + } else if (strcmp(argv[argi], "--dadr") == 0) { + if (++argi < argc) { + if (address_mac_from_ascii(&adr, argv[argi])) { + specific_address = true; + } + } + } else { + if (target_args == 0) { + Target_Device_ID = strtol(argv[argi], NULL, 0); + target_args++; + } else if (target_args == 1) { + Target_Vendor_ID = strtol(argv[argi], NULL, 0); + target_args++; + } else if (target_args == 2) { + Target_Max_APDU = strtol(argv[argi], NULL, 0); + target_args++; + } else if (target_args == 3) { + Target_Segmentation = strtol(argv[argi], NULL, 0); + target_args++; + } else { + print_usage(filename); + return 1; + } + } + } + address_init(); + if (specific_address) { + if (adr.len && mac.len) { + memcpy(&dest.mac[0], &mac.adr[0], mac.len); + dest.mac_len = mac.len; + memcpy(&dest.adr[0], &adr.adr[0], adr.len); + dest.len = adr.len; + if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) { + dest.net = dnet; + } else { + dest.net = BACNET_BROADCAST_NETWORK; + } + } else if (mac.len) { + memcpy(&dest.mac[0], &mac.adr[0], mac.len); + dest.mac_len = mac.len; + dest.len = 0; + if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) { + dest.net = dnet; + } else { + dest.net = 0; + } + } else { + if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) { + dest.net = dnet; + } else { + dest.net = BACNET_BROADCAST_NETWORK; + } + dest.mac_len = 0; + dest.len = 0; + } + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + Init_Service_Handlers(); + address_init(); + dlenv_init(); + atexit(datalink_cleanup); + /* send the request */ + Send_I_Am_To_Network(&dest, + Target_Device_ID, + Target_Max_APDU, + Target_Segmentation, + Target_Vendor_ID); + + return 0; +} diff --git a/bacnet-stack/demo/iam/makefile.b32 b/bacnet-stack/demo/iam/makefile.b32 new file mode 100644 index 00000000..22fb517b --- /dev/null +++ b/bacnet-stack/demo/iam/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 = baciam +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/client.h b/bacnet-stack/include/client.h index 4912125b..10acdb7a 100644 --- a/bacnet-stack/include/client.h +++ b/bacnet-stack/include/client.h @@ -48,6 +48,12 @@ extern "C" { /* unconfirmed requests */ void Send_I_Am( uint8_t * buffer); + void Send_I_Am_To_Network( + BACNET_ADDRESS * target_address, + uint32_t device_id, + unsigned int max_apdu, + int segmentation, + uint16_t vendor_id); int iam_encode_pdu( uint8_t * buffer, BACNET_ADDRESS * dest,