From b602ba573956f2ba79c48d374120214016c47138 Mon Sep 17 00:00:00 2001 From: skarg Date: Tue, 27 Mar 2007 17:39:27 +0000 Subject: [PATCH] Added demo for extracting EPICS from device. Only extracts device object properties at this time. --- bacnet-stack/demo/epics/Makefile | 87 ++++++++ bacnet-stack/demo/epics/main.c | 290 +++++++++++++++++++++++++++ bacnet-stack/demo/epics/makefile.b32 | 157 +++++++++++++++ 3 files changed, 534 insertions(+) create mode 100644 bacnet-stack/demo/epics/Makefile create mode 100644 bacnet-stack/demo/epics/main.c create mode 100644 bacnet-stack/demo/epics/makefile.b32 diff --git a/bacnet-stack/demo/epics/Makefile b/bacnet-stack/demo/epics/Makefile new file mode 100644 index 00000000..6d971a49 --- /dev/null +++ b/bacnet-stack/demo/epics/Makefile @@ -0,0 +1,87 @@ +#Makefile to build BACnet Application for the Linux Port +CC = gcc +BASEDIR = . +#CFLAGS = -Wall -I. +# -g for debugging with gdb +#CFLAGS = -Wall -I. -O2 -g +# Note: you can strip out symbols using the strip command +# to get an idea of how big the compile really is. +#DEFINES = -DBACFILE=1 -DTSM_ENABLED=1 -DBIG_ENDIAN=0 -DPRINT_ENABLED=1 -DBACDL_ETHERNET=1 +#DEFINES = -DBACFILE=1 -DTSM_ENABLED=1 -DBIG_ENDIAN=0 -DPRINT_ENABLED=1 -DBACDL_ARCNET=1 +#DEFINES = -DBACFILE=1 -DTSM_ENABLED=1 -DBIG_ENDIAN=0 -DPRINT_ENABLED=1 -DBACDL_MSTP=1 +DEFINES = -DBACFILE=1 -DTSM_ENABLED=1 -DBIG_ENDIAN=0 -DPRINT_ENABLED=1 -DBACDL_BIP=1 +BACNET_PORT = ../../ports/linux +BACNET_OBJECT = ../object +BACNET_HANDLER = ../handler +BACNET_ROOT = ../.. +INCLUDES = -I$(BACNET_ROOT) -I$(BACNET_PORT) -I$(BACNET_OBJECT) -I$(BACNET_HANDLER) + +CFLAGS = -Wall -g $(INCLUDES) $(DEFINES) + +TARGET = bacepics + +SRCS = main.c \ + $(BACNET_PORT)/bip-init.c \ + $(BACNET_PORT)/ethernet.c \ + $(BACNET_PORT)/arcnet.c \ + $(BACNET_ROOT)/bip.c \ + $(BACNET_HANDLER)/txbuf.c \ + $(BACNET_HANDLER)/noserv.c \ + $(BACNET_HANDLER)/h_whois.c \ + $(BACNET_HANDLER)/h_rp.c \ + $(BACNET_HANDLER)/h_iam.c \ + $(BACNET_HANDLER)/h_rp_a.c \ + $(BACNET_HANDLER)/s_rp.c \ + $(BACNET_HANDLER)/s_whois.c \ + $(BACNET_OBJECT)/device.c \ + $(BACNET_OBJECT)/ai.c \ + $(BACNET_OBJECT)/ao.c \ + $(BACNET_OBJECT)/av.c \ + $(BACNET_OBJECT)/bi.c \ + $(BACNET_OBJECT)/bo.c \ + $(BACNET_OBJECT)/bv.c \ + $(BACNET_OBJECT)/lc.c \ + $(BACNET_OBJECT)/lsp.c \ + $(BACNET_OBJECT)/mso.c \ + $(BACNET_OBJECT)/bacfile.c \ + $(BACNET_ROOT)/filename.c \ + $(BACNET_ROOT)/rp.c \ + $(BACNET_ROOT)/bacdcode.c \ + $(BACNET_ROOT)/bacapp.c \ + $(BACNET_ROOT)/bacprop.c \ + $(BACNET_ROOT)/datetime.c \ + $(BACNET_ROOT)/bacstr.c \ + $(BACNET_ROOT)/bactext.c \ + $(BACNET_ROOT)/indtext.c \ + $(BACNET_ROOT)/bigend.c \ + $(BACNET_ROOT)/whois.c \ + $(BACNET_ROOT)/iam.c \ + $(BACNET_ROOT)/tsm.c \ + $(BACNET_ROOT)/address.c \ + $(BACNET_ROOT)/arf.c \ + $(BACNET_ROOT)/dcc.c \ + $(BACNET_ROOT)/abort.c \ + $(BACNET_ROOT)/reject.c \ + $(BACNET_ROOT)/bacerror.c \ + $(BACNET_ROOT)/apdu.c \ + $(BACNET_ROOT)/npdu.c + +OBJS = ${SRCS:.c=.o} + +all: ${TARGET} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak ports/linux/*.bak *.1 *.ini + +include: .depend + diff --git a/bacnet-stack/demo/epics/main.c b/bacnet-stack/demo/epics/main.c new file mode 100644 index 00000000..8d6dc1f0 --- /dev/null +++ b/bacnet-stack/demo/epics/main.c @@ -0,0 +1,290 @@ +/************************************************************************** +* +* 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 "arf.h" +#include "tsm.h" +#include "address.h" +#include "config.h" +#include "bacdef.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" + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* global variables used in this file */ +static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; +static bool Error_Detected = false; +static BACNET_ADDRESS Target_Address; + +static void MyErrorHandler(BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, BACNET_ERROR_CODE error_code) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + /* Ignore errors for EPICS application */ + /* + printf("BACnet Error: %s: %s\r\n", + bactext_error_class_name(error_class), + bactext_error_code_name(error_code)); + */ + Error_Detected = true; +} + +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; + /* Ignore errors for EPICS application */ + /* + 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; + /* Ignore errors for EPICS application */ + /* + 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); + /* 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_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property_ack); + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, + MyErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +static uint8_t Read_Properties(uint32_t device_instance) +{ + uint8_t invoke_id = 0; + static unsigned index = 0; + /* list of required (and some optional) properties in the + Device Object + note: you could just loop through + all the properties in all the objects. */ + const int object_props[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_SYSTEM_STATUS, + PROP_VENDOR_NAME, + PROP_VENDOR_IDENTIFIER, + PROP_MODEL_NAME, + PROP_FIRMWARE_REVISION, + PROP_APPLICATION_SOFTWARE_VERSION, + PROP_PROTOCOL_VERSION, + PROP_PROTOCOL_CONFORMANCE_CLASS, + PROP_PROTOCOL_SERVICES_SUPPORTED, + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + PROP_MAX_APDU_LENGTH_ACCEPTED, + PROP_SEGMENTATION_SUPPORTED, + PROP_LOCAL_TIME, + PROP_LOCAL_DATE, + PROP_UTC_OFFSET, + PROP_DAYLIGHT_SAVINGS_STATUS, + PROP_APDU_SEGMENT_TIMEOUT, + PROP_APDU_TIMEOUT, + PROP_NUMBER_OF_APDU_RETRIES, + PROP_TIME_SYNCHRONIZATION_RECIPIENTS, + PROP_MAX_MASTER, + PROP_MAX_INFO_FRAMES, + PROP_DEVICE_ADDRESS_BINDING, + /* note: PROP_OBJECT_LIST is missing cause + we need to get it with an index method since + the list could be very large */ + /* some proprietary properties */ + 514, 515, + /* end of list */ + -1 + }; + + if (object_props[index] != -1) { + printf("%s: ",bactext_property_name(object_props[index])); + invoke_id = Send_Read_Property_Request(device_instance, + OBJECT_DEVICE, + device_instance, + object_props[index], + BACNET_ARRAY_ALL); + if (invoke_id) { + index++; + } + } + + return invoke_id; +} + +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; + uint8_t invoke_id = 0; + bool found = false; + + if (argc < 2) { + printf + ("%s device-instance\r\n", + filename_remove_path(argv[0])); + return 0; + } + /* decode the command line parameters */ + Target_Device_Object_Instance = strtol(argv[1], 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; + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + address_init(); + Init_Service_Handlers(); +#if defined(BACDL_ETHERNET) + /* init the physical layer */ + if (!ethernet_init("eth0")) + return 1; +#elif defined(BACDL_BIP) + bip_set_interface("eth0"); + if (!bip_init()) + return 1; + printf("bip: using port %hu\r\n", bip_get_port()); +#elif defined(BACDL_ARCNET) + if (!arcnet_init("arc0")) + return 1; +#else +#error Datalink (BACDL_ETHERNET,BACDL_BIP, or BACDL_ARCNET) undefined +#endif + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = (Device_APDU_Timeout() / 1000) * + Device_Number_Of_APDU_Retries(); + /* try to bind with the device */ + Send_WhoIs(Target_Device_Object_Instance, + Target_Device_Object_Instance); + /* 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); + } + /* at least one second has passed */ + if (current_seconds != last_seconds) + tsm_timer_milliseconds(((current_seconds - + last_seconds) * 1000)); + /* wait until the device is bound, or timeout and quit */ + found = address_bind_request(Target_Device_Object_Instance, + &max_apdu, &Target_Address); + if (found) { + if (invoke_id == 0) { + invoke_id = Read_Properties(Target_Device_Object_Instance); + if (invoke_id == 0) { + break; + } + } else if (tsm_invoke_id_free(invoke_id)) { + invoke_id = 0; + } + else if (tsm_invoke_id_failed(invoke_id)) { + fprintf(stderr, "\rError: TSM Timeout!\r\n"); + tsm_free_invoke_id(invoke_id); + invoke_id = 0; + } else if (Error_Detected) { + invoke_id = 0; + } + } else { + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) { + printf("\rError: APDU Timeout!\r\n"); + break; + } + } + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + return 0; +} diff --git a/bacnet-stack/demo/epics/makefile.b32 b/bacnet-stack/demo/epics/makefile.b32 new file mode 100644 index 00000000..95191f9f --- /dev/null +++ b/bacnet-stack/demo/epics/makefile.b32 @@ -0,0 +1,157 @@ +# +# 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 = bacepics +PRODUCT_EXE = $(PRODUCT).exe + +# Choose the Data Link Layer to Enable +DEFINES = -DBACDL_BIP=1;TSM_ENABLED=1;BIG_ENDIAN=0;PRINT_ENABLED=1 + +SRCS = main.c \ + ..\..\ports\win32\bip-init.c \ + ..\..\filename.c \ + ..\..\bip.c \ + ..\..\demo\handler\txbuf.c \ + ..\..\demo\handler\noserv.c \ + ..\..\demo\handler\h_whois.c \ + ..\..\demo\handler\h_iam.c \ + ..\..\demo\handler\h_rp.c \ + ..\..\demo\handler\h_rp_a.c \ + ..\..\demo\handler\s_rp.c \ + ..\..\demo\handler\s_whois.c \ + ..\..\bacdcode.c \ + ..\..\bacapp.c \ + ..\..\bacstr.c \ + ..\..\datetime.c \ + ..\..\bactext.c \ + ..\..\indtext.c \ + ..\..\bigend.c \ + ..\..\whois.c \ + ..\..\iam.c \ + ..\..\rp.c \ + ..\..\wp.c \ + ..\..\arf.c \ + ..\..\awf.c \ + ..\..\dcc.c \ + ..\..\demo\object\bacfile.c \ + ..\..\demo\object\device.c \ + ..\..\demo\object\ai.c \ + ..\..\demo\object\ao.c \ + ..\..\demo\object\av.c \ + ..\..\demo\object\bi.c \ + ..\..\demo\object\bo.c \ + ..\..\demo\object\bv.c \ + ..\..\demo\object\lc.c \ + ..\..\demo\object\lsp.c \ + ..\..\demo\object\mso.c \ + ..\..\tsm.c \ + ..\..\address.c \ + ..\..\abort.c \ + ..\..\reject.c \ + ..\..\bacerror.c \ + ..\..\apdu.c \ + ..\..\npdu.c + +OBJS = $(SRCS:.c=.obj) + +# Compiler definitions +# +CC = $(BORLAND_DIR)\bin\bcc32 +bcc32.cfg +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 +TLIB = $(BORLAND_DIR)\bin\tlib + +# +# Include directories +# +CC_DIR = $(BORLAND_DIR)\BIN +INCL_DIRS = -I$(BORLAND_DIR)\include;..\..\;..\..\demo\object\;..\..\demo\handler\;..\..\ports\win32\;. + +CFLAGS = $(INCL_DIRS) $(CS_FLAGS) $(DEFINES) + +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(C_LIB_DIR)\IMPORT32.lib \ +$(C_LIB_DIR)\CW32MT.lib + +# +# Main target +# +# This should be the first one in the makefile + +all : bcc32.cfg $(PRODUCT_EXE) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\utils\$(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 +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -m -c -s -v @&&| # temp response file, starts with | + $(BORLAND_DIR)\lib\c0x32.obj $** # $** lists each dependency + $< + $*.map + $(LIBS) +| # end of temp response file + +# +# Utilities + +clean : + @echo Deleting obj files, $(PRODUCT_EXE) and map files. +# del $(OBJS) # command too long, bummer! + del *.obj + del ..\..\*.obj + del ..\..\demo\handler\*.obj + del ..\..\demo\object\*.obj + del ..\..\ports\win32\*.obj + del $(PRODUCT_EXE) + del *.map + del bcc32.cfg + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) -o$@ $< + +# Compiler configuration file +bcc32.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