Issue 2 move folders and use deep path include file names to prevent collisions (#4)
* moving folders and files and adjust server demo build * Fix Makefile for apps/server on Linux * fix unit test source file folders * fix datetime convert UTC functions. Add Code::Blocks project for datetime testing * added some ignore extensions * disable parallel make option * fix build for abort, dcc, and epics apps * fix build for dcc, epics, error, and getevent apps. * Fixed building of all apps * fix the ipv4 to ipv6 router app build * Change indent style from Google to Webkit * make pretty to re-format style * removed common Makefile since we already had one and two was too many * remove scripts from root folder that are no longer maintained or used * remove mercurial EOL and ignore files for git repo * remove .vscodeconfig files from repo * tweak clang-format style * clang-format src and apps with tweaked style * added clang-tidy to fix readability if braces in src * result of make tidy for src and apps * fix clang-tidy mangling * Added code::blocks project for BACnet server simulation * added code::blocks linux project for WhoIs app * update text files for EOL * fix EOL in some files * fixed make win32 apps for older gcc * Removed Borland C++ Makefile in apps. Unable to maintain support for Borland C++ compiler. * created codeblocks project for apps/epics for Windows * fixing ports/xplained to work with new data structure. * fix ports/xplained example for Atmel Studio compile * fix ports/stm32f10x example for gcc Makefile compile * fix ports/stm32f10x example for IAR EWARM compile * fix ports/xplained timer callback * fix ports/bdk_atxx_mspt build with subdirs * fix ports/bdk_atxx_mspt build with subdirs * updated git ignore for IAR build artifacts * updated gitignore for non-tracked files and folders * fixed bdk-atxx4-mstp port for Rowley Crossworks project file * fixed bdk-atxx4-mstp port for GCC AVR Makefile * fixed atmega168 port for IAR AVR and GCC AVR Makefile * fixed at91sam7s port for IAR ARM and GCC ARM Makefile * removed unmaintainable DOS, RTOS32, and atmega8 ports. Updated rx62n (untested). * changed arm7 to uip port
This commit is contained in:
+275
@@ -0,0 +1,275 @@
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
# AR = ar
|
||||
# MAKE = make
|
||||
# SIZE = size
|
||||
#
|
||||
# Assumes rm and cp are available
|
||||
|
||||
# Passing parameters via command line or from Makefile export to this one
|
||||
BACNET_DEFINES ?=
|
||||
BACNET_LIB ?=
|
||||
|
||||
# choose a datalink to build the example applications
|
||||
# Use BACDL=mstp or BACDL=bip and BBMD=server when invoking make
|
||||
|
||||
ifeq (${BACDL_DEFINE},)
|
||||
ifeq (${BACDL},ethernet)
|
||||
BACDL_DEFINE=-DBACDL_ETHERNET=1
|
||||
endif
|
||||
ifeq (${BACDL},arcnet)
|
||||
BACDL_DEFINE=-DBACDL_ARCNET=1
|
||||
endif
|
||||
ifeq (${BACDL},mstp)
|
||||
BACDL_DEFINE=-DBACDL_MSTP=1
|
||||
endif
|
||||
ifeq (${BACDL},bip)
|
||||
BACDL_DEFINE=-DBACDL_BIP=1
|
||||
endif
|
||||
ifeq (${BACDL},bip6)
|
||||
BACDL_DEFINE=-DBACDL_BIP6=1
|
||||
endif
|
||||
ifeq (${BACDL},)
|
||||
BACDL_DEFINE ?= -DBACDL_BIP=1
|
||||
BBMD_DEFINE ?= -DBBMD_ENABLED=1
|
||||
endif
|
||||
|
||||
ifeq (${BBMD},server)
|
||||
BBMD_DEFINE=-DBBMD_ENABLED=1
|
||||
endif
|
||||
ifeq (${BBMD},client)
|
||||
BBMD_DEFINE=-DBBMD_ENABLED=1
|
||||
BBMD_DEFINE=-DBBMD_CLIENT_ENABLED
|
||||
endif
|
||||
endif
|
||||
|
||||
# Define WEAK_FUNC for unsupported or specific compilers
|
||||
BACNET_DEFINES += $(BACDL_DEFINE)
|
||||
BACNET_DEFINES += $(BBMD_DEFINE)
|
||||
BACNET_DEFINES += -DWEAK_FUNC=
|
||||
BACNET_DEFINES += $(MAKE_DEFINE)
|
||||
|
||||
# Choose a BACnet Ports Directory for the example applications target OS
|
||||
# linux, win32, bsd
|
||||
BACNET_PORT ?= linux
|
||||
|
||||
# build in uci integration - use UCI=1 when invoking make
|
||||
ifeq (${UCI},1)
|
||||
BACNET_DEFINES += -DBAC_UCI
|
||||
UCI_LIB_DIR ?= /usr/local/lib
|
||||
BACNET_LIB += -L$(UCI_LIB_DIR),-luci
|
||||
endif
|
||||
# OS specific builds
|
||||
ifeq (${BACNET_PORT},linux)
|
||||
PFLAGS = -pthread
|
||||
TARGET_EXT =
|
||||
SYSTEM_LIB=-lc,-lgcc,-lrt,-lm
|
||||
endif
|
||||
ifeq (${BACNET_PORT},bsd)
|
||||
PFLAGS = -pthread
|
||||
TARGET_EXT =
|
||||
SYSTEM_LIB=-lc,-lm
|
||||
endif
|
||||
ifeq (${BACNET_PORT},win32)
|
||||
TARGET_EXT = .exe
|
||||
SYSTEM_LIB=-lws2_32,-lgcc,-lm,-liphlpapi,-lwinmm
|
||||
BACNET_DEFINES += -D_NO_OLDNAMES
|
||||
endif
|
||||
|
||||
# source file locations
|
||||
BACNET_PORT_DIR = $(realpath ../ports/$(BACNET_PORT))
|
||||
BACNET_SRC_DIR = $(realpath ../src)
|
||||
|
||||
#build for release (default) or debug
|
||||
OPTIMIZATION ?= -Os
|
||||
DEBUGGING ?=
|
||||
WARNINGS ?= -Wall -Wmissing-prototypes
|
||||
# dead code removal
|
||||
ifeq (${BUILD},debug)
|
||||
OPTIMIZATION = -O0
|
||||
DEBUGGING = -g -DDEBUG_ENABLED=1
|
||||
ifeq (${BACDL_DEFINE},-DBACDL_BIP=1)
|
||||
BACNET_DEFINES += -DBIP_DEBUG
|
||||
endif
|
||||
endif
|
||||
|
||||
# If BACNET_DEFINES has not already been set, configure to your needs here
|
||||
BACNET_DEFINES ?=
|
||||
BACNET_DEFINES += -DPRINT_ENABLED=1
|
||||
BACNET_DEFINES += -DBACAPP_ALL
|
||||
BACNET_DEFINES += -DBACFILE
|
||||
BACNET_DEFINES += -DINTRINSIC_REPORTING
|
||||
BACNET_DEFINES += -DBACNET_TIME_MASTER
|
||||
BACNET_DEFINES += -DBACNET_PROPERTY_LISTS=1
|
||||
BACNET_DEFINES += -DBACNET_PROTOCOL_REVISION=17
|
||||
|
||||
# put all the flags together
|
||||
INCLUDES = -I$(BACNET_SRC_DIR) -I$(BACNET_PORT_DIR)
|
||||
CFLAGS += $(WARNINGS) $(DEBUGGING) $(OPTIMIZATION) $(BACNET_DEFINES) $(INCLUDES)
|
||||
LFLAGS += -Wl,$(SYSTEM_LIB)
|
||||
ifneq (${BACNET_LIB},)
|
||||
LFLAGS += -Wl,$(BACNET_LIB)
|
||||
endif
|
||||
# GCC dead code removal
|
||||
CFLAGS += -ffunction-sections -fdata-sections
|
||||
LFLAGS += -Wl,--gc-sections
|
||||
|
||||
PORT_ARCNET_SRC = \
|
||||
$(BACNET_PORT_DIR)/arcnet.c
|
||||
|
||||
PORT_MSTP_SRC = \
|
||||
$(BACNET_PORT_DIR)/rs485.c \
|
||||
$(BACNET_PORT_DIR)/dlmstp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/datalink/mstp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/datalink/mstptext.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/datalink/crc.c
|
||||
|
||||
PORT_ETHERNET_SRC = \
|
||||
$(BACNET_PORT_DIR)/ethernet.c
|
||||
|
||||
PORT_BIP_SRC = \
|
||||
$(BACNET_PORT_DIR)/bip-init.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/datalink/bvlc.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/datalink/bip.c
|
||||
|
||||
PORT_BIP6_SRC = \
|
||||
$(BACNET_PORT_DIR)/bip6.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/bbmd6/h_bbmd6.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/bbmd6/vmac.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/datalink/bvlc6.c
|
||||
|
||||
PORT_ALL_SRC = \
|
||||
$(BACNET_SRC_DIR)/bacnet/datalink/datalink.c \
|
||||
$(PORT_ARCNET_SRC) \
|
||||
$(PORT_MSTP_SRC) \
|
||||
$(PORT_ETHERNET_SRC) \
|
||||
$(PORT_BIP_SRC) \
|
||||
$(PORT_BIP6_SRC)
|
||||
|
||||
ifeq (${BACDL_DEFINE},-DBACDL_BIP=1)
|
||||
BACNET_PORT_SRC = ${PORT_BIP_SRC}
|
||||
endif
|
||||
ifeq (${BACDL_DEFINE},-DBACDL_BIP6=1)
|
||||
BACNET_PORT_SRC = ${PORT_BIP6_SRC}
|
||||
endif
|
||||
ifeq (${BACDL_DEFINE},-DBACDL_MSTP=1)
|
||||
BACNET_PORT_SRC = ${PORT_MSTP_SRC}
|
||||
endif
|
||||
ifeq (${BACDL_DEFINE},-DBACDL_ARCNET=1)
|
||||
BACNET_PORT_SRC = ${PORT_ARCNET_SRC}
|
||||
endif
|
||||
ifeq (${BACDL_DEFINE},-DBACDL_ETHERNET=1)
|
||||
BACNET_PORT_SRC = ${PORT_ETHERNET_SRC}
|
||||
endif
|
||||
ifdef BACDL_ALL
|
||||
BACNET_PORT_SRC = ${PORT_ALL_SRC}
|
||||
endif
|
||||
|
||||
BACNET_PORT_SRC += \
|
||||
$(BACNET_SRC_DIR)/bacnet/datalink/dlenv.c \
|
||||
$(BACNET_PORT_DIR)/mstimer-init.c \
|
||||
$(BACNET_PORT_DIR)/datetime-init.c \
|
||||
|
||||
BACNET_SRC ?= \
|
||||
$(wildcard $(BACNET_SRC_DIR)/bacnet/*.c) \
|
||||
|
||||
BACNET_BASIC_SRC ?= \
|
||||
$(wildcard $(BACNET_SRC_DIR)/bacnet/basic/*.c) \
|
||||
$(wildcard $(BACNET_SRC_DIR)/bacnet/basic/binding/*.c) \
|
||||
$(wildcard $(BACNET_SRC_DIR)/bacnet/basic/sys/*.c) \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/npdu/h_npdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/npdu/s_router.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/tsm/tsm.c
|
||||
|
||||
.EXPORT_ALL_VARIABLES:
|
||||
|
||||
SUBDIRS = readprop writeprop readfile writefile reinit server dcc \
|
||||
whohas whois iam ucov scov timesync epics readpropm readrange \
|
||||
writepropm uptransfer getevent uevent abort error
|
||||
|
||||
ifeq (${BACDL_DEFINE},-DBACDL_BIP=1)
|
||||
SUBDIRS += whoisrouter iamrouter initrouter readbdt
|
||||
endif
|
||||
|
||||
ifeq (${BACNET_PORT},linux)
|
||||
ifneq (${OSTYPE},cygwin)
|
||||
SUBDIRS += mstpcap mstpcrc
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq (${BACNET_PORT},win32)
|
||||
SUBDIRS += mstpcap mstpcrc
|
||||
endif
|
||||
|
||||
.PHONY: all clean
|
||||
TARGETS = all clean
|
||||
|
||||
$(TARGETS): %: $(patsubst %, %.%, $(SUBDIRS))
|
||||
|
||||
$(foreach TGT, $(TARGETS), $(patsubst %, %.$(TGT), $(SUBDIRS))):
|
||||
$(MAKE) -b -C $(subst ., , $@)
|
||||
|
||||
.PHONY: gateway
|
||||
gateway:
|
||||
$(MAKE) -s -b -C gateway
|
||||
|
||||
.PHONY: server
|
||||
server:
|
||||
$(MAKE) -s -b -C $@
|
||||
|
||||
.PHONY: mstpcap
|
||||
mstpcap:
|
||||
$(MAKE) -b -C $@
|
||||
|
||||
.PHONY: mstpcrc
|
||||
mstpcrc:
|
||||
$(MAKE) -b -C $@
|
||||
|
||||
.PHONY: abort
|
||||
abort:
|
||||
$(MAKE) -b -C $@
|
||||
|
||||
.PHONY: dcc
|
||||
dcc:
|
||||
$(MAKE) -b -C $@
|
||||
|
||||
.PHONY: epics
|
||||
epics:
|
||||
$(MAKE) -b -C $@
|
||||
|
||||
.PHONY: error
|
||||
error:
|
||||
$(MAKE) -b -C $@
|
||||
|
||||
.PHONY: getevent
|
||||
getevent:
|
||||
$(MAKE) -b -C $@
|
||||
|
||||
.PHONY: iam
|
||||
iam:
|
||||
$(MAKE) -b -C $@
|
||||
|
||||
.PHONY: iamrouter
|
||||
iamrouter:
|
||||
$(MAKE) -b -C $@
|
||||
|
||||
.PHONY: initrouter
|
||||
initrouter:
|
||||
$(MAKE) -b -C $@
|
||||
|
||||
.PHONY: uevent
|
||||
uevent:
|
||||
$(MAKE) -b -C $@
|
||||
|
||||
.PHONY: router
|
||||
router:
|
||||
$(MAKE) -s -b -C $@
|
||||
|
||||
.PHONY: router-ipv6
|
||||
router-ipv6:
|
||||
$(MAKE) -b -C $@
|
||||
|
||||
.PHONY: writepropm
|
||||
writepropm:
|
||||
$(MAKE) -b -C $@
|
||||
@@ -0,0 +1,50 @@
|
||||
#Makefile to build BACnet Application for the GCC port
|
||||
|
||||
# Executable file name
|
||||
TARGET = bacabort
|
||||
|
||||
# BACnet objects that are used with this app
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/client/device-client.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c
|
||||
BACNET_BASIC_SRC += \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_apdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_noserv.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_whois.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_abort.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_whois.c
|
||||
|
||||
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = $(SRC) $(BACNET_SRC) $(BACNET_BASIC_SRC) $(BACNET_PORT_SRC)
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2016 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h> /* for time */
|
||||
#include <errno.h>
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/version.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/sys/filename.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/datalink/dlenv.h"
|
||||
|
||||
/* parsed command line parameters */
|
||||
static uint8_t Target_Invoke_ID = 1;
|
||||
static uint16_t Target_Abort_Reason;
|
||||
static bool Target_Server = true;
|
||||
/* flag for signalling errors */
|
||||
static bool Error_Detected = false;
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
static 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 [abort-reason [invoke-id [server]]]\n", filename);
|
||||
printf(" [--dnet][--dadr][--mac]\n");
|
||||
printf(" [--version][--help]\n");
|
||||
}
|
||||
|
||||
static void print_help(char *filename)
|
||||
{
|
||||
printf("Send BACnet Abort message to the network.\n");
|
||||
printf(
|
||||
"--mac A\n"
|
||||
"Optional destination 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 destination 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("abort-reason:\n"
|
||||
" number from 0 to 65535\n"
|
||||
"invoke-id:\n"
|
||||
" number from 1 to 255\n"
|
||||
"server:\n"
|
||||
" 0=false, 1=true\n"
|
||||
"Example:\n"
|
||||
"%s 3 2 1\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_Abort_Reason = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else if (target_args == 1) {
|
||||
Target_Invoke_ID = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else if (target_args == 2) {
|
||||
Target_Server = 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_Abort_To_Network(&Handler_Transmit_Buffer[0], &dest, Target_Invoke_ID,
|
||||
Target_Abort_Reason, Target_Server);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#Makefile to build BACnet Application for GCC compiler
|
||||
|
||||
TARGET = bacdcc
|
||||
|
||||
# BACnet objects that are used with this app
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/client/device-client.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c
|
||||
BACNET_BASIC_SRC += \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_apdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_dcc.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_noserv.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_whois.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_dcc.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_whois.c
|
||||
|
||||
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = $(SRC) $(BACNET_SRC) $(BACNET_BASIC_SRC) $(BACNET_PORT_SRC)
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
|
||||
+283
@@ -0,0 +1,283 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 demo for BACnet stack */
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h> /* for time */
|
||||
#include <errno.h>
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/arf.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacport.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/whois.h"
|
||||
#include "bacnet/dcc.h"
|
||||
#include "bacnet/version.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/sys/filename.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/dlenv.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 BACNET_ADDRESS Target_Address;
|
||||
static uint16_t Communication_Timeout_Minutes = 0;
|
||||
static BACNET_COMMUNICATION_ENABLE_DISABLE Communication_State =
|
||||
COMMUNICATION_ENABLE;
|
||||
static char *Communication_Password = NULL;
|
||||
|
||||
static bool Error_Detected = false;
|
||||
|
||||
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;
|
||||
printf("BACnet Error: %s: %s\n", bactext_error_class_name(error_class),
|
||||
bactext_error_code_name(error_code));
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
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\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\n", bactext_reject_reason_name(reject_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
static void MyDeviceCommunicationControlSimpleAckHandler(
|
||||
BACNET_ADDRESS *src, uint8_t invoke_id)
|
||||
{
|
||||
(void)src;
|
||||
(void)invoke_id;
|
||||
printf("DeviceCommunicationControl Acknowledged!\n");
|
||||
}
|
||||
|
||||
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);
|
||||
/* 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 communication so we can shutup when asked */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
|
||||
handler_device_communication_control);
|
||||
/* handle the ack coming back */
|
||||
apdu_set_confirmed_simple_ack_handler(
|
||||
SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
|
||||
MyDeviceCommunicationControlSimpleAckHandler);
|
||||
/* handle any errors coming back */
|
||||
apdu_set_error_handler(
|
||||
SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, MyErrorHandler);
|
||||
apdu_set_abort_handler(MyAbortHandler);
|
||||
apdu_set_reject_handler(MyRejectHandler);
|
||||
}
|
||||
|
||||
static void print_usage(char *filename)
|
||||
{
|
||||
printf("Usage: %s device-instance state [timeout [password]]\n", filename);
|
||||
printf(" [--version][--help]\n");
|
||||
}
|
||||
|
||||
static void print_help(char *filename)
|
||||
{
|
||||
printf(
|
||||
"Send BACnet DeviceCommunicationControl service to device.\n"
|
||||
"\n"
|
||||
"The device-instance can be 0 to %lu.\n"
|
||||
"Possible state values:\n"
|
||||
" 0=enable\n"
|
||||
" 1=disable\n"
|
||||
" 2=disable-initiation\n"
|
||||
"The timeout can be 0 for infinite, or a value in minutes for "
|
||||
"disable.\n"
|
||||
"The optional password is a character string of 1 to 20 characters.\n"
|
||||
"\nExample:\n"
|
||||
"If you want disable Device Communications in Device 123\n"
|
||||
"for 60 minutes with password 'filister', use the following command:\n"
|
||||
"%s 123 1 60 filister\n",
|
||||
(unsigned long)(BACNET_MAX_INSTANCE - 1), filename);
|
||||
}
|
||||
|
||||
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;
|
||||
int argi = 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) 2014 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 (argc < 3) {
|
||||
print_usage(filename);
|
||||
return 0;
|
||||
}
|
||||
/* decode the command line parameters */
|
||||
Target_Device_Object_Instance = strtol(argv[1], NULL, 0);
|
||||
Communication_State = (uint16_t)strtol(argv[2], NULL, 0);
|
||||
/* optional timeout, required if password is included */
|
||||
if (argc > 3) {
|
||||
Communication_Timeout_Minutes = (uint16_t)strtol(argv[3], NULL, 0);
|
||||
}
|
||||
/* optional password */
|
||||
if (argc > 4) {
|
||||
Communication_Password = argv[4];
|
||||
}
|
||||
if (Target_Device_Object_Instance >= BACNET_MAX_INSTANCE) {
|
||||
fprintf(stderr, "device-instance=%u - it must be less than %u\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();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
timeout_seconds = (apdu_timeout() / 1000) * apdu_retries();
|
||||
/* try to bind with the device */
|
||||
found = address_bind_request(
|
||||
Target_Device_Object_Instance, &max_apdu, &Target_Address);
|
||||
if (!found) {
|
||||
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(
|
||||
(uint16_t)((current_seconds - last_seconds) * 1000));
|
||||
}
|
||||
if (Error_Detected)
|
||||
break;
|
||||
/* wait until the device is bound, or timeout and quit */
|
||||
if (!found) {
|
||||
found = address_bind_request(
|
||||
Target_Device_Object_Instance, &max_apdu, &Target_Address);
|
||||
}
|
||||
if (found) {
|
||||
if (invoke_id == 0) {
|
||||
invoke_id = Send_Device_Communication_Control_Request(
|
||||
Target_Device_Object_Instance,
|
||||
Communication_Timeout_Minutes, Communication_State,
|
||||
Communication_Password);
|
||||
} else if (tsm_invoke_id_free(invoke_id)) {
|
||||
break;
|
||||
} else if (tsm_invoke_id_failed(invoke_id)) {
|
||||
fprintf(stderr, "\rError: TSM Timeout!\n");
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
/* try again or abort? */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* increment timer - exit if timed out */
|
||||
elapsed_seconds += (current_seconds - last_seconds);
|
||||
if (elapsed_seconds > timeout_seconds) {
|
||||
printf("\rError: APDU Timeout!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* keep track of time for next check */
|
||||
last_seconds = current_seconds;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#Makefile to build BACnet Application for the Linux Port
|
||||
|
||||
TARGET = bacepics
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/client/device-client.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c
|
||||
BACNET_BASIC_SRC += \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_apdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_noserv.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp_a.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rpm_a.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_whois.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_rpm.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_whois.c
|
||||
|
||||
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = $(SRC) $(BACNET_SRC) $(BACNET_BASIC_SRC) $(BACNET_PORT_SRC)
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<CodeBlocks_project_file>
|
||||
<FileVersion major="1" minor="6" />
|
||||
<Project>
|
||||
<Option title="BACnet EPICS Demo" />
|
||||
<Option pch_mode="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Build>
|
||||
<Target title="Debug">
|
||||
<Option output="bin/Debug/bacepics" prefix_auto="1" extension_auto="1" />
|
||||
<Option object_output="obj/Debug/" />
|
||||
<Option type="1" />
|
||||
<Option compiler="gcc" />
|
||||
<Compiler>
|
||||
<Add option="-g" />
|
||||
</Compiler>
|
||||
</Target>
|
||||
<Target title="Release">
|
||||
<Option output="bin/Release/bacepics" prefix_auto="1" extension_auto="1" />
|
||||
<Option object_output="obj/Release/" />
|
||||
<Option type="1" />
|
||||
<Option compiler="gcc" />
|
||||
<Compiler>
|
||||
<Add option="-O2" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add option="-s" />
|
||||
</Linker>
|
||||
</Target>
|
||||
</Build>
|
||||
<Compiler>
|
||||
<Add option="-Wmissing-declarations" />
|
||||
<Add option="-Wall" />
|
||||
<Add option="-m32" />
|
||||
<Add option="-ffunction-sections" />
|
||||
<Add option="-fdata-sections" />
|
||||
<Add option="-DBACDL_BIP" />
|
||||
<Add option="-DPRINT_ENABLED=1" />
|
||||
<Add option="-DBACAPP_ALL" />
|
||||
<Add option="-DBACFILE" />
|
||||
<Add option="-DBACNET_TIME_MASTER" />
|
||||
<Add option="-DBACNET_PROPERTY_LISTS=1" />
|
||||
<Add option="-DBACNET_PROTOCOL_REVISION=17" />
|
||||
<Add option="-D_NO_OLDNAMES" />
|
||||
<Add directory="." />
|
||||
<Add directory="../../ports/win32" />
|
||||
<Add directory="../../src" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add option="-static" />
|
||||
<Add option="-m32" />
|
||||
<Add option="-Wl,--gc-sections" />
|
||||
<Add library="ws2_32" />
|
||||
<Add library="iphlpapi" />
|
||||
<Add library="gcc" />
|
||||
<Add library="m" />
|
||||
<Add library="winmm" />
|
||||
</Linker>
|
||||
<Unit filename="../../ports/win32/bip-init.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../ports/win32/datetime-init.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../ports/win32/mstimer-init.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/abort.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/access_rule.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/alarm_ack.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/arf.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/assigned_access_rights.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/authentication_factor.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/authentication_factor_format.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/awf.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacaddr.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacapp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacdcode.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacdevobjpropref.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacerror.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacint.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacprop.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacpropstates.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacreal.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacstr.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bactext.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bactimevalue.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/binding/address.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/npdu/h_npdu.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/client/device-client.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/netport.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_apdu.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_iam.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_noserv.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_rp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_rp_a.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_rpm_a.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_whois.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_iam.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_rp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_rpm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_whois.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/sys/bigend.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/sys/debug.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/sys/fifo.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/sys/filename.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/sys/key.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/sys/keylist.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/sys/mstimer.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/sys/ringbuf.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/sys/sbuf.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/tsm/tsm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/cov.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/credential_authentication_factor.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/datalink/bip.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/datalink/bvlc.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/datalink/dlenv.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/datetime.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/dcc.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/event.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/get_alarm_sum.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/getevent.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/iam.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/ihave.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/indtext.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/lighting.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/lso.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/memcopy.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/npdu.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/property.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/proplist.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/ptransfer.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/rd.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/readrange.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/reject.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/rp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/rpm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/timestamp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/timesync.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/whohas.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/whois.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/wp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/wpm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="main.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Extensions>
|
||||
<code_completion />
|
||||
<envvars />
|
||||
<debugger />
|
||||
</Extensions>
|
||||
</Project>
|
||||
</CodeBlocks_project_file>
|
||||
@@ -0,0 +1,104 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef BACEPICS_H_
|
||||
#define BACEPICS_H_
|
||||
|
||||
/** @file epics/bacepics.h Header for tool to generate EPICS-usable output. */
|
||||
|
||||
|
||||
/** @defgroup BACEPICS Tool to generate EPICS-usable output.
|
||||
* @ingroup Demos
|
||||
* This tool will generate a list of Objects and their properties for use in
|
||||
* an EPICS file.
|
||||
* You will still need to provide the front part of the EPICS (see file
|
||||
* demo/server/epics_vts3.tpi) which cannot be easily determined by observation,
|
||||
* but this tool communicates with the test device and does the grunt work of
|
||||
* creating the list of Objects and the supported properties for
|
||||
* each of those Objects.
|
||||
*
|
||||
* Usage:
|
||||
* bacepics [-v] [-p sport] [-t target_mac] device-instance
|
||||
* -v: show values instead of '?'
|
||||
* -p: Use sport for "my" port, instead of 0xBAC0 (BACnet/IP only)
|
||||
* Allows you to communicate with a localhost target.
|
||||
* -t: declare target's MAC instead of using Who-Is to bind to
|
||||
* device-instance. Format is "C0:A8:00:18:BA:C0" (as usual)
|
||||
*
|
||||
* Examples:
|
||||
* ./bacepics -v 1234
|
||||
* where the device instance to be addressed is 1234
|
||||
* and the optional -v prints values out rather than the '?' that
|
||||
* the EPICS format for VTS3 wants.
|
||||
* ./bacepics -p 0xBAC1 -t "7F:0:0:1:BA:C0" 4194303
|
||||
* communicates with the BACnet device on localhost (127.0.0.1), using
|
||||
* port 47809 as "my" source port so it doesn't conflict with
|
||||
* the device's port 47808.
|
||||
*
|
||||
*
|
||||
* The tool follows an optimal approach which will use efficient communication
|
||||
* means if available or else fall back to simple-minded methods.
|
||||
* Starting with the Device Object, the tool will
|
||||
* - Try to fetch ALL the Properties with RPM
|
||||
* - If RPM is not supported, will use coded properties in demo/object folder
|
||||
* - If response is too big to fit (without segmentation), then will fetch
|
||||
* ALL again with array index of 0, which should result mostly in errors
|
||||
* but will provide the list of supported properties.
|
||||
* - If that succeeds, build the list of properties to be accessed. <br>
|
||||
* - If no RPM or failed to get ALL properties from the target device, then
|
||||
* fetch the coded Required and Optional properties from the demo/object
|
||||
* folder for this object type, and use this to build the list of
|
||||
* properties to be accessed.
|
||||
* - If the Fetch All succeeded, print the values for each property
|
||||
* - Otherwise, for each property in the list for this object,
|
||||
* - Request the single property value with ReadProperty (RP)
|
||||
* - From the response, print the property's value
|
||||
* The Device Object will have fetched the Object List property and built a list
|
||||
* of objects from that; use it now to cycle through each other Object and
|
||||
* repeat the above process to get and print out their property values.
|
||||
*/
|
||||
|
||||
/** The allowed States of the bacepics State Machine.
|
||||
* Important to distinguish the request from the response phases as well
|
||||
* as which approach will get all the properties for us.
|
||||
* @ingroup BACEPICS
|
||||
*/
|
||||
typedef enum {
|
||||
/** Initial state to establish a binding with the target device. */
|
||||
INITIAL_BINDING,
|
||||
/** Get selected device information and put out the heading information. */
|
||||
GET_HEADING_INFO, GET_HEADING_RESPONSE, PRINT_HEADING,
|
||||
/** Getting ALL properties and values at once with RPM. */
|
||||
GET_ALL_REQUEST, GET_ALL_RESPONSE,
|
||||
/** Getting ALL properties with array index = 0, just to get the list. */
|
||||
GET_LIST_OF_ALL_REQUEST, GET_LIST_OF_ALL_RESPONSE,
|
||||
/** Processing the properties individually with ReadProperty. */
|
||||
GET_PROPERTY_REQUEST, GET_PROPERTY_RESPONSE,
|
||||
/** Done with this Object; move onto the next. */
|
||||
NEXT_OBJECT
|
||||
} EPICS_STATES;
|
||||
|
||||
|
||||
#endif /* BACEPICS_H_ */
|
||||
+1836
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,47 @@
|
||||
#Makefile to build BACnet Application
|
||||
|
||||
TARGET = bacerror
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/client/device-client.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c
|
||||
BACNET_BASIC_SRC += \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_apdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_noserv.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_whois.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_error.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_whois.c
|
||||
|
||||
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = $(SRC) $(BACNET_SRC) $(BACNET_BASIC_SRC) $(BACNET_PORT_SRC)
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2016 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h> /* for time */
|
||||
#include <errno.h>
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/version.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/sys/filename.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/datalink/dlenv.h"
|
||||
|
||||
/* parsed command line parameters */
|
||||
static uint16_t Target_Error_Class;
|
||||
static uint16_t Target_Error_Code;
|
||||
static uint8_t Target_Invoke_ID = 1;
|
||||
static uint16_t Target_Service = SERVICE_CONFIRMED_READ_PROPERTY;
|
||||
/* flag for signalling errors */
|
||||
static bool Error_Detected = false;
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
static 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 [error-class error-code service-number invoke-id]\n",
|
||||
filename);
|
||||
printf(" [--dnet][--dadr][--mac]\n");
|
||||
printf(" [--version][--help]\n");
|
||||
}
|
||||
|
||||
static void print_help(char *filename)
|
||||
{
|
||||
printf("Send BACnet Error message to the network.\n");
|
||||
printf(
|
||||
"--mac A\n"
|
||||
"Optional destination 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 destination 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("error-class:\n"
|
||||
" number from 0 to 65535\n"
|
||||
"error-code:\n"
|
||||
" number from 0 to 65535\n"
|
||||
"service-number:\n"
|
||||
" number from 0 to 65535 for BACnet Services\n"
|
||||
"invoke-id:\n"
|
||||
" number from 1 to 255\n"
|
||||
"Example:\n"
|
||||
"%s 3 2 1\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_Error_Class = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else if (target_args == 1) {
|
||||
Target_Error_Code = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else if (target_args == 2) {
|
||||
Target_Service = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else if (target_args == 2) {
|
||||
Target_Invoke_ID = 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_Error_To_Network(&Handler_Transmit_Buffer[0], &dest, Target_Invoke_ID,
|
||||
Target_Service, Target_Error_Class, Target_Error_Code);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
#Makefile to build BACnet Gateway Demonstration Application for the Linux Port
|
||||
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
|
||||
# Executable file name
|
||||
TARGET = bacgateway
|
||||
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
# put any overloaded or special built src files here,
|
||||
# so the linker uses these instead of the functions in the library
|
||||
SRCS = main.c \
|
||||
$(BACNET_OBJECT)/gw_device.c \
|
||||
$(BACNET_HANDLER)/h_routed_npdu.c \
|
||||
$(BACNET_HANDLER)/s_router.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)/channel.c \
|
||||
$(BACNET_OBJECT)/command.c \
|
||||
$(BACNET_OBJECT)/csv.c \
|
||||
$(BACNET_OBJECT)/iv.c \
|
||||
$(BACNET_OBJECT)/lc.c \
|
||||
$(BACNET_OBJECT)/lo.c \
|
||||
$(BACNET_OBJECT)/lsp.c \
|
||||
$(BACNET_OBJECT)/ms-input.c \
|
||||
$(BACNET_OBJECT)/mso.c \
|
||||
$(BACNET_OBJECT)/msv.c \
|
||||
$(BACNET_OBJECT)/nc.c \
|
||||
$(BACNET_OBJECT)/netport.c \
|
||||
$(BACNET_OBJECT)/osv.c \
|
||||
$(BACNET_OBJECT)/piv.c \
|
||||
$(BACNET_OBJECT)/schedule.c \
|
||||
$(BACNET_OBJECT)/trendlog.c \
|
||||
$(BACNET_OBJECT)/bacfile.c
|
||||
|
||||
OBJS = ${SRCS:.c=.o}
|
||||
|
||||
DEFINES += -DBAC_ROUTING
|
||||
|
||||
CFLAGS = $(WARNINGS) $(DEBUGGING) $(OPTIMIZATION) $(STANDARDS) $(INCLUDES) $(DEFINES)
|
||||
|
||||
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
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef GATEWAY_H_
|
||||
#define GATEWAY_H_
|
||||
|
||||
/** @file gateway/gateway.h Header for example gateway (ie, BACnet Router
|
||||
* and Devices) using the BACnet Stack. */
|
||||
|
||||
/** @defgroup GatewayDemo Demo of a BACnet virtual gateway (multiple Device).
|
||||
* @ingroup Demos
|
||||
* This is a basic demonstration of a BACnet Router with child devices (ie,
|
||||
* gateway) appearing on a virtual BACnet network behind the Router.
|
||||
* This is an extension of the ServerDemo project.
|
||||
*/
|
||||
|
||||
/* Device configuration definitions. */
|
||||
#define FIRST_DEVICE_NUMBER 260001
|
||||
#define VIRTUAL_DNET 2709 /* your choice of number here */
|
||||
#define DEV_NAME_BASE "Gateway Demo Device"
|
||||
#define DEV_DESCR_GATEWAY "Gateway Device and Router"
|
||||
#define DEV_DESCR_REMOTE "Routed Remote Device"
|
||||
|
||||
|
||||
|
||||
#endif /* GATEWAY_H_ */
|
||||
@@ -0,0 +1,338 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
/**
|
||||
* Code for this project began with code from the demo/server project and
|
||||
* Paul Chapman's vmac project.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include "bacnet/config.h"
|
||||
#include "gateway.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/datalink/dlenv.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/object/bacfile.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/dcc.h"
|
||||
#include "bacport.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/object/lc.h"
|
||||
#include "bacnet/basic/sys/debug.h"
|
||||
#include "bacnet/version.h"
|
||||
/* include the device object */
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#ifdef BACNET_TEST_VMAC
|
||||
#include "bacnet/basic/bbmd6/vmac.h"
|
||||
#endif
|
||||
|
||||
/** @file gateway/main.c Example virtual gateway application using the BACnet
|
||||
* Stack. */
|
||||
|
||||
/* Prototypes */
|
||||
|
||||
/* (Doxygen note: The next two lines pull all the following Javadoc
|
||||
* into the GatewayDemo module.) */
|
||||
/** @addtogroup GatewayDemo */
|
||||
/*@{*/
|
||||
|
||||
/** Buffer used for receiving */
|
||||
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
|
||||
|
||||
/** The list of DNETs that our router can reach.
|
||||
* Only one entry since we don't support downstream routers.
|
||||
*/
|
||||
int DNET_list[2] = {
|
||||
VIRTUAL_DNET, -1 /* Need -1 terminator */
|
||||
};
|
||||
|
||||
/* current version of the BACnet stack */
|
||||
static const char *BACnet_Version = BACNET_VERSION_TEXT;
|
||||
|
||||
/** Initialize the Device Objects and each of the child Object instances.
|
||||
* @param first_object_instance Set the first (gateway) Device to this
|
||||
instance number, and subsequent devices to incremented values.
|
||||
*/
|
||||
static void Devices_Init(uint32_t first_object_instance)
|
||||
{
|
||||
int i;
|
||||
char nameText[MAX_DEV_NAME_LEN];
|
||||
char descText[MAX_DEV_DESC_LEN];
|
||||
BACNET_CHARACTER_STRING name_string;
|
||||
|
||||
/* Gateway Device has already been initialized.
|
||||
* But give it a better Description. */
|
||||
Routed_Device_Set_Description(DEV_DESCR_GATEWAY, strlen(DEV_DESCR_GATEWAY));
|
||||
|
||||
/* Now initialize the remote Device objects. */
|
||||
for (i = 1; i < MAX_NUM_DEVICES; i++) {
|
||||
#ifdef _MSC_VER
|
||||
_snprintf(nameText, MAX_DEV_NAME_LEN, "%s %d", DEV_NAME_BASE, i + 1);
|
||||
_snprintf(descText, MAX_DEV_DESC_LEN, "%s %d", DEV_DESCR_REMOTE, i);
|
||||
#else
|
||||
snprintf(nameText, MAX_DEV_NAME_LEN, "%s %d", DEV_NAME_BASE, i + 1);
|
||||
snprintf(descText, MAX_DEV_DESC_LEN, "%s %d", DEV_DESCR_REMOTE, i);
|
||||
#endif
|
||||
characterstring_init_ansi(&name_string, nameText);
|
||||
|
||||
Add_Routed_Device((first_object_instance + i), &name_string, descText);
|
||||
}
|
||||
}
|
||||
|
||||
/** Initialize the handlers we will utilize.
|
||||
* @see Device_Init, apdu_set_unconfirmed_handler, apdu_set_confirmed_handler
|
||||
*/
|
||||
static void Init_Service_Handlers(uint32_t first_object_instance)
|
||||
{
|
||||
Device_Init(NULL);
|
||||
Routing_Device_Init(first_object_instance);
|
||||
|
||||
/* we need to handle who-is to support dynamic device binding
|
||||
* For the gateway, we will use the unicast variety so we can
|
||||
* get back through switches to different subnets.
|
||||
* Don't need the routed versions, since the npdu handler calls
|
||||
* each device in turn.
|
||||
*/
|
||||
apdu_set_unconfirmed_handler(
|
||||
SERVICE_UNCONFIRMED_WHO_IS, handler_who_is_unicast);
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has);
|
||||
/* 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);
|
||||
/* Set the handlers for any confirmed services that we support. */
|
||||
/* We must implement read property - it's required! */
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_READ_PROPERTY, handler_read_property);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_READ_PROP_MULTIPLE, handler_read_property_multiple);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_WRITE_PROPERTY, handler_write_property);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_READ_RANGE, handler_read_range);
|
||||
#if defined(BACFILE)
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_ATOMIC_READ_FILE, handler_atomic_read_file);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_ATOMIC_WRITE_FILE, handler_atomic_write_file);
|
||||
#endif
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_REINITIALIZE_DEVICE, handler_reinitialize_device);
|
||||
apdu_set_unconfirmed_handler(
|
||||
SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, handler_timesync_utc);
|
||||
apdu_set_unconfirmed_handler(
|
||||
SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, handler_timesync);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_SUBSCRIBE_COV, handler_cov_subscribe);
|
||||
apdu_set_unconfirmed_handler(
|
||||
SERVICE_UNCONFIRMED_COV_NOTIFICATION, handler_ucov_notification);
|
||||
/* handle communication so we can shutup when asked */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
|
||||
handler_device_communication_control);
|
||||
}
|
||||
|
||||
/** Initialize the BACnet Device Addresses for each Device object.
|
||||
* The gateway has already gotten the normal address (eg, PC's IP for BIP) and
|
||||
* the remote devices get
|
||||
* - For BIP, the IP address reversed, and 4th byte equal to index.
|
||||
* (Eg, 11.22.33.44 for the gateway becomes 44.33.22.01 for the first remote
|
||||
* device.) This is sure to be unique! The port number stays the same.
|
||||
* - For MS/TP, [Steve inserts a good idea here]
|
||||
*/
|
||||
static void Initialize_Device_Addresses()
|
||||
{
|
||||
int i = 0; /* First entry is Gateway Device */
|
||||
uint32_t virtual_mac = 0;
|
||||
DEVICE_OBJECT_DATA *pDev = NULL;
|
||||
/* Setup info for the main gateway device first */
|
||||
pDev = Get_Routed_Device_Object(i);
|
||||
#if defined(BACDL_BIP)
|
||||
uint16_t myPort;
|
||||
struct in_addr *netPtr; /* Lets us cast to this type */
|
||||
uint8_t *gatewayMac = NULL;
|
||||
uint32_t myAddr = bip_get_addr();
|
||||
gatewayMac = pDev->bacDevAddr.mac; /* Keep pointer to the main MAC */
|
||||
memcpy(pDev->bacDevAddr.mac, &myAddr, 4);
|
||||
myPort = bip_get_port();
|
||||
memcpy(&pDev->bacDevAddr.mac[4], &myPort, 2);
|
||||
pDev->bacDevAddr.mac_len = 6;
|
||||
#elif defined(BACDL_MSTP)
|
||||
pDev->bacDevAddr.mac_len = 1;
|
||||
pDev->bacDevAddr.mac[0] = dlmstp_mac_address();
|
||||
#else
|
||||
#error "No support for this Data Link Layer type "
|
||||
#endif
|
||||
/* broadcast an I-Am on startup */
|
||||
Send_I_Am(&Handler_Transmit_Buffer[0]);
|
||||
|
||||
for (i = 1; i < MAX_NUM_DEVICES; i++) {
|
||||
pDev = Get_Routed_Device_Object(i);
|
||||
if (pDev == NULL)
|
||||
continue;
|
||||
#if defined(BACDL_BIP)
|
||||
virtual_mac = i;
|
||||
netPtr = (struct in_addr *)pDev->bacDevAddr.mac;
|
||||
#if (MAX_NUM_DEVICES > 0xFFFFFF)
|
||||
pDev->bacDevAddr.mac[0] = ((virtual_mac & 0xff000000) >> 24);
|
||||
#else
|
||||
pDev->bacDevAddr.mac[0] = gatewayMac[3];
|
||||
#endif
|
||||
#if (MAX_NUM_DEVICES > 0xFFFF)
|
||||
pDev->bacDevAddr.mac[1] = ((virtual_mac & 0xff0000) >> 16);
|
||||
#else
|
||||
pDev->bacDevAddr.mac[1] = gatewayMac[2];
|
||||
#endif
|
||||
#if (MAX_NUM_DEVICES > 0xFF)
|
||||
pDev->bacDevAddr.mac[2] = ((virtual_mac & 0xff00) >> 8);
|
||||
#else
|
||||
pDev->bacDevAddr.mac[2] = gatewayMac[1];
|
||||
#endif
|
||||
pDev->bacDevAddr.mac[3] = (virtual_mac & 0xff);
|
||||
memcpy(&pDev->bacDevAddr.mac[4], &myPort, 2);
|
||||
pDev->bacDevAddr.mac_len = 6;
|
||||
pDev->bacDevAddr.net = VIRTUAL_DNET;
|
||||
memcpy(&pDev->bacDevAddr.adr[0], &pDev->bacDevAddr.mac[0], 6);
|
||||
pDev->bacDevAddr.len = 6;
|
||||
printf(" - Routed device [%d] ID %u at %s \n", i,
|
||||
pDev->bacObj.Object_Instance_Number, inet_ntoa(*netPtr));
|
||||
#elif defined(BACDL_MSTP)
|
||||
/* Todo: set MS/TP net and port #s */
|
||||
pDev->bacDevAddr.mac_len = 2;
|
||||
#endif
|
||||
/* broadcast an I-Am for each routed Device now */
|
||||
Send_I_Am(&Handler_Transmit_Buffer[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Main function of server demo.
|
||||
*
|
||||
* @see Device_Set_Object_Instance_Number, dlenv_init, Send_I_Am,
|
||||
* datalink_receive, npdu_handler,
|
||||
* dcc_timer_seconds, bvlc_maintenance_timer,
|
||||
* Load_Control_State_Machine_Handler, handler_cov_task,
|
||||
* tsm_timer_milliseconds
|
||||
*
|
||||
* @param argc [in] Arg count.
|
||||
* @param argv [in] Takes one argument: the Device Instance #.
|
||||
* @return 0 on success.
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
BACNET_ADDRESS src = { 0 }; /* address where message came from */
|
||||
uint16_t pdu_len = 0;
|
||||
unsigned timeout = 1000; /* milliseconds */
|
||||
time_t last_seconds = 0;
|
||||
time_t current_seconds = 0;
|
||||
uint32_t elapsed_seconds = 0;
|
||||
uint32_t elapsed_milliseconds = 0;
|
||||
uint32_t first_object_instance = FIRST_DEVICE_NUMBER;
|
||||
#ifdef BACNET_TEST_VMAC
|
||||
/* Router data */
|
||||
BACNET_DEVICE_PROFILE *device;
|
||||
BACNET_VMAC_ADDRESS adr;
|
||||
#endif
|
||||
|
||||
/* allow the device ID to be set */
|
||||
if (argc > 1) {
|
||||
first_object_instance = strtol(argv[1], NULL, 0);
|
||||
if ((first_object_instance == 0) ||
|
||||
(first_object_instance >= BACNET_MAX_INSTANCE)) {
|
||||
printf("Error: Invalid Object Instance %s \n", argv[1]);
|
||||
printf(
|
||||
"Provide a number from 1 to %ul \n", BACNET_MAX_INSTANCE - 1);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
printf("BACnet Router Demo\n"
|
||||
"BACnet Stack Version %s\n"
|
||||
"BACnet Device ID: %u\n"
|
||||
"Max APDU: %d\n",
|
||||
BACnet_Version, first_object_instance, MAX_APDU);
|
||||
Init_Service_Handlers(first_object_instance);
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
Devices_Init(first_object_instance);
|
||||
Initialize_Device_Addresses();
|
||||
|
||||
#ifdef BACNET_TEST_VMAC
|
||||
/* initialize vmac table and router device */
|
||||
device = vmac_initialize(99, 2001);
|
||||
debug_printf(device->name, "ROUTER:%u", vmac_get_subnet());
|
||||
#endif
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
|
||||
/* broadcast an I-am-router-to-network on startup */
|
||||
printf("Remote Network DNET Number %d \n", DNET_list[0]);
|
||||
Send_I_Am_Router_To_Network(DNET_list);
|
||||
|
||||
/* loop forever */
|
||||
for (;;) {
|
||||
/* input */
|
||||
current_seconds = time(NULL);
|
||||
|
||||
/* returns 0 bytes on timeout */
|
||||
pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
|
||||
|
||||
/* process */
|
||||
if (pdu_len) {
|
||||
routing_npdu_handler(&src, DNET_list, &Rx_Buf[0], pdu_len);
|
||||
}
|
||||
/* at least one second has passed */
|
||||
elapsed_seconds = current_seconds - last_seconds;
|
||||
if (elapsed_seconds) {
|
||||
last_seconds = current_seconds;
|
||||
dcc_timer_seconds(elapsed_seconds);
|
||||
#if defined(BACDL_BIP) && BBMD_ENABLED
|
||||
bvlc_maintenance_timer(elapsed_seconds);
|
||||
#endif
|
||||
dlenv_maintenance_timer(elapsed_seconds);
|
||||
Load_Control_State_Machine_Handler();
|
||||
elapsed_milliseconds = elapsed_seconds * 1000;
|
||||
tsm_timer_milliseconds(elapsed_milliseconds);
|
||||
}
|
||||
handler_cov_task();
|
||||
/* output */
|
||||
|
||||
/* blink LEDs, Turn on or off outputs, etc */
|
||||
}
|
||||
/* Dummy return */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* @} */
|
||||
|
||||
/* End group GatewayDemo */
|
||||
@@ -0,0 +1,50 @@
|
||||
#Makefile to build BACnet Application using GCC compiler
|
||||
|
||||
# Executable file name
|
||||
TARGET = bacge
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/client/device-client.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c
|
||||
BACNET_BASIC_SRC += \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_apdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_getevent.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_noserv.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rr_a.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_whois.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_getevent.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_whois.c
|
||||
|
||||
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = $(SRC) $(BACNET_SRC) $(BACNET_BASIC_SRC) $(BACNET_PORT_SRC)
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
/*************************************************************************
|
||||
* Copyright (C) 2015 B Weitsch
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
/* core stuff needed */
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/arf.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/whois.h"
|
||||
#include "bacnet/getevent.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/sys/filename.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/datalink/dlenv.h"
|
||||
/* some port stuff needed */
|
||||
#include "bacport.h"
|
||||
|
||||
/* Depending on on the max-APDU-length-accepted (varies per device),
|
||||
the amount of event entries in the GetEventInformation ACK can sum
|
||||
up to 24 max (max APDU length is 1476), especially if the flag
|
||||
MORE_EVENTS is true. */
|
||||
#define MAX_OBJ_IDS_IN_GE_ACK 24
|
||||
|
||||
/* buffer used for receive */
|
||||
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
|
||||
|
||||
/* converted command line arguments */
|
||||
static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE;
|
||||
/* the invoke id is needed to filter incoming messages */
|
||||
static uint8_t Request_Invoke_ID = 0;
|
||||
static BACNET_ADDRESS Target_Address;
|
||||
static bool Error_Detected = false;
|
||||
static bool Recieved_Ack = false;
|
||||
static bool More_Events = false;
|
||||
static BACNET_OBJECT_ID LastReceivedObjectIdentifier;
|
||||
|
||||
static void MyErrorHandler(BACNET_ADDRESS *src,
|
||||
uint8_t invoke_id,
|
||||
BACNET_ERROR_CLASS error_class,
|
||||
BACNET_ERROR_CODE error_code)
|
||||
{
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
printf("BACnet Error: %s: %s\r\n",
|
||||
bactext_error_class_name((int)error_class),
|
||||
bactext_error_code_name((int)error_code));
|
||||
Error_Detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void MyAbortHandler(
|
||||
BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t abort_reason, bool server)
|
||||
{
|
||||
(void)server;
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
printf("BACnet Abort: %s\r\n",
|
||||
bactext_abort_reason_name((int)abort_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void MyRejectHandler(
|
||||
BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t reject_reason)
|
||||
{
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
printf("BACnet Reject: %s\r\n",
|
||||
bactext_reject_reason_name((int)reject_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Handler for a GetEvent ACK.
|
||||
* Print out the ACK data of a matching request and check if we need
|
||||
* to repeat or GetEventInformation request.
|
||||
*
|
||||
* @param service_request [in] The contents of the service request.
|
||||
* @param service_len [in] The length of the service_request.
|
||||
* @param src [in] BACNET_ADDRESS of the source of the message
|
||||
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
|
||||
* decoded from the APDU header of this message.
|
||||
*/
|
||||
static void My_Get_Event_Ack_Handler(uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS *src,
|
||||
BACNET_CONFIRMED_SERVICE_ACK_DATA *service_data)
|
||||
{
|
||||
int len = 0;
|
||||
int i;
|
||||
BACNET_GET_EVENT_INFORMATION_DATA data[MAX_OBJ_IDS_IN_GE_ACK];
|
||||
for (i = 0; i < MAX_OBJ_IDS_IN_GE_ACK - 1; i++)
|
||||
data[i].next = &data[i + 1];
|
||||
|
||||
printf("Recieved Ack. Saved invoke ID was %i, service returned %i\n",
|
||||
Request_Invoke_ID, service_data->invoke_id);
|
||||
|
||||
if (service_data->invoke_id == Request_Invoke_ID) {
|
||||
len = getevent_ack_decode_service_request(
|
||||
service_request, service_len, &data[0], &More_Events);
|
||||
printf("Decode of Ack returned length %i. MoreEvents flag was %i \n",
|
||||
len, More_Events);
|
||||
if (len > 0) {
|
||||
ge_ack_print_data(&(data[0]), Target_Device_Object_Instance);
|
||||
if (More_Events) {
|
||||
BACNET_GET_EVENT_INFORMATION_DATA *lastData = &(data[0]);
|
||||
while (lastData->next)
|
||||
lastData = lastData->next;
|
||||
LastReceivedObjectIdentifier = lastData->objectIdentifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
Recieved_Ack = 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);
|
||||
/* 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 getevent - it's required! */
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_GET_EVENT_INFORMATION, handler_get_event_information);
|
||||
/* handle the data coming back from confirmed requests */
|
||||
apdu_set_confirmed_ack_handler(
|
||||
SERVICE_CONFIRMED_GET_EVENT_INFORMATION, My_Get_Event_Ack_Handler);
|
||||
/* 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 int print_help(char *exe_name)
|
||||
{
|
||||
printf("Usage:\n"
|
||||
"\n"
|
||||
"%s device-instance [--help]\n"
|
||||
"\n"
|
||||
" Send BACnet GetEventInformation service retruequest to given "
|
||||
"device, and wait\n"
|
||||
" for responses.\n\n",
|
||||
exe_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
LastReceivedObjectIdentifier.instance = 0;
|
||||
LastReceivedObjectIdentifier.type = 0;
|
||||
|
||||
bool found = false;
|
||||
if (argc <= 1) {
|
||||
printf("Usage: %s device-instance\r\n", filename_remove_path(argv[0]));
|
||||
return 0;
|
||||
} else if (strcmp(argv[1], "--help") == 0) {
|
||||
print_help(filename_remove_path(argv[0]));
|
||||
return 0;
|
||||
}
|
||||
/* decode the command line parameter */
|
||||
Target_Device_Object_Instance = strtol(argv[1], NULL, 0);
|
||||
/* setup my info */
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
address_init();
|
||||
Init_Service_Handlers();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
timeout_seconds = (apdu_timeout() / 1000) * apdu_retries();
|
||||
/* try to bind with the device */
|
||||
found = address_bind_request(
|
||||
Target_Device_Object_Instance, &max_apdu, &Target_Address);
|
||||
if (!found) {
|
||||
Send_WhoIs(
|
||||
Target_Device_Object_Instance, Target_Device_Object_Instance);
|
||||
}
|
||||
/* loop forever */
|
||||
for (;;) {
|
||||
/* increment timer - exit if timed out */
|
||||
current_seconds = time(NULL);
|
||||
|
||||
/* at least one second has passed */
|
||||
if (current_seconds != last_seconds) {
|
||||
tsm_timer_milliseconds(
|
||||
(uint16_t)((current_seconds - last_seconds) * 1000));
|
||||
}
|
||||
|
||||
if (Error_Detected) {
|
||||
break;
|
||||
}
|
||||
/* wait until the device is bound, or timeout and quit */
|
||||
if (!found) {
|
||||
found = address_bind_request(
|
||||
Target_Device_Object_Instance, &max_apdu, &Target_Address);
|
||||
}
|
||||
if (found) {
|
||||
if (Request_Invoke_ID == 0) {
|
||||
printf("\nSending first GetEventInformation request ...\n");
|
||||
Request_Invoke_ID = Send_GetEvent(&Target_Address, NULL);
|
||||
} else if (More_Events) {
|
||||
printf("\nSending another GetEventInformation request ...\n");
|
||||
Request_Invoke_ID = Send_GetEvent(
|
||||
&Target_Address, &LastReceivedObjectIdentifier);
|
||||
More_Events = false;
|
||||
} else if (tsm_invoke_id_free(Request_Invoke_ID)) {
|
||||
if (Recieved_Ack) {
|
||||
break;
|
||||
}
|
||||
} else if (tsm_invoke_id_failed(Request_Invoke_ID)) {
|
||||
fprintf(stderr, "\rError: TSM Timeout!\r\n");
|
||||
tsm_free_invoke_id(Request_Invoke_ID);
|
||||
Error_Detected = true;
|
||||
/* try again or abort? */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* increment timer - exit if timed out */
|
||||
elapsed_seconds += (current_seconds - last_seconds);
|
||||
if (elapsed_seconds > timeout_seconds) {
|
||||
printf("\rError: APDU Timeout!\r\n");
|
||||
Error_Detected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* keep track of time for next check */
|
||||
last_seconds = current_seconds;
|
||||
}
|
||||
if (Error_Detected)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#Makefile to build BACnet Application for the Linux Port
|
||||
|
||||
TARGET = baciam
|
||||
# BACnet objects that are used with this app
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/client/device-client.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c
|
||||
BACNET_BASIC_SRC += \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_apdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_noserv.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_whois.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_whois.c
|
||||
|
||||
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = $(SRC) $(BACNET_SRC) $(BACNET_BASIC_SRC) $(BACNET_PORT_SRC)
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2016 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h> /* for time */
|
||||
#include <errno.h>
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/version.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/sys/filename.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/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;
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
static 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(" [--dnet][--dadr][--mac]\n");
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#Makefile to build BACnet Application for the Linux Port
|
||||
|
||||
TARGET = baciamr
|
||||
# BACnet objects that are used with this app
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/client/device-client.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c
|
||||
BACNET_BASIC_SRC += \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_apdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_noserv.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_whois.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_whois.c
|
||||
|
||||
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = $(SRC) $(BACNET_SRC) $(BACNET_BASIC_SRC) $(BACNET_PORT_SRC)
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2008 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h> /* for time */
|
||||
#include <errno.h>
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/version.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/sys/filename.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/dlenv.h"
|
||||
|
||||
/* 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;
|
||||
|
||||
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\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\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 DNET [DNET] [DNET] [...]\n", filename);
|
||||
printf(" [--version][--help]\n");
|
||||
}
|
||||
|
||||
static void print_help(char *filename)
|
||||
{
|
||||
printf("Send BACnet I-Am-Router-To-Network message for \n"
|
||||
"one or more networks.\n"
|
||||
"\nDNET:\n"
|
||||
"BACnet destination network number 0-65534\n"
|
||||
"To send a I-Am-Router-To-Network message for DNET 86:\n"
|
||||
"%s 86\n"
|
||||
"To send a I-Am-Router-To-Network message for multiple DNETs\n"
|
||||
"use the following command:\n"
|
||||
"%s 86 42 24 14\n",
|
||||
filename, filename);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned arg_count = 0;
|
||||
int argi = 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);
|
||||
exit(0);
|
||||
}
|
||||
if (strcmp(argv[argi], "--version") == 0) {
|
||||
printf("%s %s\n", filename, BACNET_VERSION_TEXT);
|
||||
printf("Copyright (C) 2014 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");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
if (argc < 2) {
|
||||
print_usage(filename);
|
||||
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!\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\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();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
/* send the request */
|
||||
Send_I_Am_Router_To_Network(Target_Router_Networks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#Makefile to build BACnet Application for the Linux Port
|
||||
|
||||
TARGET = bacinitr
|
||||
# BACnet objects that are used with this app
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/client/device-client.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c
|
||||
BACNET_BASIC_SRC += \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_apdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_noserv.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_whois.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_whois.c
|
||||
|
||||
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = $(SRC) $(BACNET_SRC) $(BACNET_BASIC_SRC) $(BACNET_PORT_SRC)
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
|
||||
@@ -0,0 +1,384 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2008 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h> /* for time */
|
||||
#include <errno.h>
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/version.h"
|
||||
/* some demo stuff needed */
|
||||
#ifndef DEBUG_ENABLED
|
||||
#define DEBUG_ENABLED 0
|
||||
#endif
|
||||
#include "bacnet/basic/sys/debug.h"
|
||||
#include "bacnet/basic/sys/filename.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/dlenv.h"
|
||||
|
||||
/* buffer used for receive */
|
||||
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
|
||||
|
||||
/* target address */
|
||||
static BACNET_ADDRESS Target_Router_Address;
|
||||
/* static BACNET_ROUTER_PORT *Target_Router_Port_List; */
|
||||
|
||||
#define VIRTUAL_DNET 2709 /* your choice of number here */
|
||||
/** The list of DNETs that our router can reach.
|
||||
* Only one entry since we don't support downstream routers.
|
||||
*/
|
||||
int DNET_list[2] = {
|
||||
VIRTUAL_DNET, -1 /* Need -1 terminator */
|
||||
};
|
||||
|
||||
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\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\n", bactext_reject_reason_name(reject_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
static void My_Router_Handler(BACNET_ADDRESS *src,
|
||||
BACNET_NPDU_DATA *npdu_data,
|
||||
uint8_t *npdu, /* PDU data */
|
||||
uint16_t npdu_len)
|
||||
{
|
||||
uint16_t npdu_offset = 0;
|
||||
uint16_t dnet = 0;
|
||||
uint16_t len = 0;
|
||||
uint16_t j = 0;
|
||||
unsigned port_mappings = 0;
|
||||
unsigned port_id = 0;
|
||||
unsigned port_info_len = 0;
|
||||
|
||||
switch (npdu_data->network_message_type) {
|
||||
case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK:
|
||||
break;
|
||||
case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK:
|
||||
break;
|
||||
case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK:
|
||||
break;
|
||||
case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK:
|
||||
break;
|
||||
case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK:
|
||||
break;
|
||||
case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK:
|
||||
break;
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE:
|
||||
break;
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
|
||||
printf("Initialize-Routing-Table-Ack from ");
|
||||
for (j = 0; j < MAX_MAC_LEN; j++) {
|
||||
if (j < src->mac_len) {
|
||||
printf("%02X", src->mac[j]);
|
||||
}
|
||||
}
|
||||
if (npdu_len >= 1) {
|
||||
port_mappings = npdu[npdu_offset];
|
||||
printf("\nPort Mappings: %u\n", port_mappings);
|
||||
npdu_offset++;
|
||||
npdu_len--;
|
||||
}
|
||||
while (npdu_len) {
|
||||
if (npdu_len >= 2) {
|
||||
len = decode_unsigned16(&npdu[npdu_offset], &dnet);
|
||||
printf("DNET=%hu, ", dnet);
|
||||
npdu_offset += len;
|
||||
npdu_len -= len;
|
||||
}
|
||||
if (npdu_len >= 1) {
|
||||
port_id = npdu[npdu_offset];
|
||||
printf("Port ID=%u, ", port_id);
|
||||
npdu_offset++;
|
||||
npdu_len--;
|
||||
}
|
||||
if (npdu_len >= 1) {
|
||||
port_info_len = npdu[npdu_offset];
|
||||
printf("Port Info Length=%u, ", port_info_len);
|
||||
npdu_offset++;
|
||||
npdu_len--;
|
||||
} else {
|
||||
port_info_len = 0;
|
||||
}
|
||||
if (port_info_len) {
|
||||
printf("Port Info=\"");
|
||||
while (port_info_len) {
|
||||
if (npdu_len >= 1) {
|
||||
printf("%02X", npdu[npdu_offset]);
|
||||
npdu_offset++;
|
||||
npdu_len--;
|
||||
}
|
||||
}
|
||||
printf("\"");
|
||||
}
|
||||
if (npdu_len) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
break;
|
||||
case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK:
|
||||
break;
|
||||
case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void My_NPDU_Handler(BACNET_ADDRESS *src, /* source address */
|
||||
uint8_t *pdu, /* PDU data */
|
||||
uint16_t pdu_len)
|
||||
{ /* length PDU */
|
||||
int apdu_offset = 0;
|
||||
BACNET_ADDRESS dest = { 0 };
|
||||
BACNET_NPDU_DATA npdu_data = { 0 };
|
||||
|
||||
apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data);
|
||||
if (npdu_data.network_layer_message) {
|
||||
if (apdu_offset <= pdu_len) {
|
||||
My_Router_Handler(src, &npdu_data, &pdu[apdu_offset],
|
||||
(uint16_t)(pdu_len - apdu_offset));
|
||||
}
|
||||
} else if ((apdu_offset > 0) && (apdu_offset <= pdu_len)) {
|
||||
if ((npdu_data.protocol_version == BACNET_PROTOCOL_VERSION) &&
|
||||
((dest.net == 0) || (dest.net == BACNET_BROADCAST_NETWORK))) {
|
||||
/* only handle the version that we know how to handle */
|
||||
/* and we are not a router, so ignore messages with
|
||||
routing information cause they are not for us */
|
||||
apdu_handler(
|
||||
src, &pdu[apdu_offset], (uint16_t)(pdu_len - apdu_offset));
|
||||
} else {
|
||||
if (dest.net) {
|
||||
debug_printf("NPDU: DNET=%d. Discarded!\n", dest.net);
|
||||
} else {
|
||||
debug_printf("NPDU: BACnet Protocol Version=%d. Discarded!\n",
|
||||
npdu_data.protocol_version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
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 address [DNET ID Len Info]\n", filename);
|
||||
printf(" [--version][--help]\n");
|
||||
}
|
||||
|
||||
static void print_help(char *filename)
|
||||
{
|
||||
printf(
|
||||
"Send BACnet Initialize-Routing-Table message to a network\n"
|
||||
"and wait for responses. Displays their network information.\n"
|
||||
"\n"
|
||||
"address:\n"
|
||||
"MAC address in xx:xx:xx:xx:xx:xx format or IP x.x.x.x:port\n"
|
||||
"DNET ID Len Info:\n"
|
||||
"Port-info data:\n"
|
||||
" DNET:\n"
|
||||
" Destination network number 0-65534\n"
|
||||
" ID:\n"
|
||||
" Port Identifier number 0-255\n"
|
||||
" Info:\n"
|
||||
" Octet string of data, up to 255 octets\n"
|
||||
"To query the complete routing table, do not include any port-info.\n"
|
||||
"To query using Initialize-Routing-Table message to 192.168.0.18:\n"
|
||||
"%s 192.168.0.18:47808\n",
|
||||
filename);
|
||||
}
|
||||
|
||||
static void address_parse(BACNET_ADDRESS *dst, int argc, char *argv[])
|
||||
{
|
||||
unsigned mac[6];
|
||||
unsigned port;
|
||||
int count = 0;
|
||||
int index = 0;
|
||||
|
||||
if (argc > 0) {
|
||||
count = sscanf(argv[0], "%u.%u.%u.%u:%u", &mac[0], &mac[1], &mac[2],
|
||||
&mac[3], &port);
|
||||
if (count == 5) {
|
||||
dst->mac_len = 6;
|
||||
for (index = 0; index < 4; index++) {
|
||||
dst->mac[index] = mac[index];
|
||||
}
|
||||
encode_unsigned16(&dst->mac[4], port);
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dst->net = 0;
|
||||
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 */
|
||||
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;
|
||||
int argi = 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);
|
||||
exit(0);
|
||||
}
|
||||
if (strcmp(argv[argi], "--version") == 0) {
|
||||
printf("%s %s\n", filename, BACNET_VERSION_TEXT);
|
||||
printf("Copyright (C) 2014 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");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
if (argc < 2) {
|
||||
print_usage(filename);
|
||||
return 0;
|
||||
}
|
||||
if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) {
|
||||
return 0;
|
||||
}
|
||||
/* decode the command line parameters */
|
||||
address_parse(&Target_Router_Address, argc - 1, &argv[1]);
|
||||
if (argc > 2) {
|
||||
/* FIXME: add port info parse */
|
||||
/* BACNET_ROUTER_PORT *router_port_list
|
||||
Target_Router_Port_List
|
||||
ports_parse(&router_port[0], argc-2, &argv[2]);
|
||||
Target_Router_Port_List = router_port[0];
|
||||
*/
|
||||
}
|
||||
/* 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 */
|
||||
Send_Initialize_Routing_Table(&Target_Router_Address, DNET_list);
|
||||
/* 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) {
|
||||
My_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;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#Makefile to build BACnet Application
|
||||
|
||||
# Executable file name
|
||||
TARGET = mstpcap
|
||||
|
||||
# BACNET_PORT, BACNET_PORT_DIR, BACNET_PORT_SRC are defined in common Makefile
|
||||
# BACNET_SRC_DIR is defined in common apps Makefile
|
||||
SRCS = main.c \
|
||||
${BACNET_PORT_DIR}/rs485.c \
|
||||
${BACNET_PORT_DIR}/mstimer-init.c \
|
||||
${BACNET_SRC_DIR}/bacnet/bacdcode.c \
|
||||
${BACNET_SRC_DIR}/bacnet/bacint.c \
|
||||
${BACNET_SRC_DIR}/bacnet/bacreal.c \
|
||||
${BACNET_SRC_DIR}/bacnet/bacstr.c \
|
||||
${BACNET_SRC_DIR}/bacnet/iam.c \
|
||||
${BACNET_SRC_DIR}/bacnet/indtext.c \
|
||||
${BACNET_SRC_DIR}/bacnet/npdu.c \
|
||||
${BACNET_SRC_DIR}/bacnet/basic/sys/debug.c \
|
||||
${BACNET_SRC_DIR}/bacnet/basic/sys/fifo.c \
|
||||
${BACNET_SRC_DIR}/bacnet/basic/sys/filename.c \
|
||||
${BACNET_SRC_DIR}/bacnet/basic/sys/mstimer.c \
|
||||
${BACNET_SRC_DIR}/bacnet/basic/sys/ringbuf.c \
|
||||
${BACNET_SRC_DIR}/bacnet/datalink/mstp.c \
|
||||
${BACNET_SRC_DIR}/bacnet/datalink/mstptext.c \
|
||||
${BACNET_SRC_DIR}/bacnet/datalink/crc.c
|
||||
|
||||
# This demo seems to be a little unique
|
||||
DEFINES = $(BACNET_DEFINES) -DBACDL_MSTP
|
||||
|
||||
# BACNET_PORT, BACNET_PORT_DIR, BACNET_PORT_SRC are defined in common Makefile
|
||||
# BACNET_SRC_DIR is defined in common apps Makefile
|
||||
# WARNINGS, DEBUGGING, OPTIMIZATION are defined in common apps Makefile
|
||||
# BACNET_DEFINES is defined in common apps Makefile
|
||||
# put all the flags together
|
||||
INCLUDES = -I$(BACNET_SRC_DIR) -I$(BACNET_PORT_DIR)
|
||||
CFLAGS += $(WARNINGS) $(DEBUGGING) $(OPTIMIZATION) $(BACNET_DEFINES) $(INCLUDES)
|
||||
LFLAGS += -Wl,$(SYSTEM_LIB)
|
||||
ifneq (${BACNET_LIB},)
|
||||
LFLAGS += -Wl,$(BACNET_LIB)
|
||||
endif
|
||||
# GCC dead code removal
|
||||
CFLAGS += -ffunction-sections -fdata-sections
|
||||
LFLAGS += -Wl,--gc-sections
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
+1204
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,211 @@
|
||||
BACnet MS/TP Capture Tool
|
||||
|
||||
This tool captures BACnet MS/TP packets on an RS485 serial interface,
|
||||
and saves the packets to a file in Wireshark PCAP format for
|
||||
the BACnet MS/TP dissector to read. The filename has a date and time
|
||||
code in it, and will contain up to 65535 packets. A new file
|
||||
will be created at each 65535 packet interval. The tool can
|
||||
be stopped by using Control-C. The tool can also pipe its output
|
||||
to Wireshark to be monitored in real-time.
|
||||
|
||||
Here is a sample of the tool running (use CTRL-C to quit):
|
||||
D:\code\bacnet-stack>bin\mstpcap.exe com54 38400
|
||||
Adjusted interface name to \\.\COM54
|
||||
mstpcap: Using \\.\COM54 for capture at 38400 bps.
|
||||
mstpcap: saving capture to mstp_20110413134119.cap
|
||||
1156 packets
|
||||
==== MS/TP Frame Counts ====
|
||||
MAC Device Tokens PFM RPFM DER Postpd DNER TestReq TestRsp
|
||||
0 - 188 4 0 0 0 0 0 0
|
||||
2 - 189 0 0 0 0 0 0 0
|
||||
3 - 189 9 0 0 0 0 0 0
|
||||
7 - 189 60 0 0 0 0 0 0
|
||||
35 - 188 140 0 0 0 0 0 0
|
||||
Node Count: 5
|
||||
|
||||
==== MS/TP Usage and Timing Maximums ====
|
||||
MAC MaxMstr Retries Npoll Self Treply Tusage Trpfm Tder Tpostpd
|
||||
0 1 0 52 0 11 24 0 0 0
|
||||
2 0 0 0 0 23 0 0 0 0
|
||||
3 6 0 50 0 5 100 0 0 0
|
||||
7 34 0 52 0 5 34 0 0 0
|
||||
35 127 0 50 0 6 63 0 0 0
|
||||
Node Count: 5
|
||||
Invalid Frame Count: 0
|
||||
|
||||
The files that are captured can also be scanned to give some statistics:
|
||||
D:\code\bacnet-stack>bin\mstpcap.exe --scan mstp_20110413134119.cap
|
||||
Scanning mstp_20110413134119.cap
|
||||
1156 packets
|
||||
==== MS/TP Frame Counts ====
|
||||
MAC Device Tokens PFM RPFM DER Postpd DNER TestReq TestRsp
|
||||
0 - 188 4 0 0 0 0 0 0
|
||||
2 - 189 0 0 0 0 0 0 0
|
||||
3 - 189 9 0 0 0 0 0 0
|
||||
7 - 189 60 0 0 0 0 0 0
|
||||
35 - 188 140 0 0 0 0 0 0
|
||||
Node Count: 5
|
||||
|
||||
==== MS/TP Usage and Timing Maximums ====
|
||||
MAC MaxMstr Retries Npoll Self Treply Tusage Trpfm Tder Tpostpd
|
||||
0 1 0 52 0 11 24 0 0 0
|
||||
2 0 0 0 0 23 0 0 0 0
|
||||
3 6 0 50 0 5 100 0 0 0
|
||||
7 34 0 52 0 5 34 0 0 0
|
||||
35 127 0 50 0 6 63 0 0 0
|
||||
Node Count: 5
|
||||
Invalid Frame Count: 0
|
||||
|
||||
The BACnet MS/TP capture tool also includes statistics which are
|
||||
listed for any MAC addresses found passing a token,
|
||||
or any MAC address replying to a DER message.
|
||||
The statistics are emitted when Control-C is pressed, or when
|
||||
65535 packets are captured and the new file is created.
|
||||
The statistics are cleared when the new file is created.
|
||||
The statistics can be emitted from a file using the "--scan" option.
|
||||
|
||||
The MS/TP Frame counts use the following abbreviations:
|
||||
|
||||
Device = Device ID when an I-Am is seen in a capture (trigger with Who-Is).
|
||||
|
||||
Tokens = number of Token frames sent from this MAC address.
|
||||
|
||||
PFM = number of Poll-For-Master frames sent from this MAC address.
|
||||
|
||||
RPFM = number of Reply-To-Poll-For-Master frames sent from this MAC address.
|
||||
|
||||
DER = number of Data-Expecting-Reply frames sent from this MAC address.
|
||||
|
||||
Postpd = number of Reply-Postponed frames sent from this MAC address.
|
||||
|
||||
DNER = number of Data-Not-Expecting-Reply frames sent from this MAC address.
|
||||
|
||||
TestReq = number of Test-Request frames sent from this MAC address.
|
||||
|
||||
TestRsp = number of Test-Response frames sent from this MAC address.
|
||||
|
||||
The MS/TP Usage and Timing Maximums use the following abbreviations:
|
||||
|
||||
MaxMstr = highest destination MAC address during PFM
|
||||
|
||||
Retries = number of second tokens sent to this MAC address.
|
||||
|
||||
Npoll = number of Tokens between Poll-For-Master
|
||||
|
||||
Self/TT = number of Tokens sent to self (Addendum 135-2008v) and
|
||||
number of tardy tokens sent late.
|
||||
|
||||
Treply = maximum number of milliseconds it took to reply with
|
||||
a token after receiving a token. Treply is required to be less
|
||||
than 25ms (but the mstpcap tool may not have that good of
|
||||
resolution on Windows).
|
||||
|
||||
Tusage = the maximum number of milliseconds the
|
||||
device waits for a ReplyToPollForMaster or Token retry.
|
||||
Tusage is required to be between 20ms and 100ms.
|
||||
|
||||
Trpfm = maximum number of milliseconds to respond to PFM with RPFM. It is
|
||||
required to be less than 25ms.
|
||||
|
||||
Tder = maximum number of milliseconds that a device takes to
|
||||
respond to a DataExpectingReply request. Tder is required to be less
|
||||
than 250ms.
|
||||
|
||||
Tpostpd = maximum number of milliseconds to respond to
|
||||
DataExpectingReply request with ReplyPostponed. Tpostpd is
|
||||
required to be less than 250ms.
|
||||
|
||||
==== FTDI chip RS-485 converter 76800 baud tricks ====
|
||||
|
||||
If you are using FTDI chip in your RS485 converter, you can
|
||||
alias an existing baud rate on Windows in the FTDIPORT.INF file
|
||||
in order to acheive non-standard 76800 bps:
|
||||
HKR,,"ConfigData",1,11,00,3F,3F,27,C0,00,00,27,00,00,00,C4,09,00,00,E2,04,00,00,71,02,00,00,38,41,00,00,9C,80,00,00,4E,C0,00,00,34,00,00,00,1A,00,00,00,0D,00,00,00,06,40,00,00,03,80,00,00,00,00,00,00,D0,80,00,00
|
||||
|
||||
replace the 10,27,00,00 => divisor = 10000, rate = 300 bps alias
|
||||
|
||||
hex values actual
|
||||
----------- ---------
|
||||
27,C0,00,00 - 76923 bps => divisor=39.125
|
||||
27,00,00,00 - 76677 bps => divisor=39.000
|
||||
|
||||
Windows XP (from koby3101)
|
||||
1) Plug in and locate your USB/RSS85 in Device Manager under ports. Right click
|
||||
on it and select Properties. Click Details tab and from the drop down select
|
||||
Device Instance Id.
|
||||
|
||||
2) Click Start, Run and then type regedit.
|
||||
Follow this path in the registry
|
||||
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Enum\FTDIBUS
|
||||
|
||||
Locate the folder that has the same name as what you found earlier Device Instance
|
||||
Id in step 1. Click on 0000 folder and then Device Parameters. On the right
|
||||
side you will see ConfigData. Right click and select Modify Binary Data.
|
||||
Locate the 10 27 which in my case were in 5th and 6th position and replace with
|
||||
27 C0.
|
||||
|
||||
This will make the RS485 go to 76800 baud (76923 baud) baud when you ask it
|
||||
to be 300 baud.
|
||||
|
||||
So to capture at 76800 baud type: mstpcap.exe COM2 300
|
||||
|
||||
Linux (used with Debian Lenny and Fedora 15)
|
||||
http://www.connecttech.com/KnowledgeDatabase/kdb309.htm
|
||||
As root:
|
||||
Change USB so I can use it later as normal user:
|
||||
# chmod 777 /dev/ttyUSB0 -
|
||||
Print current info about the device:
|
||||
# setserial /dev/ttyUSB0 –a
|
||||
/dev/ttyUSB0, Line 0, UART: unknown, Port: 0x0000, IRQ: 0
|
||||
Baud_base: 24000000, close_delay: 0, divisor: 0
|
||||
closing_wait: infinte
|
||||
Flags: spd_normal low_latency
|
||||
|
||||
Make custom speed:
|
||||
# setserial /dev/ttyUSB0 spd_cust
|
||||
24000000/312 gives 76923 baudrate:
|
||||
# setserial /dev/ttyUSB0 divisor 312
|
||||
Print to make sure changes got applied:
|
||||
# setserial /dev/ttyUSB0 –a
|
||||
/dev/ttyUSB0, Line 0, UART: unknown, Port: 0x0000, IRQ: 0
|
||||
Baud_base: 24000000, close_delay: 0, divisor: 312
|
||||
closing_wait: infinte
|
||||
Flags: spd_cust low_latency
|
||||
|
||||
Now as normal user running the mstpcap which uses the default 38400 baud it
|
||||
will actually capture at 76800 baud. (76923)
|
||||
|
||||
Just navigate (cd bin) to bin folder in the project and type:
|
||||
|
||||
$ ./mstpcap
|
||||
|
||||
==== Named Pipe direct to Wireshark ====
|
||||
|
||||
Use the named pipe option to send the capture output directly to Wireshark.
|
||||
On Windows, use \\.\pipe\wireshark as the name, and set that name as the
|
||||
interface name in Wireshark. On Linux, the named pipe name can be just about
|
||||
any file name, such as /tmp/wireshark. See:
|
||||
http://wiki.wireshark.org/CaptureSetup/Pipes
|
||||
|
||||
==== EXTCAP direct from Wireshark ====
|
||||
|
||||
To use extcap, run Wireshark and go to the About-dialog.
|
||||
Find a tab located there named "Folders".
|
||||
Locate the extcap search path.
|
||||
Copy the mstpcap.exe to that folder, which may not exist.
|
||||
Restart Wireshark, and look for "BACnet MS/TP on COMx" interfaces.
|
||||
Configure the interface to change baud rate.
|
||||
Capture directly from the interface.
|
||||
|
||||
==== Developer Info about Wireshark ExtCap ====
|
||||
|
||||
BACnet MS/TP uses a DLT (data link type) requested from and
|
||||
approved by libPCAP development team, which is 165. Wireshark
|
||||
includes a dissector that decodes the MS/TP packets when they
|
||||
use the DLT for MS/TP. Other DLTs exist for other types, and
|
||||
there are also DLTs set aside as USER DLTs.
|
||||
|
||||
See the following URLs for information about ExtCap:
|
||||
https://github.com/wireshark/wireshark/blob/master/doc/README.extcap
|
||||
https://github.com/wireshark/wireshark/blob/master/doc/extcap_example.py
|
||||
https://wiki.wireshark.org/Development/Extcap
|
||||
@@ -0,0 +1,51 @@
|
||||
#Makefile to build BACnet Application
|
||||
|
||||
# Executable file name
|
||||
TARGET = mstpcrc
|
||||
|
||||
SRCS = main.c \
|
||||
${BACNET_PORT_DIR}/mstimer-init.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/sys/mstimer.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/datalink/crc.c
|
||||
|
||||
# BACNET_PORT, BACNET_PORT_DIR, BACNET_PORT_SRC are defined in common Makefile
|
||||
# BACNET_SRC_DIR is defined in common apps Makefile
|
||||
# WARNINGS, DEBUGGING, OPTIMIZATION are defined in common apps Makefile
|
||||
# BACNET_DEFINES is defined in common apps Makefile
|
||||
# put all the flags together
|
||||
INCLUDES = -I$(BACNET_SRC_DIR) -I$(BACNET_PORT_DIR)
|
||||
CFLAGS += $(WARNINGS) $(DEBUGGING) $(OPTIMIZATION) $(BACNET_DEFINES) $(INCLUDES)
|
||||
LFLAGS += -Wl,$(SYSTEM_LIB)
|
||||
ifneq (${BACNET_LIB},)
|
||||
LFLAGS += -Wl,$(BACNET_LIB)
|
||||
endif
|
||||
# GCC dead code removal
|
||||
CFLAGS += -ffunction-sections -fdata-sections
|
||||
LFLAGS += -Wl,--gc-sections
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
@@ -0,0 +1,9 @@
|
||||
@echo off
|
||||
echo Build with MinGW and MSYS: mingw.sourceforge.net
|
||||
rem set PATH=C:\MinGW\msys\1.0\bin;C:\MinGW\bin
|
||||
rem assumes rm, cp, size are already in path
|
||||
set CC=gcc
|
||||
set AR=ar
|
||||
set MAKE=make
|
||||
set TARGET_EXT=.exe
|
||||
make BACNET_PORT=win32 clean all
|
||||
@@ -0,0 +1,335 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2012 Steve Karg
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307
|
||||
USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <ctype.h>
|
||||
/* OS specific include*/
|
||||
#include "bacport.h"
|
||||
/* local includes */
|
||||
#include "bacnet/bytes.h"
|
||||
#include "bacnet/basic/sys/mstimer.h"
|
||||
#include "bacnet/datalink/crc.h"
|
||||
#include "bacnet/version.h"
|
||||
|
||||
#ifndef max
|
||||
#define max(a, b) (((a)(b)) ? (a) : (b))
|
||||
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/* buffer needed by CRC functions */
|
||||
static uint8_t CRC_Buffer[1512];
|
||||
static unsigned CRC_Buffer_Len = 0;
|
||||
/* flags needed for options */
|
||||
static bool ASCII_Decimal = false;
|
||||
static unsigned CRC_Size = 8;
|
||||
/* save to capture file for viewing in Wireshark */
|
||||
static bool MSTP_Cap = false;
|
||||
static bool MSTP_Text_File = false;
|
||||
static char Capture_Filename[64] = "mstp_20090123091200.cap";
|
||||
static FILE *pFile = NULL; /* stream pointer */
|
||||
static FILE *pText_File = NULL; /* stream pointer */
|
||||
|
||||
/******************************************************************
|
||||
* DESCRIPTION: Takes one of the arguments passed by the main function
|
||||
* and converts it into a buffer value.
|
||||
* argi - single argument in string form.
|
||||
* RETURN: nothing
|
||||
* NOTES: none
|
||||
******************************************************************/
|
||||
static void Parse_Number(char *argi)
|
||||
{
|
||||
long long_value = 0;
|
||||
|
||||
if (ASCII_Decimal) {
|
||||
long_value = strtol(argi, NULL, 10);
|
||||
} else {
|
||||
long_value = strtol(argi, NULL, 16);
|
||||
}
|
||||
CRC_Buffer[CRC_Buffer_Len] = (uint8_t)long_value;
|
||||
CRC_Buffer_Len++;
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
* DESCRIPTION: Takes one of the arguments passed by the main function
|
||||
* and sets flags if it matches one of the predefined args.
|
||||
* PARAMETERS: argc (IN) number of arguments.
|
||||
* argv (IN) an array of arguments in string form.
|
||||
* RETURN: number of arguments parsed
|
||||
* NOTES: none
|
||||
******************************************************************/
|
||||
static void Parse_Arguments(int argc, char *argv[])
|
||||
{
|
||||
int i = 0;
|
||||
long long_value = 0;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (argv[i][0] == '-') {
|
||||
/* numeric arguments */
|
||||
if (isdigit(argv[i][1])) {
|
||||
long_value = strtol(&argv[i][1], NULL, 10);
|
||||
/* dash arguments */
|
||||
switch (long_value) {
|
||||
case 8:
|
||||
CRC_Size = 8;
|
||||
break;
|
||||
case 16:
|
||||
CRC_Size = 16;
|
||||
break;
|
||||
case 32:
|
||||
CRC_Size = 32;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* dash arguments */
|
||||
switch (argv[i][1]) {
|
||||
case 'h':
|
||||
case 'H':
|
||||
ASCII_Decimal = false;
|
||||
break;
|
||||
case 'd':
|
||||
case 'D':
|
||||
ASCII_Decimal = true;
|
||||
break;
|
||||
case 'm':
|
||||
case 'M':
|
||||
MSTP_Cap = true;
|
||||
break;
|
||||
case 'f':
|
||||
case 'F':
|
||||
MSTP_Text_File = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (MSTP_Text_File) {
|
||||
/* open existing file. */
|
||||
pText_File = fopen(argv[i], "r");
|
||||
} else {
|
||||
/* should be number values here */
|
||||
Parse_Number(argv[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void filename_create(char *filename)
|
||||
{
|
||||
time_t my_time;
|
||||
struct tm *today;
|
||||
|
||||
if (filename) {
|
||||
my_time = time(NULL);
|
||||
today = localtime(&my_time);
|
||||
sprintf(filename, "mstp_%04d%02d%02d%02d%02d%02d.cap",
|
||||
1900 + today->tm_year, 1 + today->tm_mon, today->tm_mday,
|
||||
today->tm_hour, today->tm_min, today->tm_sec);
|
||||
}
|
||||
}
|
||||
|
||||
/* write packet to file in libpcap format */
|
||||
static void write_global_header(const char *filename)
|
||||
{
|
||||
uint32_t magic_number = 0xa1b2c3d4; /* magic number */
|
||||
uint16_t version_major = 2; /* major version number */
|
||||
uint16_t version_minor = 4; /* minor version number */
|
||||
int32_t thiszone = 0; /* GMT to local correction */
|
||||
uint32_t sigfigs = 0; /* accuracy of timestamps */
|
||||
uint32_t snaplen = 65535; /* max length of captured packets, in octets */
|
||||
uint32_t network = 165; /* data link type - BACNET_MS_TP */
|
||||
|
||||
/* create a new file. */
|
||||
pFile = fopen(filename, "wb");
|
||||
if (pFile) {
|
||||
(void)fwrite(&magic_number, sizeof(magic_number), 1, pFile);
|
||||
(void)fwrite(&version_major, sizeof(version_major), 1, pFile);
|
||||
(void)fwrite(&version_minor, sizeof(version_minor), 1, pFile);
|
||||
(void)fwrite(&thiszone, sizeof(thiszone), 1, pFile);
|
||||
(void)fwrite(&sigfigs, sizeof(sigfigs), 1, pFile);
|
||||
(void)fwrite(&snaplen, sizeof(snaplen), 1, pFile);
|
||||
(void)fwrite(&network, sizeof(network), 1, pFile);
|
||||
fflush(pFile);
|
||||
fprintf(stdout, "mstpcap: saving capture to %s\n", filename);
|
||||
} else {
|
||||
fprintf(stderr, "mstpcap[header]: failed to open %s: %s\n", filename,
|
||||
strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void write_received_packet(uint8_t *buffer, unsigned length)
|
||||
{
|
||||
uint32_t ts_sec; /* timestamp seconds */
|
||||
uint32_t ts_usec; /* timestamp microseconds */
|
||||
uint32_t incl_len; /* number of octets of packet saved in file */
|
||||
uint32_t orig_len; /* actual length of packet */
|
||||
struct timeval tv;
|
||||
|
||||
if (pFile) {
|
||||
gettimeofday(&tv, NULL);
|
||||
ts_sec = tv.tv_sec;
|
||||
ts_usec = tv.tv_usec;
|
||||
(void)fwrite(&ts_sec, sizeof(ts_sec), 1, pFile);
|
||||
(void)fwrite(&ts_usec, sizeof(ts_usec), 1, pFile);
|
||||
orig_len = incl_len = length;
|
||||
(void)fwrite(&incl_len, sizeof(incl_len), 1, pFile);
|
||||
(void)fwrite(&orig_len, sizeof(orig_len), 1, pFile);
|
||||
(void)fwrite(buffer, length, 1, pFile);
|
||||
} else {
|
||||
fprintf(stderr, "mstpcrc[packet]: failed to open %s: %s\n",
|
||||
Capture_Filename, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void Write_Pcap(uint8_t *buffer, unsigned length)
|
||||
{
|
||||
filename_create(&Capture_Filename[0]);
|
||||
write_global_header(&Capture_Filename[0]);
|
||||
write_received_packet(buffer, length);
|
||||
if (pFile) {
|
||||
fclose(pFile);
|
||||
}
|
||||
}
|
||||
/* hold 3 ASCII characters per byte of data */
|
||||
static char Text_Buffer[1024 * 3];
|
||||
static void Process_Text_File(void)
|
||||
{
|
||||
char *argi = NULL;
|
||||
|
||||
filename_create(&Capture_Filename[0]);
|
||||
write_global_header(&Capture_Filename[0]);
|
||||
while (fgets(Text_Buffer, sizeof(Text_Buffer), pText_File)) {
|
||||
CRC_Buffer_Len = 0;
|
||||
do {
|
||||
if (!argi) {
|
||||
argi = strtok(Text_Buffer, " ");
|
||||
} else {
|
||||
argi = strtok(NULL, " ");
|
||||
}
|
||||
if (argi) {
|
||||
Parse_Number(argi);
|
||||
}
|
||||
} while (argi);
|
||||
write_received_packet(CRC_Buffer, CRC_Buffer_Len);
|
||||
}
|
||||
if (pFile) {
|
||||
fclose(pFile);
|
||||
}
|
||||
if (pText_File) {
|
||||
fclose(pText_File);
|
||||
}
|
||||
}
|
||||
|
||||
/* simple program to CRC the data and print the CRC */
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
/* accumulates the crc value */
|
||||
uint8_t crc8 = 0xff;
|
||||
uint16_t crc16 = 0xffff;
|
||||
unsigned i = 0;
|
||||
|
||||
/* initialize our interface */
|
||||
if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) {
|
||||
printf(
|
||||
"mstpcrc [options] <05 03 01 0D...>\r\n"
|
||||
"perform MS/TP CRC on data bytes.\r\n"
|
||||
"options:\r\n"
|
||||
"[-x] interprete the arguments as ascii hex (default)\r\n"
|
||||
"[-d] interprete the argument as ascii decimal\r\n"
|
||||
"[-m] Write the bytes to Wireshark capture file\r\n"
|
||||
"[-8] calculate the MS/TP 8-bit Header CRC (default)\r\n"
|
||||
"[-16] calculate the MS/TP 16-bit Data CRC\r\n"
|
||||
"[-32] calculate the MS/TP 32-bit Extended Frame CRC\r\n"
|
||||
"[-f filename] read MS/TP capture data from text file\r\n"
|
||||
"Note: MS/TP Header CRC does not include the 55 FF preamble.\r\n");
|
||||
return 0;
|
||||
}
|
||||
if ((argc > 1) && (strcmp(argv[1], "--version") == 0)) {
|
||||
printf("mstpcap %s\r\n", BACNET_VERSION_TEXT);
|
||||
printf(
|
||||
"Copyright (C) 2012 by Steve Karg\r\n"
|
||||
"This is free software; see the source for copying conditions.\r\n"
|
||||
"There is NO warranty; not even for MERCHANTABILITY or\r\n"
|
||||
"FITNESS FOR A PARTICULAR PURPOSE.\r\n");
|
||||
return 0;
|
||||
}
|
||||
Parse_Arguments(argc, argv);
|
||||
if (MSTP_Text_File) {
|
||||
Process_Text_File();
|
||||
} else if (CRC_Buffer_Len) {
|
||||
if (MSTP_Cap) {
|
||||
Write_Pcap(CRC_Buffer, CRC_Buffer_Len);
|
||||
} else {
|
||||
for (i = 0; i < CRC_Buffer_Len; i++) {
|
||||
if (CRC_Size == 8) {
|
||||
crc8 = CRC_Calc_Header(CRC_Buffer[i], crc8);
|
||||
} else if (CRC_Size == 16) {
|
||||
crc16 = CRC_Calc_Data(CRC_Buffer[i], crc16);
|
||||
}
|
||||
if (ASCII_Decimal) {
|
||||
printf("%u\r\n", (unsigned)CRC_Buffer[i]);
|
||||
} else {
|
||||
printf("0x%02X\r\n", CRC_Buffer[i]);
|
||||
}
|
||||
}
|
||||
if (CRC_Size == 8) {
|
||||
crc8 = ~crc8;
|
||||
if (ASCII_Decimal) {
|
||||
printf("%u Header CRC\r\n", (unsigned)crc8);
|
||||
} else {
|
||||
printf("0x%02X Header CRC\r\n", crc8);
|
||||
}
|
||||
} else if (CRC_Size == 16) {
|
||||
crc16 = ~crc16;
|
||||
if (ASCII_Decimal) {
|
||||
printf("%u Data CRC\r\n", (unsigned)(crc16 & 0xFF));
|
||||
printf("%u Data CRC\r\n", (unsigned)(crc16 >> 8));
|
||||
} else {
|
||||
printf("0x%02X Data CRC\r\n", (crc16 & 0xFF));
|
||||
printf("0x%02X Data CRC\r\n", (crc16 >> 8));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
BACnet MS/TP CRC Calculator
|
||||
|
||||
This tool receives MS/TP bytes and generates a CRC for those bytes
|
||||
|
||||
mstpcrc [options] <00 00 00 00...>
|
||||
perform MS/TP CRC on data bytes.
|
||||
options:
|
||||
[-x] interprete the arguments as ascii hex (default)
|
||||
[-d] interprete the argument as ascii decimal
|
||||
[-8] calculate the MS/TP 8-bit Header CRC (default)
|
||||
[-16] calculate the MS/TP 16-bit Data CRC
|
||||
|
||||
Here is a sample of the tool running (use CTRL-C to quit):
|
||||
D:\code\bacnet-stack\demo\mstpcrc>mstpcrc 06 ff 01 00 15
|
||||
0x06
|
||||
0xFF
|
||||
0x01
|
||||
0x00
|
||||
0x15
|
||||
0x8E Header CRC
|
||||
@@ -0,0 +1,348 @@
|
||||
<?xml version="1.0" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>API Documentation</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<link rev="made" href="mailto:rurban@x-ray.at" />
|
||||
</head>
|
||||
|
||||
<body style="background-color: white">
|
||||
|
||||
|
||||
<!-- INDEX BEGIN -->
|
||||
<div name="index">
|
||||
<p><a name="__index__"></a></p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="#name">NAME</a></li>
|
||||
<li><a href="#description">DESCRIPTION</a></li>
|
||||
<li><a href="#options">OPTIONS</a></li>
|
||||
<li><a href="#this_tool_s_api">This tool's API</a></li>
|
||||
<ul>
|
||||
|
||||
<li><a href="#readproperty">ReadProperty</a></li>
|
||||
<ul>
|
||||
|
||||
<li><a href="#inputs_to_readproperty">Inputs to ReadProperty</a></li>
|
||||
<li><a href="#outputs_from_readproperty">Outputs from ReadProperty</a></li>
|
||||
<li><a href="#example_of_readproperty">Example of ReadProperty</a></li>
|
||||
</ul>
|
||||
|
||||
<li><a href="#readpropertymultiple">ReadPropertyMultiple</a></li>
|
||||
<ul>
|
||||
|
||||
<li><a href="#inputs_to_readpropertymultiple">Inputs to ReadPropertyMultiple</a></li>
|
||||
<li><a href="#outputs_from_readpropertymultiple">Outputs from ReadPropertyMultiple</a></li>
|
||||
<li><a href="#example_of_readpropertymultiple">Example of ReadPropertyMultiple</a></li>
|
||||
</ul>
|
||||
|
||||
<li><a href="#writeproperty">WriteProperty</a></li>
|
||||
<ul>
|
||||
|
||||
<li><a href="#inputs_to_writeproperty">Inputs to WriteProperty</a></li>
|
||||
<li><a href="#outputs_from_writeproperty">Outputs from WriteProperty</a></li>
|
||||
<li><a href="#example_of_writeproperty">Example of WriteProperty</a></li>
|
||||
</ul>
|
||||
|
||||
<li><a href="#timesync">TimeSync</a></li>
|
||||
<ul>
|
||||
|
||||
<li><a href="#inputs_to_timesync">Inputs to TimeSync</a></li>
|
||||
<li><a href="#outputs_from_timesync">Outputs from TimeSync</a></li>
|
||||
<li><a href="#example_of_timesync">Example of TimeSync</a></li>
|
||||
</ul>
|
||||
|
||||
<li><a href="#log">Log</a></li>
|
||||
<ul>
|
||||
|
||||
<li><a href="#inputs_to_log">Inputs to Log</a></li>
|
||||
<li><a href="#example_of_log">Example of Log</a></li>
|
||||
</ul>
|
||||
|
||||
<li><a href="#silencelog">SilenceLog</a></li>
|
||||
<ul>
|
||||
|
||||
<li><a href="#inputs_to_silencelog">Inputs to SilenceLog</a></li>
|
||||
<li><a href="#outputs_from_silencelog">Outputs from SilenceLog</a></li>
|
||||
<li><a href="#example_of_silencelog">Example of SilenceLog</a></li>
|
||||
</ul>
|
||||
|
||||
<li><a href="#retry">Retry</a></li>
|
||||
<ul>
|
||||
|
||||
<li><a href="#inputs_to_retry">Inputs to Retry</a></li>
|
||||
<li><a href="#outputs_from_retry">Outputs from Retry</a></li>
|
||||
<li><a href="#example_of_retry">Example of Retry</a></li>
|
||||
</ul>
|
||||
|
||||
</ul>
|
||||
|
||||
</ul>
|
||||
|
||||
<hr name="index" />
|
||||
</div>
|
||||
<!-- INDEX END -->
|
||||
|
||||
<p>
|
||||
</p>
|
||||
<h1><a name="name">NAME</a></h1>
|
||||
<p>bacnet.pl - Scriptable BACnet communications</p>
|
||||
<p>
|
||||
</p>
|
||||
<hr />
|
||||
<h1><a name="description">DESCRIPTION</a></h1>
|
||||
<p>This is a tool for scriptable BACnet communication. Users can write their own
|
||||
scripts using standard Perl syntax and API defined in this tool to perform desired
|
||||
execution sequences. For details on this tool's API, see Documentation.html. For other
|
||||
Perl documentation, see <a href="http://perldoc.perl.org">http://perldoc.perl.org</a></p>
|
||||
<link href="syntax.css" rel="stylesheet" type="text/css">
|
||||
<script src="jquery.js"></script>
|
||||
<script src="syntax.js"></script><p>
|
||||
</p>
|
||||
<hr />
|
||||
<h1><a name="options">OPTIONS</a></h1>
|
||||
<p>Usage: bacnet.pl [program_options] [-- script_args]</p>
|
||||
<p>This program executes a script in perl syntax to perform BACnet/IP operations.</p>
|
||||
<pre>
|
||||
|
||||
Possible program options:
|
||||
--script=s The script to execute.
|
||||
--log=s The file to log all output.
|
||||
--help This help message.</pre>
|
||||
<pre>
|
||||
Possible environment variables are:
|
||||
BACNET_IFACE - set this value to dotted IP address of the interface (see
|
||||
ipconfig) for which you want to bind. Default is the interface which
|
||||
Windows considers to be the default (how???). Hence, if there is only a
|
||||
single network interface on Windows, the applications will choose it, and
|
||||
this setting will not be needed.
|
||||
BACNET_IP_PORT - UDP/IP port number (0..65534) used for BACnet/IP
|
||||
communications. Default is 47808 (0xBAC0).
|
||||
BACNET_APDU_TIMEOUT - set this value in milliseconds to change the APDU
|
||||
timeout. APDU Timeout is how much time a client waits for a response from
|
||||
a BACnet device.
|
||||
BACNET_BBMD_PORT - UDP/IP port number (0..65534) used for Foreign Device
|
||||
Registration. Defaults to 47808 (0xBAC0).
|
||||
BACNET_BBMD_TIMETOLIVE - number of seconds used in Foreign Device
|
||||
Registration (0..65535). Defaults to 60000 seconds.
|
||||
BACNET_BBMD_ADDRESS - dotted IPv4 address of the BBMD or Foreign Device
|
||||
Registrar.</pre>
|
||||
<p>
|
||||
</p>
|
||||
<hr />
|
||||
<h1><a name="this_tool_s_api">This tool's API</a></h1>
|
||||
<p>In addition to having all standard Perl flow control, functions, and modules,
|
||||
the this tool provides an API for performing BACnet communication functions.</p>
|
||||
<p>
|
||||
</p>
|
||||
<h2><a name="readproperty">ReadProperty</a></h2>
|
||||
<p>This function implements the ReadProperty service. There are no built in retry
|
||||
mechanisms. NOTE: all enumerations are defined in <em class="file">bacenum.h</em></p>
|
||||
<p>
|
||||
</p>
|
||||
<h3><a name="inputs_to_readproperty">Inputs to ReadProperty</a></h3>
|
||||
<ul>
|
||||
<li><b>devideInstance</b> - the instance number of the device we are reading</li>
|
||||
<li><b>objectName</b> - the enumeration for the object name we are reading</li>
|
||||
<li><b>objectInstance</b> - the instance number of the object we are reading</li>
|
||||
<li><b>propertyName</b> - the enumeration for the property name we are reading</li>
|
||||
<li><b>index</b> - Optional (default -1): the index number we are reading from. -1 if not applicable</li>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="outputs_from_readproperty">Outputs from ReadProperty</a></h3>
|
||||
<ul>
|
||||
<li><b>result</b> - the sting result (value or error) for ReadProperty</li>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="example_of_readproperty">Example of ReadProperty</a></h3>
|
||||
<p>The following example will read AV0.PresentValue from device 1234</p>
|
||||
<pre>
|
||||
my ($res, $failed) = ReadProperty(1234, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE');</pre>
|
||||
<p>
|
||||
</p>
|
||||
<h2><a name="readpropertymultiple">ReadPropertyMultiple</a></h2>
|
||||
<p>This function implements the ReadPropertyMultiple service. There are no built in retry
|
||||
mechanisms. NOTE: all enumerations are defined in <em class="file">bacenum.h</em></p>
|
||||
<p>
|
||||
</p>
|
||||
<h3><a name="inputs_to_readpropertymultiple">Inputs to ReadPropertyMultiple</a></h3>
|
||||
<ul>
|
||||
<li><b>devideInstance</b> - the instance number of the device we are reading</li>
|
||||
<li><b>r_answerList</b> - reference to a list where to store the answers</li>
|
||||
<li><b>list</b> - a list of ReadAccessSpecifications</li>
|
||||
<ul>
|
||||
<li><b>objectType</b> - the enumeration for the object name to read from</li>
|
||||
<li><b>objectInstance</b> - the instance number of the object we are reading</li>
|
||||
<li><b>propertyName</b> - the enumeration for the property name we are reading</li>
|
||||
<li><b>index</b> - the index number we are reading from. Use -1 if not applicable</li>
|
||||
</ul>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="outputs_from_readpropertymultiple">Outputs from ReadPropertyMultiple</a></h3>
|
||||
<ul>
|
||||
<li><b>result</b> - the 'QQQ' delimited concatenated sting result (value or error) for ReadPropertyMultiple. The parsed out result is returned in r_answerList</li>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="example_of_readpropertymultiple">Example of ReadPropertyMultiple</a></h3>
|
||||
<p>The following example will read AV0.PresentValue and AV1.PresentValue from device 1234</p>
|
||||
<pre>
|
||||
my @RPM_request = ();
|
||||
my @RPM_answer = ();
|
||||
my $failed;
|
||||
push @RPM_request, ['OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE', -1];
|
||||
push @RPM_request, ['OBJECT_ANALOG_VALUE', 1, 'PROP_PRESENT_VALUE', -1];
|
||||
(undef, $failed) = ReadPropertyMultiple(1234, \@RPM_answer, @RPM_request);</pre>
|
||||
<p>
|
||||
</p>
|
||||
<h2><a name="writeproperty">WriteProperty</a></h2>
|
||||
<p>This function implements the WriteProperty service. There are no built in retry
|
||||
mechanisms. NOTE: all enumerations are defined in <em class="file">bacenum.h</em></p>
|
||||
<p>
|
||||
</p>
|
||||
<h3><a name="inputs_to_writeproperty">Inputs to WriteProperty</a></h3>
|
||||
<ul>
|
||||
<li><b>devideInstance</b> - the instance number of the device we are writing</li>
|
||||
<li><b>objectName</b> - the enumeration for the object name we are writing</li>
|
||||
<li><b>objectInstance</b> - the instance number of the object we are writing</li>
|
||||
<li><b>propertyName</b> - the enumeration for the property name we are writing</li>
|
||||
<li><b>tagName</b> - the enumeration for the type of value we are writing. To specify context tags, prepend the tag name with "Cn:" where 'n' is the context number.</li>
|
||||
<li><b>value</b> - the value we are writing</li>
|
||||
<li><b>priority</b> - Optional (default 0): the priority within Priority Array to write at. Use 1-16 when specify priority, 0 to not specify priority.</li>
|
||||
<li><b>index</b> - Optional (default -1): the index within an array we are writing to. Use positive number to indicate index, -1 to not specify index.</li>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="outputs_from_writeproperty">Outputs from WriteProperty</a></h3>
|
||||
<ul>
|
||||
<li><b>result</b> - the sting result (value or error) for WriteProperty</li>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="example_of_writeproperty">Example of WriteProperty</a></h3>
|
||||
<p>The following example will write 1.0 to AV0.PresentValue in device 1234</p>
|
||||
<pre>
|
||||
my ($res, $failed) = WriteProperty(1234, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE', 'BACNET_APPLICATION_TAG_REAL', 1.0);</pre>
|
||||
<p>
|
||||
</p>
|
||||
<h2><a name="timesync">TimeSync</a></h2>
|
||||
<p>This function implements the TimeSync and UTCTimeSync services</p>
|
||||
<p>
|
||||
</p>
|
||||
<h3><a name="inputs_to_timesync">Inputs to TimeSync</a></h3>
|
||||
<ul>
|
||||
<li><b>deviceInstanceNumber</b> - the instance number of the device we are reading</li>
|
||||
<li><b>year</b> - Year (i.e. 2011)</li>
|
||||
<li><b>month</b> - Month (i.e. 11 for November)</li>
|
||||
<li><b>day</b> - Day (i.e. 1 for first of month)</li>
|
||||
<li><b>hour</b> - Hour (i.e. 23 for 11pm)</li>
|
||||
<li><b>minute</b> - Minute (i.e. 0-59)</li>
|
||||
<li><b>second</b> - Second (i,e. 0-59)</li>
|
||||
<li><b>utcOffset</b> - Optional: if specified defines the UTC offset and forces UTCTimeSync</li>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="outputs_from_timesync">Outputs from TimeSync</a></h3>
|
||||
<ul>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="example_of_timesync">Example of TimeSync</a></h3>
|
||||
<pre>
|
||||
$isFailure = TimeSync($deviceInstance, $1, $2, $3, $4, $5, $6) unless $isFailure;</pre>
|
||||
<p>
|
||||
</p>
|
||||
<h2><a name="log">Log</a></h2>
|
||||
<p>This function prints out to the desired method of logging (STDOUT or file).
|
||||
NewLine characters are not required when making calls to this function. If any
|
||||
NewLine characters are specified, they will be stripped out. To print an empty
|
||||
line, pass in a space as the message. NOTE: This function will honor previous
|
||||
requests to silence the log (see SilcenseLog for details)</p>
|
||||
<p>
|
||||
</p>
|
||||
<h3><a name="inputs_to_log">Inputs to Log</a></h3>
|
||||
<ul>
|
||||
<li><b>msg</b> - the message to output
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="example_of_log">Example of Log</a></h3>
|
||||
<p>The following example will print out "hello world"</p>
|
||||
<pre>
|
||||
Log("Hello World");</pre>
|
||||
<p>
|
||||
</p>
|
||||
<h2><a name="silencelog">SilenceLog</a></h2>
|
||||
<p>This function requests that all future log messages be either suppressed or
|
||||
enabled.</p>
|
||||
<p>
|
||||
</p>
|
||||
<h3><a name="inputs_to_silencelog">Inputs to SilenceLog</a></h3>
|
||||
<ul>
|
||||
<li><b>logIsQuiet</b> - zero means print to log, non-zero means supress log
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="outputs_from_silencelog">Outputs from SilenceLog</a></h3>
|
||||
<p>The previous value of whether or not the log was silenced before caling this
|
||||
function.</p>
|
||||
<p>
|
||||
</p>
|
||||
<h3><a name="example_of_silencelog">Example of SilenceLog</a></h3>
|
||||
<p>The following example will print out "hello", but not "world"</p>
|
||||
<pre>
|
||||
Log("Hello");
|
||||
SilenceLog(1);
|
||||
Log("World");</pre>
|
||||
<p>
|
||||
</p>
|
||||
<h2><a name="retry">Retry</a></h2>
|
||||
<p>This function will try to execute the requested command up to specified number
|
||||
of times, awaiting the requested answer, with a specified pause between
|
||||
retries. NOTE: the only functions which can be executed by this function are
|
||||
ones which return two parameres in the form of ($response, $isFailure)</p>
|
||||
<p>
|
||||
</p>
|
||||
<h3><a name="inputs_to_retry">Inputs to Retry</a></h3>
|
||||
<ul>
|
||||
<li><b>r_func</b> - The reference to the function which is to be retried</li>
|
||||
<li><b>r_funcArgs</b> - A reference to an array of arguments for the function to be executed</li>
|
||||
<li><b>desiredOutput</b> - The condition which will terminate the retrying. Can be either a number or a regexp to patch against the $response return of the function</li>
|
||||
<li><b>maxTries</b> - The maximum number of retry attempts before calling it quits</li>
|
||||
<li><b>sleepSeconds</b> - The number of seconds (could be fractional) to wait between retries</li>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="outputs_from_retry">Outputs from Retry</a></h3>
|
||||
<ul>
|
||||
<li><b>$resp</b> - The response from the last execution of requested function</li>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="example_of_retry">Example of Retry</a></h3>
|
||||
<p>The following example will execute the ReadProperty function to read a property
|
||||
from an object (see ReadProperty for details on those arguments) with up to
|
||||
$maxRetries retries (with $retryDelay delay between retries) or unitl the
|
||||
desired answer of 42 is received.</p>
|
||||
<pre>
|
||||
my ($resp, $isFailure) = Retry(
|
||||
\&ReadProperty, [$deviceInstance, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE'],
|
||||
42, $maxRetries, $retryDelay
|
||||
);
|
||||
if ($isFailure)
|
||||
{
|
||||
die "Value was not 42. Last response was '$resp'";
|
||||
}</pre>
|
||||
<p>The following example will try to execute a WriteProperty (see that function for
|
||||
details on its arguments) until the write succeeds.</p>
|
||||
<pre>
|
||||
my ($resp, $isFailure) = Retry(
|
||||
\&WriteProperty, [$deviceInstance, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE', 'BACNET_APPLICATION_TAG_REAL', 42.0],
|
||||
"Acknowledged", $maxRetries, $retryDelay
|
||||
);
|
||||
if ($isFailure)
|
||||
{
|
||||
die "Could not write 42. Last response was '$resp'";
|
||||
}</pre>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Vendored
+4
File diff suppressed because one or more lines are too long
@@ -0,0 +1,75 @@
|
||||
pre{
|
||||
font-family: "Courier New", Courier, monospace, sans-serif;
|
||||
text-align: left;
|
||||
line-height: 1.6em;
|
||||
font-size: 11px;
|
||||
padding: 0.1em 0.5em 0.3em 0.7em;
|
||||
border: 2px solid #888;
|
||||
margin: 1.7em 0 1.7em 0.3em;
|
||||
overflow: auto;
|
||||
width: 93%;
|
||||
background: #EEEEEE;
|
||||
}
|
||||
h1 {
|
||||
font-size: 20pt;
|
||||
counter-increment: counter-h1;
|
||||
counter-reset: counter-h2;
|
||||
}
|
||||
h2 {
|
||||
font-size: 17pt;
|
||||
counter-increment: counter-h2;
|
||||
counter-reset: counter-h3;
|
||||
}
|
||||
h3 {
|
||||
font-size: 14pt;
|
||||
counter-increment: counter-h3;
|
||||
counter-reset: counter-h4;
|
||||
}
|
||||
h1:before {
|
||||
content: counter(counter-h1) ". ";
|
||||
}
|
||||
h2:before {
|
||||
content: counter(counter-h1) "." counter(counter-h2) ". ";
|
||||
}
|
||||
h3:before {
|
||||
content: counter(counter-h1) "." counter(counter-h2) "." counter(counter-h3) ". ";
|
||||
}
|
||||
ul {
|
||||
list-style-type: circle;
|
||||
}
|
||||
.quotedString
|
||||
{
|
||||
color: #0000FF;
|
||||
}
|
||||
.comment
|
||||
{
|
||||
color: #999999;
|
||||
}
|
||||
.operator
|
||||
{
|
||||
color: #00CCCC;
|
||||
}
|
||||
.builtinVariable
|
||||
{
|
||||
color: #CCCC00;
|
||||
}
|
||||
.variableSpecifier
|
||||
{
|
||||
color: #FF0000;
|
||||
}
|
||||
.keyword
|
||||
{
|
||||
color: #AA0033;
|
||||
}
|
||||
.builtinFunction
|
||||
{
|
||||
color: #AA00AA;
|
||||
}
|
||||
.identifier
|
||||
{
|
||||
color: #009900;
|
||||
}
|
||||
.number
|
||||
{
|
||||
color: #9999FF;
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
var ie = document.all != null;
|
||||
var moz = !ie && document.getElementById != null && document.layers == null;
|
||||
function emulateHTMLModel()
|
||||
{
|
||||
// copied from http://www.webfx.nu/dhtml/ieemu/htmlmodel.html
|
||||
|
||||
// This function is used to generate a html string for the text properties/methods
|
||||
// It replaces '\n' with "<BR"> as well as fixes consecutive white spaces
|
||||
// It also repalaces some special characters
|
||||
function convertTextToHTML(s) {
|
||||
s = s.replace(/\&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\n/g, "<BR>").replace(/\t/g, " "); //tachyon
|
||||
while (/\s\s/.test(s))
|
||||
s = s.replace(/\s\s/, " ");
|
||||
return s.replace(/\s/g, " ");
|
||||
}
|
||||
|
||||
|
||||
HTMLElement.prototype.__defineSetter__("innerText", function (sText) {
|
||||
this.innerHTML = convertTextToHTML(sText);
|
||||
return sText;
|
||||
});
|
||||
|
||||
var tmpGet;
|
||||
HTMLElement.prototype.__defineGetter__("innerText", tmpGet = function () {
|
||||
var r = this.ownerDocument.createRange();
|
||||
r.selectNodeContents(this);
|
||||
return r.toString();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
if (moz)
|
||||
emulateHTMLModel();
|
||||
|
||||
|
||||
// Regular Expressions largely copied from Cory Johns (darkness@yossman.net) excellent Syntax::Highlight::Perl module (see http://search.cpan.org/~johnsca/)
|
||||
|
||||
var re;
|
||||
var RE = new Array;
|
||||
|
||||
// quoted string
|
||||
re = /('|"|`).*?\1/;
|
||||
RE[0] = new RegExp(re);
|
||||
|
||||
// comment
|
||||
re = /\#.*?([\r\n]+|$)/; //tachyon
|
||||
RE[1] = new RegExp(re);
|
||||
|
||||
// operator
|
||||
re = /xor|\.\.\.|and|not|\|\|\=|cmp|\>\>\=|\<\<\=|\<\=\>|\&\&\=|or|\=\>|\!\~|\^\=|\&\=|\|\=|\.\=|x\=|\%\=|\/\=|\*\=|\-\=|\+\=|\=\~|\*\*|\-\-|\.\.|\|\||\&\&|\+\+|\-\>|ne|eq|\!\=|\=\=|ge|le|gt|lt|\>\=|\<\=|\>\>|\<\<|\,|\=|\:|\?|\^|\||x|\%|\/|\*|\<|\&|\\|\~|\!|\>|\.|\-|\+ /;
|
||||
RE[2] = new RegExp(re);
|
||||
|
||||
// builtin variables
|
||||
re = /\$\#?_|\$(?:\^[LAECDFHIMOPRSTWX]|[0-9&`'+*.\/|,\\";#%=\-~^:?!@\$<>()\[\]])|\$\#?ARGV(?:\s*\[)?|\$\#?INC\s*\[|\$(?:ENV|SIG|INC)\s*\{|\@(?:_|ARGV|INC)|\%(?:INC|ENV|SIG)/;
|
||||
RE[3] = new RegExp(re);
|
||||
|
||||
// variable class specifiers
|
||||
re = /(?:(?:[\@\%\*]|\$\#?)\$*)/;
|
||||
RE[4] = new RegExp(re);
|
||||
|
||||
// keyword
|
||||
re = /(continue|foreach|require|package|scalar|format|unless|local|until|while|elsif|next|last|goto|else|redo|sub|for|use|no|if|my)\b/;
|
||||
RE[5] = new RegExp(re);
|
||||
|
||||
// builtin function
|
||||
re = /(getprotobynumber|getprotobyname|getservbyname|gethostbyaddr|gethostbyname|getservbyport|getnetbyaddr|getnetbyname|getsockname|getpeername|setpriority|getprotoent|setprotoent|getpriority|endprotoent|getservent|setservent|endservent|sethostent|socketpair|getsockopt|gethostent|endhostent|setsockopt|setnetent|quotemeta|localtime|prototype|getnetent|endnetent|rewinddir|wantarray|getpwuid|closedir|getlogin|readlink|endgrent|getgrgid|getgrnam|shmwrite|shutdown|readline|endpwent|setgrent|readpipe|formline|truncate|dbmclose|syswrite|setpwent|getpwnam|getgrent|getpwent|ucfirst|sysread|setpgrp|shmread|sysseek|sysopen|telldir|defined|opendir|connect|lcfirst|getppid|binmode|syscall|sprintf|getpgrp|readdir|seekdir|waitpid|reverse|unshift|symlink|dbmopen|semget|msgrcv|rename|listen|chroot|msgsnd|shmctl|accept|unpack|exists|fileno|shmget|system|unlink|printf|gmtime|msgctl|semctl|values|rindex|substr|splice|length|msgget|select|socket|return|caller|delete|alarm|ioctl|index|undef|lstat|times|srand|chown|fcntl|close|write|umask|rmdir|study|sleep|chomp|untie|print|utime|mkdir|atan2|split|crypt|flock|chmod|BEGIN|bless|chdir|semop|shift|reset|link|stat|chop|grep|fork|dump|join|open|tell|pipe|exit|glob|warn|each|bind|sort|pack|eval|push|keys|getc|kill|seek|sqrt|send|wait|rand|tied|read|time|exec|recv|eof|chr|int|ord|exp|pos|pop|sin|log|abs|oct|hex|tie|cos|vec|END|ref|map|die|\-C|\-b|\-S|\-u|\-t|\-p|\-l|\-d|\-f|\-g|\-s|\-z|uc|\-k|\-e|\-O|\-T|\-B|\-M|do|\-A|\-X|\-W|\-c|\-R|\-o|\-x|lc|\-w|\-r)\b/;
|
||||
RE[6] = new RegExp(re);
|
||||
|
||||
// identifier (variable, subroutine, packages)
|
||||
re = /(?:(?:[A-Za-z_]|::)(?:\w|::)*)/;
|
||||
RE[7] = new RegExp(re);
|
||||
|
||||
// number
|
||||
re = /0x[\da-fA-F]+|[_.\d]+([eE][-+]?\d+)?/;
|
||||
RE[8] = new RegExp(re);
|
||||
|
||||
|
||||
var classes = new Array("quotedString", "comment", "operator", "builtinVariable", "variableSpecifier", "keyword", "builtinFunction", "identifier", "number");
|
||||
|
||||
|
||||
/* This is the actual highlighting function.
|
||||
* Takes an html object as argument
|
||||
* returns nothing
|
||||
* replaces the text inside the html object with colored text using <span>'s
|
||||
* css is defined separately. See the array classes to find out the css class names.
|
||||
*/
|
||||
function HighlightCode(object)
|
||||
{
|
||||
codeText = object.innerText; //HTML.replace(/<.*?>/g, "");
|
||||
object.innerHTML = '';
|
||||
var left;
|
||||
var match;
|
||||
var right;
|
||||
while (codeText.length > 0)
|
||||
{
|
||||
var mode = -1 ;
|
||||
var index = 999999999;
|
||||
for (var i = 0; i < RE.length; i++)
|
||||
{
|
||||
if ((codeText.match(RE[i])) && (RegExp.leftContext.length < index))
|
||||
{
|
||||
left = RegExp.leftContext;
|
||||
match = RegExp.lastMatch;
|
||||
right = RegExp.rightContext;
|
||||
index = RegExp.leftContext.length;
|
||||
mode = i;
|
||||
}
|
||||
}
|
||||
if (mode == -1)
|
||||
{
|
||||
object.appendChild(document.createTextNode(codeText)); //.replace(/\r\n/g, "\r")));
|
||||
codeText = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
// append the plain text to the <code> block
|
||||
object.appendChild(document.createTextNode(left)); //.replace(/\r\n/g, "\r")));
|
||||
|
||||
// create a new <span> with the current code
|
||||
var span = document.createElement("span");
|
||||
span.setAttribute("className", classes[mode]); // ie
|
||||
span.setAttribute("class", classes[mode]); //mozilla
|
||||
span.appendChild(document.createTextNode(match));
|
||||
object.appendChild(span);
|
||||
|
||||
codeText = right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// little bit of JQuery to highlight code in all pre elements
|
||||
$(document).ready(function(){
|
||||
$("pre").each(function(i){
|
||||
HighlightCode(this);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,869 @@
|
||||
use warnings;
|
||||
use strict;
|
||||
use Getopt::Long;
|
||||
use Convert::Binary::C;
|
||||
use Hash::Util qw/lock_hash/;
|
||||
use English;
|
||||
use Scalar::Util qw/looks_like_number/;
|
||||
use File::Basename;
|
||||
use File::Spec;
|
||||
use Pod::Usage;
|
||||
use Carp;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
bacnet.pl - Scriptable BACnet communications
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This is a tool for scriptable BACnet communication. Users can write their own
|
||||
scripts using standard Perl syntax and API defined in this tool to perform desired
|
||||
execution sequences. For details on this tool's API, see Documentation.html. For other
|
||||
Perl documentation, see http://perldoc.perl.org
|
||||
|
||||
=begin html
|
||||
<link href="syntax.css" rel="stylesheet" type="text/css">
|
||||
<script src="jquery.js"></script>
|
||||
<script src="syntax.js"></script>
|
||||
|
||||
=end html
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
Usage: bacnet.pl [program_options] [-- script_args]
|
||||
|
||||
This program executes a script in perl syntax to perform BACnet/IP operations.
|
||||
|
||||
Possible program options:
|
||||
--script=s The script to execute.
|
||||
--log=s The file to log all output.
|
||||
--help This help message.
|
||||
|
||||
Possible environment variables are:
|
||||
BACNET_IFACE - set this value to dotted IP address of the interface (see
|
||||
ipconfig) for which you want to bind. Default is the interface which
|
||||
Windows considers to be the default (how???). Hence, if there is only a
|
||||
single network interface on Windows, the applications will choose it, and
|
||||
this setting will not be needed.
|
||||
BACNET_IP_PORT - UDP/IP port number (0..65534) used for BACnet/IP
|
||||
communications. Default is 47808 (0xBAC0).
|
||||
BACNET_APDU_TIMEOUT - set this value in milliseconds to change the APDU
|
||||
timeout. APDU Timeout is how much time a client waits for a response from
|
||||
a BACnet device.
|
||||
BACNET_BBMD_PORT - UDP/IP port number (0..65534) used for Foreign Device
|
||||
Registration. Defaults to 47808 (0xBAC0).
|
||||
BACNET_BBMD_TIMETOLIVE - number of seconds used in Foreign Device
|
||||
Registration (0..65535). Defaults to 60000 seconds.
|
||||
BACNET_BBMD_ADDRESS - dotted IPv4 address of the BBMD or Foreign Device
|
||||
Registrar.
|
||||
|
||||
=cut
|
||||
|
||||
############################################
|
||||
# Steps to prepare for execution
|
||||
############################################
|
||||
|
||||
# This is the relative path to get to the base directory cotaining the BACnet
|
||||
# Stack sources from the directory containing this file and the directory
|
||||
# within which InlineC code is built. The reason for delaring it here and
|
||||
# setting the value in a BEGIN block is so that the variable gets its value at
|
||||
# compile time before Inline::C tries to use that variable.
|
||||
my $relSourcePath;
|
||||
my $inlineCFile;
|
||||
my $inlineBuildDir;
|
||||
my $libDir;
|
||||
my $incDir1;
|
||||
my $incDir2;
|
||||
my $incDir3;
|
||||
BEGIN {
|
||||
# the Perl source file is in the same directory as in the InlineC file
|
||||
# this path should not contain any spaces
|
||||
$relSourcePath = File::Spec->rel2abs(dirname($0));
|
||||
die "Install path must not have spaces.\n" if $relSourcePath =~ /\s/;
|
||||
my @dirs = ();
|
||||
push @dirs, $relSourcePath;
|
||||
$inlineCFile = File::Spec->catfile(@dirs, "perl_bindings.c");
|
||||
|
||||
# all Inline C sources shall be contained in ./.Inline
|
||||
push @dirs, ".Inline";
|
||||
$inlineBuildDir = File::Spec->catdir(@dirs);
|
||||
pop @dirs;
|
||||
|
||||
# to properly link, need to reference ./../../lib
|
||||
push @dirs, "..";
|
||||
push @dirs, "..";
|
||||
push @dirs, "lib";
|
||||
$libDir = File::Spec->catdir(@dirs);
|
||||
pop @dirs;
|
||||
|
||||
# to properly build, need to reference ./../../include
|
||||
push @dirs, "include";
|
||||
$incDir1 = File::Spec->catdir(@dirs);
|
||||
pop @dirs;
|
||||
|
||||
# we will use the demo handlers, need to reference ./../../demo/object
|
||||
push @dirs, "demo";
|
||||
push @dirs, "object";
|
||||
$incDir2 = File::Spec->catdir(@dirs);
|
||||
pop @dirs;
|
||||
pop @dirs;
|
||||
|
||||
# TODO: This should be done in a more universal way
|
||||
# to properly build Win32 ports, need to refrence ./../../ports/win32
|
||||
push @dirs, "ports";
|
||||
push @dirs, "win32";
|
||||
$incDir3 = File::Spec->catdir(@dirs);
|
||||
}
|
||||
|
||||
use Inline (
|
||||
C => Config =>
|
||||
LIBS => "-L$libDir -lbacnet -liphlpapi",
|
||||
INC => ["-I$incDir1", "-I$incDir2", "-I$incDir3"],
|
||||
DIRECTORY => $inlineBuildDir,
|
||||
);
|
||||
|
||||
# this is the C source file for interfacing to the library. Yes, this could be
|
||||
# done natively in Perl, but this is just as easy (and probably faster to
|
||||
# execute).
|
||||
use Inline C => "$inlineCFile";
|
||||
|
||||
|
||||
my $ask_help = 0;
|
||||
my $script;
|
||||
my $log;
|
||||
my $logTo = \*STDOUT;
|
||||
my $logIndent = 0;
|
||||
my $logIsQuiet = 0;
|
||||
my $errorMsg;
|
||||
my $answer = '';
|
||||
|
||||
($ask_help = 1) unless GetOptions(
|
||||
'help|?' => \$ask_help,
|
||||
'script=s' => \$script,
|
||||
'log=s' => \$log,
|
||||
);
|
||||
|
||||
if (!defined($script) || !(-f $script))
|
||||
{
|
||||
print "Bad or no script file scpecified.\n";
|
||||
$ask_help = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
# Add the script's location to @INC so that they can include other scripts
|
||||
# using relative paths
|
||||
my $scriptdir = File::Spec->rel2abs(dirname($script));
|
||||
push @INC,$scriptdir;
|
||||
}
|
||||
|
||||
if ($ask_help) {
|
||||
print "============================\n\n";
|
||||
pod2usage(
|
||||
-exitval => 0,
|
||||
-verbose => 99,
|
||||
-sections => "NAME|DESCRIPTION|OPTIONS"
|
||||
);
|
||||
}
|
||||
|
||||
if (defined($log))
|
||||
{
|
||||
open(LOG, ">$log") || croak "Cannot open $log for writing: $!\n";
|
||||
$logTo = \*LOG;
|
||||
}
|
||||
|
||||
# Pull in the BACnet enumerations from the C header file
|
||||
my %C_ENUMS;
|
||||
eval {
|
||||
my $pwd = File::Spec->rel2abs(File::Spec->curdir());
|
||||
|
||||
# let's get into the directory so that we can pull in the bacnet enumerations
|
||||
my @dirs = ();
|
||||
push @dirs, dirname($0);
|
||||
push @dirs, "../../include";
|
||||
chdir(File::Spec->catdir(@dirs));
|
||||
my $c = Convert::Binary::C->new->parse_file('bacenum.h');
|
||||
foreach my $typedef ($c->typedef)
|
||||
{
|
||||
if (ref($$typedef{type}) eq "HASH")
|
||||
{
|
||||
my $enumeration = \%{$C_ENUMS{$$typedef{declarator}}};
|
||||
foreach my $enum_name (keys %{$$typedef{type}{enumerators}})
|
||||
{
|
||||
${$C_ENUMS{$$typedef{declarator}}}{$enum_name} = ${$$typedef{type}{enumerators}}{$enum_name};
|
||||
}
|
||||
}
|
||||
}
|
||||
lock_hash(%C_ENUMS);
|
||||
chdir($pwd);
|
||||
};
|
||||
if ($EVAL_ERROR)
|
||||
{
|
||||
croak "Error pulling in the enumerations. $@\n";
|
||||
}
|
||||
|
||||
# Prepare things for communication
|
||||
BacnetPrepareComm();
|
||||
|
||||
# Execute the user specified script
|
||||
Log("Executing $script - start time " . scalar(localtime(time())) );
|
||||
unless (my $return = do $script)
|
||||
{
|
||||
croak "could not parse $script: $@" if $@;
|
||||
croak "could not pull in $script: $!" unless defined $return;
|
||||
croak "could not execute $script" unless $return;
|
||||
}
|
||||
Log("Finished executing $script - end time " . scalar(localtime(time())) );
|
||||
|
||||
=head1 This tool's API
|
||||
|
||||
In addition to having all standard Perl flow control, functions, and modules,
|
||||
the this tool provides an API for performing BACnet communication functions.
|
||||
|
||||
=cut
|
||||
|
||||
##########################################
|
||||
# This block is the external API
|
||||
##########################################
|
||||
|
||||
=head2 ReadProperty
|
||||
|
||||
This function implements the ReadProperty service. There are no built in retry
|
||||
mechanisms. NOTE: all enumerations are defined in F<bacenum.h>
|
||||
|
||||
=head3 Inputs to ReadProperty
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>devideInstance</b> - the instance number of the device we are reading</li>
|
||||
<li><b>objectName</b> - the enumeration for the object name we are reading</li>
|
||||
<li><b>objectInstance</b> - the instance number of the object we are reading</li>
|
||||
<li><b>propertyName</b> - the enumeration for the property name we are reading</li>
|
||||
<li><b>index</b> - Optional (default -1): the index number we are reading from. -1 if not applicable</li>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Outputs from ReadProperty
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>result</b> - the sting result (value or error) for ReadProperty</li>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Example of ReadProperty
|
||||
|
||||
The following example will read AV0.PresentValue from device 1234
|
||||
|
||||
my ($res, $failed) = ReadProperty(1234, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE');
|
||||
|
||||
=cut
|
||||
|
||||
sub ReadProperty {
|
||||
my $deviceInstance = shift;
|
||||
my $objectName = shift;
|
||||
my $objectInstance = shift;
|
||||
my $propertyName = shift;
|
||||
my $index = shift;
|
||||
my $isFailure = BindToDevice($deviceInstance);
|
||||
|
||||
# Loop for early exit
|
||||
while(1)
|
||||
{
|
||||
last if $isFailure;
|
||||
|
||||
my ($objectPrintName, $objectValue) = LookupEnumValue('BACNET_OBJECT_TYPE', $objectName);
|
||||
my ($propertyPrintName, $propertyValue) = LookupEnumValue('BACNET_PROPERTY_ID', $propertyName);
|
||||
|
||||
my $msg = "ReadProperty $objectPrintName" . '[' . $objectInstance . "].$propertyPrintName";
|
||||
if (defined($index))
|
||||
{
|
||||
$msg .= ".$index";
|
||||
} else {
|
||||
$index = -1;
|
||||
}
|
||||
$msg .= " from Device" . '[' . $deviceInstance . "] ==> ";
|
||||
|
||||
LogAnswer('', 0);
|
||||
if ( BacnetReadProperty($deviceInstance, $objectValue, $objectInstance, $propertyValue, $index) )
|
||||
{
|
||||
BacnetGetError($errorMsg);
|
||||
$msg .= "Problem: $errorMsg";
|
||||
$isFailure = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$msg .= $answer;
|
||||
$isFailure = 0;
|
||||
}
|
||||
Log($msg);
|
||||
last;
|
||||
}
|
||||
|
||||
return ($answer, $isFailure);
|
||||
}
|
||||
|
||||
=head2 ReadPropertyMultiple
|
||||
|
||||
This function implements the ReadPropertyMultiple service. There are no built in retry
|
||||
mechanisms. NOTE: all enumerations are defined in F<bacenum.h>
|
||||
|
||||
=head3 Inputs to ReadPropertyMultiple
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>devideInstance</b> - the instance number of the device we are reading</li>
|
||||
<li><b>r_answerList</b> - reference to a list where to store the answers</li>
|
||||
<li><b>list</b> - a list of ReadAccessSpecifications</li>
|
||||
<ul>
|
||||
<li><b>objectType</b> - the enumeration for the object name to read from</li>
|
||||
<li><b>objectInstance</b> - the instance number of the object we are reading</li>
|
||||
<li><b>propertyName</b> - the enumeration for the property name we are reading</li>
|
||||
<li><b>index</b> - the index number we are reading from. Use -1 if not applicable</li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Outputs from ReadPropertyMultiple
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>result</b> - the 'QQQ' delimited concatenated sting result (value or error) for ReadPropertyMultiple. The parsed out result is returned in r_answerList</li>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Example of ReadPropertyMultiple
|
||||
|
||||
The following example will read AV0.PresentValue and AV1.PresentValue from device 1234
|
||||
|
||||
my @RPM_request = ();
|
||||
my @RPM_answer = ();
|
||||
my $failed;
|
||||
push @RPM_request, ['OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE', -1];
|
||||
push @RPM_request, ['OBJECT_ANALOG_VALUE', 1, 'PROP_PRESENT_VALUE', -1];
|
||||
(undef, $failed) = ReadPropertyMultiple(1234, \@RPM_answer, @RPM_request);
|
||||
|
||||
=cut
|
||||
|
||||
sub ReadPropertyMultiple
|
||||
{
|
||||
my $deviceInstanceNumber = shift;
|
||||
my $r_answerList = shift;
|
||||
my @list = @ARG;
|
||||
my @modifiedList = ();
|
||||
my $msg = '';
|
||||
my $isFailure = BindToDevice($deviceInstanceNumber);
|
||||
|
||||
# loop for early exit
|
||||
while(1)
|
||||
{
|
||||
last if $isFailure;
|
||||
|
||||
Log("ReadPropertyMultiple:");
|
||||
$logIndent += 4;
|
||||
|
||||
foreach my $r_prop (@list)
|
||||
{
|
||||
my @tmpList = ();
|
||||
push @tmpList, $$r_prop[$_] for (0 .. 3);
|
||||
(undef, $tmpList[0]) = LookupEnumValue('BACNET_OBJECT_TYPE', $$r_prop[0]);
|
||||
(undef, $tmpList[2]) = LookupEnumValue('BACNET_PROPERTY_ID', $$r_prop[2]);
|
||||
push @modifiedList, \@tmpList;
|
||||
}
|
||||
|
||||
LogAnswer('', 0);
|
||||
@{$r_answerList} = ();
|
||||
if (BacnetReadPropertyMultiple($deviceInstanceNumber, @modifiedList))
|
||||
{
|
||||
BacnetGetError($errorMsg);
|
||||
Log("Problem: $errorMsg");
|
||||
$isFailure = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
my $i = 0;
|
||||
foreach (split('QQQ', $answer))
|
||||
{
|
||||
my ($objectPrintName, undef) = LookupEnumValue('BACNET_OBJECT_TYPE', $list[$i][0]);
|
||||
my ($propertyPrintName, undef) = LookupEnumValue('BACNET_PROPERTY_ID', $list[$i][2]);
|
||||
my $msg = $objectPrintName . '.[' . $list[$i][1] . '].' . $propertyPrintName;
|
||||
if ($list[$i][3] != -1)
|
||||
{
|
||||
$msg .= '.[' . $list[$i][3] . ']';
|
||||
}
|
||||
$msg .= " ==> $_";
|
||||
Log($msg);
|
||||
push @{$r_answerList}, $_;
|
||||
$i++;
|
||||
}
|
||||
$isFailure = 0;
|
||||
}
|
||||
|
||||
$logIndent -= 4;
|
||||
last;
|
||||
}
|
||||
|
||||
return ($answer, $isFailure);
|
||||
}
|
||||
|
||||
=head2 WriteProperty
|
||||
|
||||
This function implements the WriteProperty service. There are no built in retry
|
||||
mechanisms. NOTE: all enumerations are defined in F<bacenum.h>
|
||||
|
||||
=head3 Inputs to WriteProperty
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>devideInstance</b> - the instance number of the device we are writing</li>
|
||||
<li><b>objectName</b> - the enumeration for the object name we are writing</li>
|
||||
<li><b>objectInstance</b> - the instance number of the object we are writing</li>
|
||||
<li><b>propertyName</b> - the enumeration for the property name we are writing</li>
|
||||
<li><b>tagName</b> - the enumeration for the type of value we are writing. To specify context tags, prepend the tag name with "Cn:" where 'n' is the context number.</li>
|
||||
<li><b>value</b> - the value we are writing</li>
|
||||
<li><b>priority</b> - Optional (default 0): the priority within Priority Array to write at. Use 1-16 when specify priority, 0 to not specify priority.</li>
|
||||
<li><b>index</b> - Optional (default -1): the index within an array we are writing to. Use positive number to indicate index, -1 to not specify index.</li>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Outputs from WriteProperty
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>result</b> - the sting result (value or error) for WriteProperty</li>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Example of WriteProperty
|
||||
|
||||
The following example will write 1.0 to AV0.PresentValue in device 1234
|
||||
|
||||
my ($res, $failed) = WriteProperty(1234, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE', 'BACNET_APPLICATION_TAG_REAL', 1.0);
|
||||
|
||||
=cut
|
||||
|
||||
sub WriteProperty {
|
||||
my $deviceInstance = shift;
|
||||
my $objectName = shift;
|
||||
my $objectInstance = shift;
|
||||
my $propertyName = shift;
|
||||
my $tagName = shift;
|
||||
my $value = shift;
|
||||
my $priority = shift;
|
||||
my $index = shift;
|
||||
my $isFailure = BindToDevice($deviceInstance);
|
||||
|
||||
# loop for early exit
|
||||
while(1)
|
||||
{
|
||||
last if $isFailure;
|
||||
|
||||
my ($objectPrintName, $objectValue) = LookupEnumValue('BACNET_OBJECT_TYPE', $objectName);
|
||||
my ($propertyPrintName, $propertyValue) = LookupEnumValue('BACNET_PROPERTY_ID', $propertyName);
|
||||
|
||||
my $tagValue = '';
|
||||
if ($tagName =~ /^(C\d+):(.*)$/)
|
||||
{
|
||||
$tagName = $2;
|
||||
$tagValue = "$1 ";
|
||||
}
|
||||
my ($tagPrintName, $tagNewValue) = LookupEnumValue('BACNET_APPLICATION_TAG', $tagName);
|
||||
$tagValue .= $tagNewValue;
|
||||
|
||||
my $msg = "WriteProperty $tagPrintName:$value to $objectPrintName" . '[' . $objectInstance . "].$propertyPrintName";
|
||||
if (defined($index))
|
||||
{
|
||||
$msg .= '[' . $index . ']';
|
||||
}
|
||||
else
|
||||
{
|
||||
# an index of -1 means that we are not writing to an array
|
||||
$index = -1;
|
||||
}
|
||||
if (defined($priority))
|
||||
{
|
||||
$msg .= '@' . $priority
|
||||
}
|
||||
else
|
||||
{
|
||||
# a priority of 0 means we are not writing to a priority array
|
||||
$priority = 0;
|
||||
}
|
||||
$msg .= " in Device" . '[' . $deviceInstance . "] ==> ";
|
||||
|
||||
LogAnswer('', 0);
|
||||
if ( BacnetWriteProperty($deviceInstance, $objectValue, $objectInstance, $propertyValue, $priority, $index, $tagValue, $value) )
|
||||
{
|
||||
BacnetGetError($errorMsg);
|
||||
$msg .= "Problem: $errorMsg\n";
|
||||
$isFailure = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$msg .= $answer;
|
||||
$isFailure = 0;
|
||||
}
|
||||
Log($msg);
|
||||
last;
|
||||
}
|
||||
|
||||
return ($answer, $isFailure);
|
||||
}
|
||||
|
||||
=head2 TimeSync
|
||||
|
||||
This function implements the TimeSync and UTCTimeSync services
|
||||
|
||||
=head3 Inputs to TimeSync
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>deviceInstanceNumber</b> - the instance number of the device we are reading</li>
|
||||
<li><b>year</b> - Year (i.e. 2011)</li>
|
||||
<li><b>month</b> - Month (i.e. 11 for November)</li>
|
||||
<li><b>day</b> - Day (i.e. 1 for first of month)</li>
|
||||
<li><b>hour</b> - Hour (i.e. 23 for 11pm)</li>
|
||||
<li><b>minute</b> - Minute (i.e. 0-59)</li>
|
||||
<li><b>second</b> - Second (i,e. 0-59)</li>
|
||||
<li><b>utcOffset</b> - Optional: if specified defines the UTC offset and forces UTCTimeSync</li>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Outputs from TimeSync
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Example of TimeSync
|
||||
|
||||
$isFailure = TimeSync($deviceInstance, $1, $2, $3, $4, $5, $6) unless $isFailure;
|
||||
|
||||
=cut
|
||||
|
||||
sub TimeSync
|
||||
{
|
||||
my $deviceInstanceNumber = shift;
|
||||
my $year = shift;
|
||||
my $month = shift;
|
||||
my $day = shift;
|
||||
my $hour = shift;
|
||||
my $minute = shift;
|
||||
my $second = shift;
|
||||
my $utcOffset = shift;
|
||||
my $isUTC;
|
||||
|
||||
my $isFailure = BindToDevice($deviceInstanceNumber);
|
||||
|
||||
# loop for early exit
|
||||
while(1)
|
||||
{
|
||||
last if $isFailure;
|
||||
|
||||
# be a pessimist. Assume things will fail
|
||||
$isFailure = 1;
|
||||
|
||||
if (defined($utcOffset))
|
||||
{
|
||||
$isUTC = 1;
|
||||
Log("UTC Time Sync not yet supported.");
|
||||
last;
|
||||
}
|
||||
else
|
||||
{
|
||||
$utcOffset = 0;
|
||||
$isUTC = 0;
|
||||
}
|
||||
|
||||
if ($year < 1900 || $year > 2099)
|
||||
{
|
||||
Log("Year '$year' is invalid.");
|
||||
last;
|
||||
}
|
||||
|
||||
if ($month <= 0 || $month > 12)
|
||||
{
|
||||
Log("Month '$month' is invalid.");
|
||||
last;
|
||||
}
|
||||
|
||||
if ($day <= 0 || $day > 31)
|
||||
{
|
||||
Log("Day '$day' is invalid.");
|
||||
last;
|
||||
}
|
||||
|
||||
if ($hour < 0 || $hour > 23)
|
||||
{
|
||||
Log("Hour '$hour' is invalid.");
|
||||
last;
|
||||
}
|
||||
|
||||
if ($minute < 0 || $minute > 59)
|
||||
{
|
||||
Log("Minute '$minute' is invalid.");
|
||||
last;
|
||||
}
|
||||
|
||||
if ($second < 0 || $second > 59)
|
||||
{
|
||||
Log("Second '$second' is invalid.");
|
||||
last;
|
||||
}
|
||||
|
||||
Log("TimeSync: Device[$deviceInstanceNumber] $year/$month/$day $hour:$minute:$second");
|
||||
|
||||
$isFailure = BacnetTimeSync($deviceInstanceNumber, $year, $month, $day, $hour, $minute, $second, $isUTC, $utcOffset);
|
||||
last;
|
||||
}
|
||||
|
||||
return $isFailure;
|
||||
}
|
||||
|
||||
=head2 Log
|
||||
|
||||
This function prints out to the desired method of logging (STDOUT or file).
|
||||
NewLine characters are not required when making calls to this function. If any
|
||||
NewLine characters are specified, they will be stripped out. To print an empty
|
||||
line, pass in a space as the message. NOTE: This function will honor previous
|
||||
requests to silence the log (see SilcenseLog for details)
|
||||
|
||||
=head3 Inputs to Log
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>msg</b> - the message to output
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Example of Log
|
||||
|
||||
The following example will print out "hello world"
|
||||
|
||||
Log("Hello World");
|
||||
|
||||
=cut
|
||||
|
||||
###############################################################################
|
||||
# Global Variables affecting this function
|
||||
# logIsQuiet do not print anytihng if the log was qieted
|
||||
# logIndent how many spaces to put in front of every logged line
|
||||
###############################################################################
|
||||
sub Log {
|
||||
my $msg = shift;
|
||||
|
||||
if (defined($msg) && !$logIsQuiet)
|
||||
{
|
||||
my @last = split('', substr($msg, -2));
|
||||
|
||||
# if there is nothing to print, then don't do it
|
||||
return if (scalar(@last) == 0);
|
||||
|
||||
# if there are newline-like characters, get rid of them.
|
||||
while ($msg =~/^(.*)[\r\n]+(.*)$/)
|
||||
{
|
||||
$msg = $1 . $2;
|
||||
}
|
||||
|
||||
local $OUTPUT_RECORD_SEPARATOR = "\n";
|
||||
print $logTo ' ' x $logIndent . $msg;
|
||||
}
|
||||
}
|
||||
|
||||
=head2 SilenceLog
|
||||
|
||||
This function requests that all future log messages be either suppressed or
|
||||
enabled.
|
||||
|
||||
=head3 Inputs to SilenceLog
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>logIsQuiet</b> - zero means print to log, non-zero means supress log
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Outputs from SilenceLog
|
||||
|
||||
The previous value of whether or not the log was silenced before caling this
|
||||
function.
|
||||
|
||||
=head3 Example of SilenceLog
|
||||
|
||||
The following example will print out "hello", but not "world"
|
||||
|
||||
Log("Hello");
|
||||
SilenceLog(1);
|
||||
Log("World");
|
||||
|
||||
=cut
|
||||
|
||||
sub SilenceLog {
|
||||
my $prevValue = $logIsQuiet;
|
||||
$logIsQuiet = shift;
|
||||
return $prevValue;
|
||||
}
|
||||
|
||||
=head2 Retry
|
||||
|
||||
This function will try to execute the requested command up to specified number
|
||||
of times, awaiting the requested answer, with a specified pause between
|
||||
retries. NOTE: the only functions which can be executed by this function are
|
||||
ones which return two parameres in the form of ($response, $isFailure)
|
||||
|
||||
=head3 Inputs to Retry
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>r_func</b> - The reference to the function which is to be retried</li>
|
||||
<li><b>r_funcArgs</b> - A reference to an array of arguments for the function to be executed</li>
|
||||
<li><b>desiredOutput</b> - The condition which will terminate the retrying. Can be either a number or a regexp to patch against the $response return of the function</li>
|
||||
<li><b>maxTries</b> - The maximum number of retry attempts before calling it quits</li>
|
||||
<li><b>sleepSeconds</b> - The number of seconds (could be fractional) to wait between retries</li>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Outputs from Retry
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>$resp</b> - The response from the last execution of requested function</li>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Example of Retry
|
||||
|
||||
The following example will execute the ReadProperty function to read a property
|
||||
from an object (see ReadProperty for details on those arguments) with up to
|
||||
$maxRetries retries (with $retryDelay delay between retries) or unitl the
|
||||
desired answer of 42 is received.
|
||||
|
||||
my ($resp, $isFailure) = Retry(
|
||||
\&ReadProperty, [$deviceInstance, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE'],
|
||||
42, $maxRetries, $retryDelay
|
||||
);
|
||||
if ($isFailure)
|
||||
{
|
||||
die "Value was not 42. Last response was '$resp'";
|
||||
}
|
||||
|
||||
The following example will try to execute a WriteProperty (see that function for
|
||||
details on its arguments) until the write succeeds.
|
||||
|
||||
my ($resp, $isFailure) = Retry(
|
||||
\&WriteProperty, [$deviceInstance, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE', 'BACNET_APPLICATION_TAG_REAL', 42.0],
|
||||
"Acknowledged", $maxRetries, $retryDelay
|
||||
);
|
||||
if ($isFailure)
|
||||
{
|
||||
die "Could not write 42. Last response was '$resp'";
|
||||
}
|
||||
|
||||
=cut
|
||||
sub Retry {
|
||||
my $r_func = shift;
|
||||
my $r_funcArgs = shift;
|
||||
my $desiredOutput = shift;
|
||||
my $maxTries = shift;
|
||||
my $sleepSeconds = shift;
|
||||
|
||||
my ($resp, $failed);
|
||||
|
||||
my $i;
|
||||
for ($i=0; $i<$maxTries; $i++)
|
||||
{
|
||||
($resp, $failed) = &{$r_func}(@{$r_funcArgs});
|
||||
unless ($failed)
|
||||
{
|
||||
if (looks_like_number($desiredOutput))
|
||||
{
|
||||
last if (looks_like_number($resp) && ($resp == $desiredOutput));
|
||||
}
|
||||
else
|
||||
{
|
||||
last if ($resp =~ /$desiredOutput/);
|
||||
}
|
||||
}
|
||||
select(undef, undef, undef, $sleepSeconds);
|
||||
}
|
||||
|
||||
return ($resp, ($i == $maxTries));
|
||||
}
|
||||
|
||||
|
||||
##########################################
|
||||
# These are the supporting functions
|
||||
##########################################
|
||||
|
||||
sub LookupEnumValue {
|
||||
my $enumType = shift;
|
||||
my $enumName = shift;
|
||||
my $printName;
|
||||
|
||||
if (!exists($C_ENUMS{$enumType}{$enumName}))
|
||||
{
|
||||
print "Requested enumeration '$enumName' does not exist within '$enumType'.\n";
|
||||
exit -1;
|
||||
}
|
||||
|
||||
# lookup the value
|
||||
my $value = $C_ENUMS{$enumType}{$enumName};
|
||||
|
||||
# reformat the OBJECT name style
|
||||
my %reformat = (
|
||||
'BACNET_PROPERTY_ID' => 'PROP',
|
||||
'BACNET_OBJECT_TYPE' => 'OBJECT',
|
||||
'BACNET_APPLICATION_TAG' => 'BACNET_APPLICATION_TAG',
|
||||
);
|
||||
|
||||
if (exists($reformat{$enumType}))
|
||||
{
|
||||
if ($enumName =~ /$reformat{$enumType}_(.*)/)
|
||||
{
|
||||
$printName = '';
|
||||
$printName .= ucfirst lc $_ foreach (split('_', $1));
|
||||
}
|
||||
}
|
||||
|
||||
return ($printName, $value);
|
||||
}
|
||||
|
||||
sub BindToDevice {
|
||||
my $deviceInstance = shift;
|
||||
my $isFailure = 0;
|
||||
|
||||
if ( BacnetBindToDevice($deviceInstance) )
|
||||
{
|
||||
BacnetGetError($errorMsg);
|
||||
Log("Problem binding to deivce $deviceInstance: $errorMsg\n");
|
||||
$isFailure = 1;
|
||||
}
|
||||
|
||||
return $isFailure;
|
||||
}
|
||||
|
||||
sub LogAnswer {
|
||||
my $newAnswer = shift;
|
||||
my $append = shift;
|
||||
|
||||
$answer = '' unless $append;
|
||||
$answer .= $newAnswer;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
my (
|
||||
$device, # device instance number
|
||||
$objectName, # object type name
|
||||
$objectInst, # object instance number
|
||||
$propName, # property name
|
||||
$index, # property index
|
||||
);
|
||||
|
||||
GetOptions(
|
||||
'device=i' => \$device,
|
||||
'objName=s' => \$objectName,
|
||||
'objInst=i' => \$objectInst,
|
||||
'property=s' => \$propName,
|
||||
'index=i' => \$index,
|
||||
);
|
||||
|
||||
Help() unless ( defined($device) &&
|
||||
defined($objectName) &&
|
||||
defined($objectInst) &&
|
||||
defined($propName)
|
||||
);
|
||||
|
||||
my ($resp, $failed) = ReadProperty($device, $objectName, $objectInst, $propName, $index);
|
||||
print "status was '$failed' and the response was '$resp'\n";
|
||||
|
||||
sub Help {
|
||||
print <<END;
|
||||
|
||||
This script demonstrates the ReadProperty service functionality using Perl
|
||||
bindings. To run this script, you must specify the following arguments to it:
|
||||
* device This is the device instance number (i.e. 1234)
|
||||
* objName This is the object type name (i.e. OBJECT_ANALOG_VALUE). See
|
||||
include/bacenum.h for complete list
|
||||
* objInst This is the object instance number you want to read (i.e. 1)
|
||||
* property This is the name of the property you want to read (i.e.
|
||||
PROP_PRESENT_VALUE). See include/bacenum.h for complete list
|
||||
* index This is an optional parameter. If you want to read from a specific
|
||||
index, then specify here (i.e. 1). Otherwise, don't specify this
|
||||
option.
|
||||
|
||||
As a complete example, to run this script using the main bacnet tool to read
|
||||
AnalogValue1.PresentValue from device instance 1234, use
|
||||
|
||||
perl bacnet.pl --script example_readprop.pl -- --device=1234 --objName=OBJECT_ANALOG_VALUE --objInst=1 --property=PROP_PRESENT_VALUE
|
||||
|
||||
END
|
||||
exit 1;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,933 @@
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include <time.h>
|
||||
#include "bacnet/arf.h"
|
||||
|
||||
/* Free is redefined as a macro, but Perl does not like that. */
|
||||
#undef free
|
||||
|
||||
/* global variables used in this file */
|
||||
static uint32_t Target_Device_Object_Instance = 4194303;
|
||||
static unsigned Target_Max_APDU = 0;
|
||||
static bool Error_Detected = false;
|
||||
static BACNET_ADDRESS Target_Address;
|
||||
static uint8_t Request_Invoke_ID = 0;
|
||||
static bool isReadPropertyHandlerRegistered = false;
|
||||
static bool isReadPropertyMultipleHandlerRegistered = false;
|
||||
static bool isWritePropertyHandlerRegistered = false;
|
||||
static bool isAtomicWriteFileHandlerRegistered = false;
|
||||
static bool isAtomicReadFileHandlerRegistered = false;
|
||||
|
||||
/****************************************/
|
||||
/* Logging Support */
|
||||
/****************************************/
|
||||
#define MAX_ERROR_STRING 128
|
||||
#define NO_ERROR "No Error"
|
||||
static char Last_Error[MAX_ERROR_STRING] = NO_ERROR;
|
||||
static void LogError(const char *msg)
|
||||
{
|
||||
strcpy(Last_Error, msg);
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
void BacnetGetError(SV *errorMsg)
|
||||
{
|
||||
sv_setpv(errorMsg, Last_Error);
|
||||
strcpy(Last_Error, NO_ERROR);
|
||||
Error_Detected = false;
|
||||
}
|
||||
|
||||
static void __LogAnswer(const char *msg, unsigned append)
|
||||
{
|
||||
dSP;
|
||||
ENTER;
|
||||
SAVETMPS;
|
||||
PUSHMARK(SP);
|
||||
XPUSHs(sv_2mortal(newSVpv(msg, 0)));
|
||||
XPUSHs(sv_2mortal(newSViv(append)));
|
||||
PUTBACK;
|
||||
call_pv("LogAnswer", G_DISCARD);
|
||||
FREETMPS;
|
||||
LEAVE;
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
/* error handlers */
|
||||
/*************************************/
|
||||
static void MyAbortHandler(
|
||||
BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t abort_reason, bool server)
|
||||
{
|
||||
(void)server;
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
char msg[MAX_ERROR_STRING];
|
||||
sprintf(msg, "BACnet Abort: %s",
|
||||
bactext_abort_reason_name((int)abort_reason));
|
||||
LogError(msg);
|
||||
}
|
||||
}
|
||||
|
||||
static void MyRejectHandler(
|
||||
BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t reject_reason)
|
||||
{
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
char msg[MAX_ERROR_STRING];
|
||||
sprintf(msg, "BACnet Reject: %s",
|
||||
bactext_reject_reason_name((int)reject_reason));
|
||||
LogError(msg);
|
||||
}
|
||||
}
|
||||
|
||||
static void My_Error_Handler(BACNET_ADDRESS *src,
|
||||
uint8_t invoke_id,
|
||||
BACNET_ERROR_CLASS error_class,
|
||||
BACNET_ERROR_CODE error_code)
|
||||
{
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
char msg[MAX_ERROR_STRING];
|
||||
sprintf(msg, "BACnet Error: %s: %s",
|
||||
bactext_error_class_name((int)error_class),
|
||||
bactext_error_code_name((int)error_code));
|
||||
LogError(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************/
|
||||
/* ACK handlers */
|
||||
/**********************************/
|
||||
|
||||
/*****************************************/
|
||||
/* Decode the ReadProperty Ack and pass to perl */
|
||||
/****************************************/
|
||||
#define MAX_ACK_STRING 512
|
||||
void rp_ack_extract_data(BACNET_READ_PROPERTY_DATA *data)
|
||||
{
|
||||
char ackString[MAX_ACK_STRING] = "";
|
||||
char *pAckString = &ackString[0];
|
||||
BACNET_OBJECT_PROPERTY_VALUE object_value; /* for bacapp printing */
|
||||
BACNET_APPLICATION_DATA_VALUE value; /* for decode value data */
|
||||
int len = 0;
|
||||
uint8_t *application_data;
|
||||
int application_data_len;
|
||||
bool first_value = true;
|
||||
bool print_brace = false;
|
||||
|
||||
if (data) {
|
||||
application_data = data->application_data;
|
||||
application_data_len = data->application_data_len;
|
||||
/* FIXME: what if application_data_len is bigger than 255? */
|
||||
/* value? need to loop until all of the len is gone... */
|
||||
for (;;) {
|
||||
len = bacapp_decode_application_data(
|
||||
application_data, (uint8_t)application_data_len, &value);
|
||||
if (first_value && (len < application_data_len)) {
|
||||
first_value = false;
|
||||
strncat(pAckString, "{", 1);
|
||||
pAckString += 1;
|
||||
print_brace = true;
|
||||
}
|
||||
object_value.object_type = data->object_type;
|
||||
object_value.object_instance = data->object_instance;
|
||||
object_value.object_property = data->object_property;
|
||||
object_value.array_index = data->array_index;
|
||||
object_value.value = &value;
|
||||
bacapp_snprintf_value(pAckString,
|
||||
MAX_ACK_STRING - (pAckString - ackString), &object_value);
|
||||
if (len > 0) {
|
||||
if (len < application_data_len) {
|
||||
application_data += len;
|
||||
application_data_len -= len;
|
||||
/* there's more! */
|
||||
strncat(pAckString, ",", 1);
|
||||
pAckString += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (print_brace) {
|
||||
strncat(pAckString, "}", 1);
|
||||
pAckString += 1;
|
||||
}
|
||||
/* Now let's call a Perl function to display the data */
|
||||
__LogAnswer(ackString, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************/
|
||||
/* Decode the ReadPropertyMultiple Ack and pass to perl */
|
||||
/****************************************/
|
||||
void rpm_ack_extract_data(BACNET_READ_ACCESS_DATA *rpm_data)
|
||||
{
|
||||
BACNET_OBJECT_PROPERTY_VALUE object_value; /* for bacapp printing */
|
||||
BACNET_PROPERTY_REFERENCE *listOfProperties;
|
||||
BACNET_APPLICATION_DATA_VALUE *value;
|
||||
bool array_value = false;
|
||||
char ackString[MAX_ACK_STRING] = "";
|
||||
char *pAckString = &ackString[0];
|
||||
|
||||
if (rpm_data) {
|
||||
listOfProperties = rpm_data->listOfProperties;
|
||||
while (listOfProperties) {
|
||||
value = listOfProperties->value;
|
||||
if (value) {
|
||||
if (value->next) {
|
||||
strncat(pAckString, "{", 1);
|
||||
pAckString++;
|
||||
array_value = true;
|
||||
} else {
|
||||
array_value = false;
|
||||
}
|
||||
object_value.object_type = rpm_data->object_type;
|
||||
object_value.object_instance = rpm_data->object_instance;
|
||||
while (value) {
|
||||
object_value.object_property =
|
||||
listOfProperties->propertyIdentifier;
|
||||
object_value.array_index =
|
||||
listOfProperties->propertyArrayIndex;
|
||||
object_value.value = value;
|
||||
bacapp_snprintf_value(pAckString,
|
||||
MAX_ACK_STRING - (pAckString - ackString),
|
||||
&object_value);
|
||||
if (value->next) {
|
||||
strncat(pAckString, ",", 1);
|
||||
pAckString++;
|
||||
} else {
|
||||
if (array_value) {
|
||||
strncat(pAckString, "}", 1);
|
||||
pAckString++;
|
||||
}
|
||||
}
|
||||
value = value->next;
|
||||
}
|
||||
} else {
|
||||
/* AccessError */
|
||||
sprintf(ackString, "BACnet Error: %s: %s",
|
||||
bactext_error_class_name(
|
||||
(int)listOfProperties->error.error_class),
|
||||
bactext_error_code_name(
|
||||
(int)listOfProperties->error.error_code));
|
||||
LogError(ackString);
|
||||
}
|
||||
listOfProperties = listOfProperties->next;
|
||||
|
||||
/* Add a separator between consecutive entries so that Perl can */
|
||||
/* parse this out */
|
||||
strncat(pAckString, "QQQ", 3);
|
||||
pAckString += 3;
|
||||
}
|
||||
|
||||
/* Now let's call a Perl function to display the data */
|
||||
__LogAnswer(ackString, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void AtomicReadFileAckHandler(uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS *src,
|
||||
BACNET_CONFIRMED_SERVICE_ACK_DATA *service_data)
|
||||
{
|
||||
int len = 0;
|
||||
BACNET_ATOMIC_READ_FILE_DATA data;
|
||||
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(service_data->invoke_id == Request_Invoke_ID)) {
|
||||
len =
|
||||
arf_ack_decode_service_request(service_request, service_len, &data);
|
||||
if (len > 0) {
|
||||
/* validate the parameters before storing data */
|
||||
if ((data.access == FILE_STREAM_ACCESS) &&
|
||||
(service_data->invoke_id == Request_Invoke_ID)) {
|
||||
char msg[32];
|
||||
uint8_t *pFileData;
|
||||
int i;
|
||||
|
||||
sprintf(msg, "EOF=%d,start=%d,", data.endOfFile,
|
||||
data.type.stream.fileStartPosition);
|
||||
__LogAnswer(msg, 0);
|
||||
|
||||
pFileData = octetstring_value(&data.fileData);
|
||||
for (i = 0; i < octetstring_length(&data.fileData); i++) {
|
||||
sprintf(msg, "%02x ", *pFileData);
|
||||
__LogAnswer(msg, 1);
|
||||
pFileData++;
|
||||
}
|
||||
} else {
|
||||
LogError("Bad stream access reported");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Handler for a ReadProperty ACK.
|
||||
* @ingroup DSRP
|
||||
* Doesn't actually do anything, except, for debugging, to
|
||||
* print out the ACK data of a matching request.
|
||||
*
|
||||
* @param service_request [in] The contents of the service request.
|
||||
* @param service_len [in] The length of the service_request.
|
||||
* @param src [in] BACNET_ADDRESS of the source of the message
|
||||
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
|
||||
* decoded from the APDU header of this message.
|
||||
*/
|
||||
static void My_Read_Property_Ack_Handler(uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS *src,
|
||||
BACNET_CONFIRMED_SERVICE_ACK_DATA *service_data)
|
||||
{
|
||||
int len = 0;
|
||||
BACNET_READ_PROPERTY_DATA data;
|
||||
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(service_data->invoke_id == Request_Invoke_ID)) {
|
||||
len =
|
||||
rp_ack_decode_service_request(service_request, service_len, &data);
|
||||
if (len > 0) {
|
||||
rp_ack_extract_data(&data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Handler for a ReadPropertyMultiple ACK.
|
||||
* @ingroup DSRPM
|
||||
* For each read property, print out the ACK'd data,
|
||||
* and free the request data items from linked property list.
|
||||
*
|
||||
* @param service_request [in] The contents of the service request.
|
||||
* @param service_len [in] The length of the service_request.
|
||||
* @param src [in] BACNET_ADDRESS of the source of the message
|
||||
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
|
||||
* decoded from the APDU header of this message.
|
||||
*/
|
||||
static void My_Read_Property_Multiple_Ack_Handler(uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS *src,
|
||||
BACNET_CONFIRMED_SERVICE_ACK_DATA *service_data)
|
||||
{
|
||||
int len = 0;
|
||||
BACNET_READ_ACCESS_DATA *rpm_data;
|
||||
BACNET_READ_ACCESS_DATA *old_rpm_data;
|
||||
BACNET_PROPERTY_REFERENCE *rpm_property;
|
||||
BACNET_PROPERTY_REFERENCE *old_rpm_property;
|
||||
BACNET_APPLICATION_DATA_VALUE *value;
|
||||
BACNET_APPLICATION_DATA_VALUE *old_value;
|
||||
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(service_data->invoke_id == Request_Invoke_ID)) {
|
||||
rpm_data = calloc(1, sizeof(BACNET_READ_ACCESS_DATA));
|
||||
if (rpm_data) {
|
||||
len = rpm_ack_decode_service_request(
|
||||
service_request, service_len, rpm_data);
|
||||
}
|
||||
if (len > 0) {
|
||||
while (rpm_data) {
|
||||
rpm_ack_extract_data(rpm_data);
|
||||
rpm_property = rpm_data->listOfProperties;
|
||||
while (rpm_property) {
|
||||
value = rpm_property->value;
|
||||
while (value) {
|
||||
old_value = value;
|
||||
value = value->next;
|
||||
free(old_value);
|
||||
}
|
||||
old_rpm_property = rpm_property;
|
||||
rpm_property = rpm_property->next;
|
||||
free(old_rpm_property);
|
||||
}
|
||||
old_rpm_data = rpm_data;
|
||||
rpm_data = rpm_data->next;
|
||||
free(old_rpm_data);
|
||||
}
|
||||
} else {
|
||||
LogError("RPM Ack Malformed! Freeing memory...");
|
||||
while (rpm_data) {
|
||||
rpm_property = rpm_data->listOfProperties;
|
||||
while (rpm_property) {
|
||||
value = rpm_property->value;
|
||||
while (value) {
|
||||
old_value = value;
|
||||
value = value->next;
|
||||
free(old_value);
|
||||
}
|
||||
old_rpm_property = rpm_property;
|
||||
rpm_property = rpm_property->next;
|
||||
free(old_rpm_property);
|
||||
}
|
||||
old_rpm_data = rpm_data;
|
||||
rpm_data = rpm_data->next;
|
||||
free(old_rpm_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void My_Write_Property_SimpleAck_Handler(BACNET_ADDRESS *src, uint8_t invoke_id)
|
||||
{
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
__LogAnswer("WriteProperty Acknowledged!", 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void Init_Service_Handlers()
|
||||
{
|
||||
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);
|
||||
|
||||
/* 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 generic errors coming back */
|
||||
apdu_set_abort_handler(MyAbortHandler);
|
||||
apdu_set_reject_handler(MyRejectHandler);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
waitAnswer,
|
||||
waitBind,
|
||||
} waitAction;
|
||||
|
||||
static void Wait_For_Answer_Or_Timeout(unsigned timeout_ms, waitAction action)
|
||||
{
|
||||
/* Wait for timeout, failure, or success */
|
||||
time_t last_seconds = time(NULL);
|
||||
time_t timeout_seconds = (apdu_timeout() / 1000) * apdu_retries();
|
||||
time_t elapsed_seconds = 0;
|
||||
uint16_t pdu_len = 0;
|
||||
BACNET_ADDRESS src = { 0 }; /* address where message came from */
|
||||
uint8_t Rx_Buf[MAX_MPDU] = { 0 };
|
||||
|
||||
while (true) {
|
||||
time_t current_seconds = time(NULL);
|
||||
|
||||
/* If error was detected then bail out */
|
||||
if (Error_Detected) {
|
||||
LogError("Some other error occurred");
|
||||
break;
|
||||
}
|
||||
|
||||
if (elapsed_seconds > timeout_seconds) {
|
||||
LogError("APDU Timeout");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Process PDU if one comes in */
|
||||
pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout_ms);
|
||||
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));
|
||||
}
|
||||
|
||||
if (action == waitAnswer) {
|
||||
/* Response was received. Exit. */
|
||||
if (tsm_invoke_id_free(Request_Invoke_ID)) {
|
||||
break;
|
||||
} else if (tsm_invoke_id_failed(Request_Invoke_ID)) {
|
||||
LogError("TSM Timeout!");
|
||||
tsm_free_invoke_id(Request_Invoke_ID);
|
||||
break;
|
||||
}
|
||||
} else if (action == waitBind) {
|
||||
if (address_bind_request(Target_Device_Object_Instance,
|
||||
&Target_Max_APDU, &Target_Address)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LogError("Invalid waitAction requested");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Keep track of time */
|
||||
elapsed_seconds += (current_seconds - last_seconds);
|
||||
last_seconds = current_seconds;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************/
|
||||
/* Interface API */
|
||||
/****************************************************/
|
||||
|
||||
/****************************************************/
|
||||
/* This is the most fundamental setup needed to start communication */
|
||||
/****************************************************/
|
||||
void BacnetPrepareComm()
|
||||
{
|
||||
/* setup my info */
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
address_init();
|
||||
Init_Service_Handlers();
|
||||
dlenv_init();
|
||||
}
|
||||
|
||||
/****************************************************/
|
||||
/* Try to bind to a device. If successful, return zero. If failure, return */
|
||||
/* non-zero and log the error details */
|
||||
/****************************************************/
|
||||
int BacnetBindToDevice(int deviceInstanceNumber)
|
||||
{
|
||||
int isFailure = 0;
|
||||
|
||||
/* Store the requested device instance number in the global variable for */
|
||||
/* reference in other communication routines */
|
||||
Target_Device_Object_Instance = deviceInstanceNumber;
|
||||
|
||||
/* try to bind with the device */
|
||||
if (!address_bind_request(
|
||||
deviceInstanceNumber, &Target_Max_APDU, &Target_Address)) {
|
||||
Send_WhoIs(
|
||||
Target_Device_Object_Instance, Target_Device_Object_Instance);
|
||||
|
||||
/* Wait for timeout, failure, or success */
|
||||
Wait_For_Answer_Or_Timeout(100, waitBind);
|
||||
}
|
||||
/* Clean up after ourselves */
|
||||
isFailure = Error_Detected;
|
||||
Error_Detected = false;
|
||||
return isFailure;
|
||||
}
|
||||
|
||||
/****************************************************/
|
||||
/* This is the interface to ReadProperty */
|
||||
/****************************************************/
|
||||
int BacnetReadProperty(int deviceInstanceNumber,
|
||||
int objectType,
|
||||
int objectInstanceNumber,
|
||||
int objectProperty,
|
||||
int objectIndex)
|
||||
{
|
||||
if (!isReadPropertyHandlerRegistered) {
|
||||
/* handle the data coming back from confirmed requests */
|
||||
apdu_set_confirmed_ack_handler(
|
||||
SERVICE_CONFIRMED_READ_PROPERTY, My_Read_Property_Ack_Handler);
|
||||
|
||||
/* handle any errors coming back */
|
||||
apdu_set_error_handler(
|
||||
SERVICE_CONFIRMED_READ_PROPERTY, My_Error_Handler);
|
||||
|
||||
/* indicate that handlers are now registered */
|
||||
isReadPropertyHandlerRegistered = true;
|
||||
}
|
||||
/* Send the message out */
|
||||
Request_Invoke_ID = Send_Read_Property_Request(deviceInstanceNumber,
|
||||
objectType, objectInstanceNumber, objectProperty, objectIndex);
|
||||
Wait_For_Answer_Or_Timeout(100, waitAnswer);
|
||||
|
||||
int isFailure = Error_Detected;
|
||||
Error_Detected = 0;
|
||||
return isFailure;
|
||||
}
|
||||
|
||||
/************************************************/
|
||||
/* This is the interface to ReadPropertyMultiple */
|
||||
/************************************************/
|
||||
int BacnetReadPropertyMultiple(int deviceInstanceNumber, ...)
|
||||
{
|
||||
/* Get the variable argument list from the stack */
|
||||
Inline_Stack_Vars;
|
||||
int rpmIndex = 1;
|
||||
BACNET_READ_ACCESS_DATA *rpm_object =
|
||||
calloc(1, sizeof(BACNET_READ_ACCESS_DATA));
|
||||
BACNET_READ_ACCESS_DATA *Read_Access_Data = rpm_object;
|
||||
BACNET_PROPERTY_REFERENCE *rpm_property;
|
||||
uint8_t buffer[MAX_PDU] = { 0 };
|
||||
|
||||
while (rpmIndex < Inline_Stack_Items) {
|
||||
SV *pSV = Inline_Stack_Item(rpmIndex++);
|
||||
|
||||
/* Make sure the argument is an Array Reference */
|
||||
if (SvTYPE(SvRV(pSV)) != SVt_PVAV) {
|
||||
LogError("Argument is not an Array reference");
|
||||
break;
|
||||
}
|
||||
/* Make sure we can access the memory */
|
||||
if (rpm_object) {
|
||||
rpm_object->listOfProperties = NULL;
|
||||
} else {
|
||||
LogError("Memory Allocation Issue");
|
||||
break;
|
||||
}
|
||||
|
||||
AV *pAV = (AV *)SvRV(pSV);
|
||||
SV **ppSV;
|
||||
|
||||
/* The 0th argument is the object type */
|
||||
ppSV = av_fetch(pAV, 0, 0);
|
||||
if (ppSV) {
|
||||
rpm_object->object_type = SvIV(*ppSV);
|
||||
} else {
|
||||
LogError("Problem parsing the Array of arguments");
|
||||
break;
|
||||
}
|
||||
|
||||
/* The 1st argument is the object instance */
|
||||
ppSV = av_fetch(pAV, 1, 0);
|
||||
if (ppSV) {
|
||||
rpm_object->object_instance = SvIV(*ppSV);
|
||||
} else {
|
||||
LogError("Problem parsing the Array of arguments");
|
||||
break;
|
||||
}
|
||||
|
||||
/* The 2nd argument is the property type */
|
||||
ppSV = av_fetch(pAV, 2, 0);
|
||||
if (ppSV) {
|
||||
rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE));
|
||||
rpm_object->listOfProperties = rpm_property;
|
||||
if (rpm_property) {
|
||||
rpm_property->propertyIdentifier = SvIV(*ppSV);
|
||||
} else {
|
||||
LogError("Memory allocation error");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LogError("Problem parsing the Array of arguments");
|
||||
break;
|
||||
}
|
||||
|
||||
/* The 3rd argument is the property index */
|
||||
ppSV = av_fetch(pAV, 3, 0);
|
||||
if (ppSV) {
|
||||
rpm_property->propertyArrayIndex = SvIV(*ppSV);
|
||||
} else {
|
||||
LogError("Problem parsing the Array of arguments");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Advance to the next RPM index */
|
||||
if (rpmIndex < Inline_Stack_Items) {
|
||||
rpm_object->next = calloc(1, sizeof(BACNET_READ_ACCESS_DATA));
|
||||
rpm_object = rpm_object->next;
|
||||
} else {
|
||||
rpm_object->next = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isReadPropertyMultipleHandlerRegistered) {
|
||||
/* handle the data coming back from confirmed requests */
|
||||
apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE,
|
||||
My_Read_Property_Multiple_Ack_Handler);
|
||||
|
||||
/* handle any errors coming back */
|
||||
apdu_set_error_handler(
|
||||
SERVICE_CONFIRMED_READ_PROP_MULTIPLE, My_Error_Handler);
|
||||
|
||||
/* indicate that handlers are now registered */
|
||||
isReadPropertyMultipleHandlerRegistered = true;
|
||||
}
|
||||
/* Send the message out */
|
||||
if (!Error_Detected) {
|
||||
Request_Invoke_ID = Send_Read_Property_Multiple_Request(
|
||||
&buffer[0], sizeof(buffer), deviceInstanceNumber, Read_Access_Data);
|
||||
Wait_For_Answer_Or_Timeout(100, waitAnswer);
|
||||
}
|
||||
/* Clean up allocated memory */
|
||||
BACNET_READ_ACCESS_DATA *old_rpm_object;
|
||||
BACNET_PROPERTY_REFERENCE *old_rpm_property;
|
||||
|
||||
rpm_object = Read_Access_Data;
|
||||
old_rpm_object = rpm_object;
|
||||
while (rpm_object) {
|
||||
rpm_property = rpm_object->listOfProperties;
|
||||
while (rpm_property) {
|
||||
old_rpm_property = rpm_property;
|
||||
rpm_property = rpm_property->next;
|
||||
free(old_rpm_property);
|
||||
}
|
||||
old_rpm_object = rpm_object;
|
||||
rpm_object = rpm_object->next;
|
||||
free(old_rpm_object);
|
||||
}
|
||||
|
||||
/* Process the return value */
|
||||
int isFailure = Error_Detected;
|
||||
Error_Detected = 0;
|
||||
return isFailure;
|
||||
}
|
||||
|
||||
/****************************************************/
|
||||
/* This is the interface to WriteProperty */
|
||||
/****************************************************/
|
||||
int BacnetWriteProperty(int deviceInstanceNumber,
|
||||
int objectType,
|
||||
int objectInstanceNumber,
|
||||
int objectProperty,
|
||||
int objectPriority,
|
||||
int objectIndex,
|
||||
const char *tag,
|
||||
const char *value)
|
||||
{
|
||||
char msg[MAX_ERROR_STRING];
|
||||
int isFailure = 1;
|
||||
|
||||
if (!isWritePropertyHandlerRegistered) {
|
||||
/* handle the ack coming back */
|
||||
apdu_set_confirmed_simple_ack_handler(SERVICE_CONFIRMED_WRITE_PROPERTY,
|
||||
My_Write_Property_SimpleAck_Handler);
|
||||
|
||||
/* handle any errors coming back */
|
||||
apdu_set_error_handler(
|
||||
SERVICE_CONFIRMED_WRITE_PROPERTY, My_Error_Handler);
|
||||
|
||||
/* indicate that handlers are now registered */
|
||||
isWritePropertyHandlerRegistered = true;
|
||||
}
|
||||
|
||||
if (objectIndex == -1) {
|
||||
objectIndex = BACNET_ARRAY_ALL;
|
||||
}
|
||||
/* Loop for eary exit; */
|
||||
do {
|
||||
/* Handle the tag/value pair */
|
||||
uint8_t context_tag = 0;
|
||||
BACNET_APPLICATION_TAG property_tag;
|
||||
BACNET_APPLICATION_DATA_VALUE propertyValue;
|
||||
|
||||
if (toupper(tag[0]) == 'C') {
|
||||
context_tag = strtol(&tag[1], NULL, 0);
|
||||
propertyValue.context_tag = context_tag;
|
||||
propertyValue.context_specific = true;
|
||||
} else {
|
||||
propertyValue.context_specific = false;
|
||||
}
|
||||
property_tag = strtol(tag, NULL, 0);
|
||||
|
||||
if (property_tag >= MAX_BACNET_APPLICATION_TAG) {
|
||||
sprintf(msg, "Error: tag=%u - it must be less than %u",
|
||||
property_tag, MAX_BACNET_APPLICATION_TAG);
|
||||
LogError(msg);
|
||||
break;
|
||||
}
|
||||
if (!bacapp_parse_application_data(
|
||||
property_tag, value, &propertyValue)) {
|
||||
sprintf(msg, "Error: unable to parse the tag value");
|
||||
LogError(msg);
|
||||
break;
|
||||
}
|
||||
propertyValue.next = NULL;
|
||||
|
||||
/* Send out the message */
|
||||
Request_Invoke_ID = Send_Write_Property_Request(deviceInstanceNumber,
|
||||
objectType, objectInstanceNumber, objectProperty, &propertyValue,
|
||||
objectPriority, objectIndex);
|
||||
Wait_For_Answer_Or_Timeout(100, waitAnswer);
|
||||
|
||||
/* If we get here, then there were no explicit failures. However, there
|
||||
*/
|
||||
/* could have been implicit failures. Let's look at those also. */
|
||||
isFailure = Error_Detected;
|
||||
} while (false);
|
||||
|
||||
/* Clean up after ourselves. */
|
||||
Error_Detected = false;
|
||||
return isFailure;
|
||||
}
|
||||
|
||||
int BacnetAtomicWriteFile(int deviceInstanceNumber,
|
||||
int fileInstanceNumber,
|
||||
int blockStartAddr,
|
||||
int blockNumBytes,
|
||||
char *nibbleBuffer)
|
||||
{
|
||||
BACNET_OCTET_STRING fileData;
|
||||
int i, nibble;
|
||||
uint8_t byteValue;
|
||||
unsigned char nibbleValue;
|
||||
|
||||
if (!isAtomicWriteFileHandlerRegistered) {
|
||||
/* handle any errors coming back */
|
||||
apdu_set_error_handler(
|
||||
SERVICE_CONFIRMED_ATOMIC_WRITE_FILE, My_Error_Handler);
|
||||
|
||||
/* indicate that handlers are now registered */
|
||||
isAtomicWriteFileHandlerRegistered = true;
|
||||
}
|
||||
|
||||
for (i = 0; i < blockNumBytes; i++) {
|
||||
byteValue = 0;
|
||||
for (nibble = 0; nibble < 2; nibble++) {
|
||||
nibbleValue = toupper(nibbleBuffer[i * 2 + nibble]);
|
||||
if ((nibbleValue >= '0') && (nibbleValue <= '9')) {
|
||||
byteValue += (nibbleValue - '0') << (4 * (1 - nibble));
|
||||
} else if ((nibbleValue >= 'A') && (nibbleValue <= 'F')) {
|
||||
byteValue += (nibbleValue - 'A' + 10) << (4 * (1 - nibble));
|
||||
} else {
|
||||
LogError("Bad data in buffer.");
|
||||
}
|
||||
}
|
||||
fileData.value[i] = byteValue;
|
||||
}
|
||||
octetstring_truncate(&fileData, blockNumBytes);
|
||||
|
||||
/* Send out the message and wait for answer */
|
||||
if (!Error_Detected) {
|
||||
Request_Invoke_ID = Send_Atomic_Write_File_Stream(deviceInstanceNumber,
|
||||
fileInstanceNumber, blockStartAddr, &fileData);
|
||||
Wait_For_Answer_Or_Timeout(100, waitAnswer);
|
||||
}
|
||||
|
||||
int isFailure = Error_Detected;
|
||||
Error_Detected = 0;
|
||||
return isFailure;
|
||||
}
|
||||
|
||||
int BacnetGetMaxApdu()
|
||||
{
|
||||
unsigned requestedOctetCount = 0;
|
||||
uint16_t my_max_apdu = 0;
|
||||
|
||||
/* calculate the smaller of our APDU size or theirs
|
||||
and remove the overhead of the APDU (varies depending on size).
|
||||
note: we could fail if there is a bottle neck (router)
|
||||
and smaller MPDU in betweeen. */
|
||||
if (Target_Max_APDU < MAX_APDU) {
|
||||
my_max_apdu = Target_Max_APDU;
|
||||
} else {
|
||||
my_max_apdu = MAX_APDU;
|
||||
}
|
||||
/* Typical sizes are 50, 128, 206, 480, 1024, and 1476 octets */
|
||||
if (my_max_apdu <= 50) {
|
||||
requestedOctetCount = my_max_apdu - 19;
|
||||
} else if (my_max_apdu <= 480) {
|
||||
requestedOctetCount = my_max_apdu - 32;
|
||||
} else if (my_max_apdu <= 1476) {
|
||||
requestedOctetCount = my_max_apdu - 64;
|
||||
} else {
|
||||
requestedOctetCount = my_max_apdu / 2;
|
||||
}
|
||||
|
||||
return requestedOctetCount;
|
||||
}
|
||||
|
||||
int BacnetTimeSync(int deviceInstanceNumber,
|
||||
int year,
|
||||
int month,
|
||||
int day,
|
||||
int hour,
|
||||
int minute,
|
||||
int second,
|
||||
int isUTC,
|
||||
int UTCOffset)
|
||||
{
|
||||
BACNET_DATE bdate;
|
||||
BACNET_TIME btime;
|
||||
struct tm my_time;
|
||||
time_t aTime;
|
||||
struct tm *newTime;
|
||||
|
||||
my_time.tm_sec = second;
|
||||
my_time.tm_min = minute;
|
||||
my_time.tm_hour = hour;
|
||||
my_time.tm_mday = day;
|
||||
my_time.tm_mon = month - 1;
|
||||
my_time.tm_year = year - 1900;
|
||||
my_time.tm_wday = 0; /* does not matter */
|
||||
my_time.tm_yday = 0; /* does not matter */
|
||||
my_time.tm_isdst = 0; /* does not matter */
|
||||
|
||||
aTime = mktime(&my_time);
|
||||
newTime = localtime(&aTime);
|
||||
|
||||
bdate.year = newTime->tm_year;
|
||||
bdate.month = newTime->tm_mon + 1;
|
||||
bdate.day = newTime->tm_mday;
|
||||
bdate.wday = newTime->tm_wday ? newTime->tm_wday : 7;
|
||||
btime.hour = newTime->tm_hour;
|
||||
btime.min = newTime->tm_min;
|
||||
btime.sec = newTime->tm_sec;
|
||||
btime.hundredths = 0;
|
||||
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
uint8_t Handler_Transmit_Buffer[MAX_PDU] = { 0 };
|
||||
|
||||
/* Loop for eary exit */
|
||||
do {
|
||||
if (!dcc_communication_enabled()) {
|
||||
LogError("DCC communicaiton is not enabled");
|
||||
break;
|
||||
}
|
||||
|
||||
/* encode the NPDU portion of the packet */
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
datalink_get_my_address(&my_address);
|
||||
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], &Target_Address,
|
||||
&my_address, &npdu_data);
|
||||
|
||||
/* encode the APDU portion of the packet */
|
||||
len = timesync_encode_apdu(
|
||||
&Handler_Transmit_Buffer[pdu_len], &bdate, &btime);
|
||||
pdu_len += len;
|
||||
|
||||
/* send it out the datalink */
|
||||
bytes_sent = datalink_send_pdu(
|
||||
&Target_Address, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
|
||||
if (bytes_sent <= 0) {
|
||||
char errorMsg[64];
|
||||
sprintf(errorMsg,
|
||||
"Failed to Send Time-Synchronization Request (%s)!",
|
||||
strerror(errno));
|
||||
LogError(errorMsg);
|
||||
break;
|
||||
}
|
||||
|
||||
Wait_For_Answer_Or_Timeout(100, waitAnswer);
|
||||
} while (false);
|
||||
|
||||
int isFailure = Error_Detected;
|
||||
Error_Detected = 0;
|
||||
return isFailure;
|
||||
}
|
||||
|
||||
/****************************************************/
|
||||
/* This is the interface to AtomicReadFile */
|
||||
/****************************************************/
|
||||
int BacnetAtomicReadFile(int deviceInstanceNumber,
|
||||
int fileInstanceNumber,
|
||||
int startOffset,
|
||||
int numBytes)
|
||||
{
|
||||
if (!isAtomicReadFileHandlerRegistered) {
|
||||
/* handle the data coming back from confirmed requests */
|
||||
apdu_set_confirmed_ack_handler(
|
||||
SERVICE_CONFIRMED_ATOMIC_READ_FILE, AtomicReadFileAckHandler);
|
||||
|
||||
/* handle any errors coming back */
|
||||
apdu_set_error_handler(
|
||||
SERVICE_CONFIRMED_ATOMIC_READ_FILE, My_Error_Handler);
|
||||
|
||||
/* indicate that handlers are now registered */
|
||||
isAtomicReadFileHandlerRegistered = true;
|
||||
}
|
||||
/* Send the message out */
|
||||
Request_Invoke_ID = Send_Atomic_Read_File_Stream(
|
||||
deviceInstanceNumber, fileInstanceNumber, startOffset, numBytes);
|
||||
Wait_For_Answer_Or_Timeout(100, waitAnswer);
|
||||
|
||||
int isFailure = Error_Detected;
|
||||
Error_Detected = 0;
|
||||
return isFailure;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
The BACnet Scriptable (using Perl) Tool.
|
||||
|
||||
* Running this tool assumes that the library has been already built. The
|
||||
library should be built with a command similar to
|
||||
|
||||
CC=/mingw/bin/gcc BACNET_DEFINES="-DPRINT_ENABLED -DBACAPP_ALL -DBACFILE
|
||||
-DINTRINSIC_REPORTING" BBMD_DEFINE=-DBBMD_ENABLED\=1 BACNET_PORT=win32 make
|
||||
clean library
|
||||
|
||||
* Currently, the tool assumes only win32 port, but should be easily modifiable
|
||||
for any port build.
|
||||
* This tool has to be run from a path without any spaces. The presence of the
|
||||
.Inline directory is required.
|
||||
* Run the tool without any arguments to see usage instructions
|
||||
* To run the example ReapProperty script (which reads Analog Value 0 Present
|
||||
Value) for Device at instance 1234 run the following command
|
||||
|
||||
perl bacnet.pl --script example_readprop.pl -- 1234
|
||||
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
###############################################################################
|
||||
# Makefile for Project - PiFace
|
||||
###############################################################################
|
||||
|
||||
## General Flags
|
||||
TARGET = bacpiface
|
||||
|
||||
# Source locations
|
||||
BACNET_CORE = ../../src
|
||||
BACNET_INCLUDE = ../../include
|
||||
BACNET_HANDLER = ../../demo/handler
|
||||
BACNET_OBJECT = ../../demo/object
|
||||
BACNET_DEMO = ../../demo
|
||||
BACNET_PORT_DIR = ../../ports/linux
|
||||
PIFACE_INCLUDE = libpifacedigital/src
|
||||
PIFACE_LIB = libpifacedigital
|
||||
MCP23S17_LIB = libmcp23s17
|
||||
|
||||
## BACnet options
|
||||
BACDL_DEFINE ?= -DBACDL_BIP=1
|
||||
# Declare your level of BBMD support
|
||||
BBMD_DEFINE ?= -DBBMD_ENABLED=1
|
||||
|
||||
# local files for this project
|
||||
CSRC = main.c \
|
||||
device.c
|
||||
|
||||
# common demo files needed
|
||||
DEMOSRC = \
|
||||
$(BACNET_HANDLER)/dlenv.c \
|
||||
$(BACNET_HANDLER)/txbuf.c \
|
||||
$(BACNET_HANDLER)/noserv.c \
|
||||
$(BACNET_HANDLER)/h_npdu.c \
|
||||
$(BACNET_HANDLER)/h_whois.c \
|
||||
$(BACNET_HANDLER)/h_iam.c \
|
||||
$(BACNET_HANDLER)/h_rp.c \
|
||||
$(BACNET_HANDLER)/h_rp_a.c \
|
||||
$(BACNET_HANDLER)/h_rpm.c \
|
||||
$(BACNET_HANDLER)/h_rpm_a.c \
|
||||
$(BACNET_HANDLER)/h_rr.c \
|
||||
$(BACNET_HANDLER)/h_wp.c \
|
||||
$(BACNET_HANDLER)/h_wpm.c \
|
||||
$(BACNET_HANDLER)/h_alarm_ack.c \
|
||||
$(BACNET_HANDLER)/h_arf.c \
|
||||
$(BACNET_HANDLER)/h_arf_a.c \
|
||||
$(BACNET_HANDLER)/h_awf.c \
|
||||
$(BACNET_HANDLER)/h_rd.c \
|
||||
$(BACNET_HANDLER)/h_dcc.c \
|
||||
$(BACNET_HANDLER)/h_ts.c \
|
||||
$(BACNET_HANDLER)/h_whohas.c \
|
||||
$(BACNET_HANDLER)/h_ihave.c \
|
||||
$(BACNET_HANDLER)/h_cov.c \
|
||||
$(BACNET_HANDLER)/h_ccov.c \
|
||||
$(BACNET_HANDLER)/h_ucov.c \
|
||||
$(BACNET_HANDLER)/h_getevent.c \
|
||||
$(BACNET_HANDLER)/h_gas_a.c \
|
||||
$(BACNET_HANDLER)/h_get_alarm_sum.c \
|
||||
$(BACNET_HANDLER)/h_getevent_a.c \
|
||||
$(BACNET_HANDLER)/h_pt.c \
|
||||
$(BACNET_HANDLER)/h_pt_a.c \
|
||||
$(BACNET_HANDLER)/h_upt.c \
|
||||
$(BACNET_HANDLER)/s_arfs.c \
|
||||
$(BACNET_HANDLER)/s_awfs.c \
|
||||
$(BACNET_HANDLER)/s_dcc.c \
|
||||
$(BACNET_HANDLER)/s_ihave.c \
|
||||
$(BACNET_HANDLER)/s_get_alarm_sum.c \
|
||||
$(BACNET_HANDLER)/s_get_event.c \
|
||||
$(BACNET_HANDLER)/s_iam.c \
|
||||
$(BACNET_HANDLER)/s_cov.c \
|
||||
$(BACNET_HANDLER)/s_ptransfer.c \
|
||||
$(BACNET_HANDLER)/s_rd.c \
|
||||
$(BACNET_HANDLER)/s_rp.c \
|
||||
$(BACNET_HANDLER)/s_rpm.c \
|
||||
$(BACNET_HANDLER)/s_ts.c \
|
||||
$(BACNET_HANDLER)/s_cevent.c \
|
||||
$(BACNET_HANDLER)/s_router.c \
|
||||
$(BACNET_HANDLER)/s_uevent.c \
|
||||
$(BACNET_HANDLER)/s_whohas.c \
|
||||
$(BACNET_HANDLER)/s_whois.c \
|
||||
$(BACNET_HANDLER)/s_wpm.c \
|
||||
$(BACNET_HANDLER)/s_upt.c \
|
||||
$(BACNET_HANDLER)/s_wp.c
|
||||
|
||||
OBJSRC = \
|
||||
$(BACNET_OBJECT)/bi.c \
|
||||
$(BACNET_OBJECT)/bo.c \
|
||||
$(BACNET_OBJECT)/netport.c
|
||||
|
||||
# core BACnet stack files
|
||||
CORESRC = \
|
||||
$(BACNET_CORE)/apdu.c \
|
||||
$(BACNET_CORE)/npdu.c \
|
||||
$(BACNET_CORE)/bacdcode.c \
|
||||
$(BACNET_CORE)/bacint.c \
|
||||
$(BACNET_CORE)/bacreal.c \
|
||||
$(BACNET_CORE)/bacstr.c \
|
||||
$(BACNET_CORE)/bacapp.c \
|
||||
$(BACNET_CORE)/bacprop.c \
|
||||
$(BACNET_CORE)/bactext.c \
|
||||
$(BACNET_CORE)/datetime.c \
|
||||
$(BACNET_CORE)/indtext.c \
|
||||
$(BACNET_CORE)/key.c \
|
||||
$(BACNET_CORE)/keylist.c \
|
||||
$(BACNET_CORE)/proplist.c \
|
||||
$(BACNET_CORE)/debug.c \
|
||||
$(BACNET_CORE)/bigend.c \
|
||||
$(BACNET_CORE)/arf.c \
|
||||
$(BACNET_CORE)/awf.c \
|
||||
$(BACNET_CORE)/cov.c \
|
||||
$(BACNET_CORE)/dcc.c \
|
||||
$(BACNET_CORE)/iam.c \
|
||||
$(BACNET_CORE)/ihave.c \
|
||||
$(BACNET_CORE)/rd.c \
|
||||
$(BACNET_CORE)/rp.c \
|
||||
$(BACNET_CORE)/rpm.c \
|
||||
$(BACNET_CORE)/timesync.c \
|
||||
$(BACNET_CORE)/whohas.c \
|
||||
$(BACNET_CORE)/whois.c \
|
||||
$(BACNET_CORE)/wp.c \
|
||||
$(BACNET_CORE)/wpm.c \
|
||||
$(BACNET_CORE)/abort.c \
|
||||
$(BACNET_CORE)/reject.c \
|
||||
$(BACNET_CORE)/bacerror.c \
|
||||
$(BACNET_CORE)/ptransfer.c \
|
||||
$(BACNET_CORE)/memcopy.c \
|
||||
$(BACNET_CORE)/filename.c \
|
||||
$(BACNET_CORE)/tsm.c \
|
||||
$(BACNET_CORE)/bacaddr.c \
|
||||
$(BACNET_CORE)/address.c \
|
||||
$(BACNET_CORE)/bacdevobjpropref.c \
|
||||
$(BACNET_CORE)/bacpropstates.c \
|
||||
$(BACNET_CORE)/alarm_ack.c \
|
||||
$(BACNET_CORE)/event.c \
|
||||
$(BACNET_CORE)/getevent.c \
|
||||
$(BACNET_CORE)/get_alarm_sum.c \
|
||||
$(BACNET_CORE)/readrange.c \
|
||||
$(BACNET_CORE)/timestamp.c \
|
||||
$(BACNET_CORE)/lighting.c \
|
||||
$(BACNET_CORE)/version.c
|
||||
|
||||
PORT_BIP_SRC = \
|
||||
$(BACNET_PORT_DIR)/bip-init.c \
|
||||
$(BACNET_CORE)/bvlc.c \
|
||||
$(BACNET_CORE)/bip.c
|
||||
|
||||
ifeq (${BACDL_DEFINE},-DBACDL_BIP=1)
|
||||
PORT_SRC = ${PORT_BIP_SRC}
|
||||
endif
|
||||
|
||||
## Include Directories
|
||||
INCLUDES = -I. -I$(BACNET_INCLUDE) -I$(BACNET_HANDLER) -I$(BACNET_OBJECT)
|
||||
INCLUDES += -I$(BACNET_PORT_DIR)
|
||||
INCLUDES += -I$(PIFACE_INCLUDE)
|
||||
|
||||
# Source to Object conversion
|
||||
COBJ = $(CSRC:%.c=%.o)
|
||||
DEMOOBJ = $(DEMOSRC:.c=.o)
|
||||
COREOBJ = $(CORESRC:.c=.o)
|
||||
OBJOBJ = $(OBJSRC:.c=.o)
|
||||
PORTOBJ = $(PORT_SRC:.c=.o)
|
||||
|
||||
# define something from the Makefile or batch file
|
||||
DEFINES = $(BACDL_DEFINE) $(BBMD_DEFINE)
|
||||
OPTIMIZATION = -Os $(OPTIMIZE_FLAGS)
|
||||
DEBUGGING = -g
|
||||
ifeq (${BUILD},debug)
|
||||
OPTIMIZATION = -O0
|
||||
DEBUGGING = -g
|
||||
endif
|
||||
ifeq (${BUILD},release)
|
||||
OPTIMIZATION = -Os $(OPTIMIZE_FLAGS)
|
||||
DEBUGGING = -DNDEBUG
|
||||
endif
|
||||
|
||||
BFLAGS += -DBACNET_PROTOCOL_REVISION=17
|
||||
BFLAGS += -DMAX_TSM_TRANSACTIONS=1
|
||||
BFLAGS += -DMAX_BINARY_INPUTS=4
|
||||
BFLAGS += -DMAX_BINARY_OUTPUTS=8
|
||||
|
||||
## Compile options for C files
|
||||
CFLAGS = $(DEFINES)
|
||||
CFLAGS += $(DEBUGGING)
|
||||
CFLAGS += $(INCLUDES)
|
||||
# warnings
|
||||
CFLAGS += -Wall
|
||||
CFLAGS += -Wstrict-prototypes
|
||||
CFLAGS += -Wmissing-prototypes
|
||||
# put it all together
|
||||
CFLAGS += $(BFLAGS) $(OPTIMIZATION)
|
||||
CFLAGS += -MD -MP -MT $(*F).o -MF dep/$(@F).d
|
||||
|
||||
## Linker flags
|
||||
LDPIFACE = -Wl,-L$(PIFACE_LIB),-lpifacedigital,-L$(MCP23S17_LIB),-lmcp23s17
|
||||
LDFLAGS = -Wl,-lc,-lgcc,-lrt,-lm
|
||||
LFLAGS := $(LDPIFACE) $(LDFLAGS)
|
||||
|
||||
## Objects not in library that must be built in order to link
|
||||
OBJECTS = $(COBJ) $(DEMOOBJ) $(COREOBJ) $(OBJOBJ) $(PORTOBJ)
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJECTS) $(LIBRARY)
|
||||
$(CC) $(OBJECTS) $(LFLAGS) -o $@
|
||||
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
clean:
|
||||
rm -f core ${TARGET} ${OBJECTS}
|
||||
|
||||
include: .depend
|
||||
|
||||
|
||||
.PHONY: clean all include depend
|
||||
|
||||
## Other dependencies
|
||||
-include $(shell mkdir dep 2>/dev/null) $(wildcard dep/*)
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
# A script to download and build the libpifacedigital for PiFace
|
||||
# The library is located at github.com/piface
|
||||
# Since the PiFace library is GPLv3, we have to keep it separate.
|
||||
|
||||
git clone https://github.com/piface/libmcp23s17.git
|
||||
git clone https://github.com/piface/libpifacedigital.git
|
||||
|
||||
# Build the library
|
||||
|
||||
make -C libmcp23s17
|
||||
make -C libpifacedigital
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,281 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/datalink/dlenv.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/object/bacfile.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/dcc.h"
|
||||
#include "bacnet/getevent.h"
|
||||
#include "bacport.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/version.h"
|
||||
/* include the device object */
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/object/bi.h"
|
||||
#include "bacnet/basic/object/bo.h"
|
||||
#include "pifacedigital.h"
|
||||
|
||||
/** @file server/main.c Example server application using the BACnet Stack. */
|
||||
|
||||
/* (Doxygen note: The next two lines pull all the following Javadoc
|
||||
* into the ServerDemo module.) */
|
||||
/** @addtogroup ServerDemo */
|
||||
/*@{*/
|
||||
|
||||
/** Buffer used for receiving */
|
||||
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
|
||||
|
||||
/* current version of the BACnet stack */
|
||||
static const char *BACnet_Version = BACNET_VERSION_TEXT;
|
||||
|
||||
/** Initialize the handlers we will utilize.
|
||||
* @see Device_Init, apdu_set_unconfirmed_handler, apdu_set_confirmed_handler
|
||||
*/
|
||||
static void Init_Service_Handlers(void)
|
||||
{
|
||||
Device_Init(NULL);
|
||||
/* we need to handle who-is to support dynamic device binding */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has);
|
||||
/* 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);
|
||||
/* Set the handlers for any confirmed services that we support. */
|
||||
/* We must implement read property - it's required! */
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_READ_PROPERTY, handler_read_property);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_READ_PROP_MULTIPLE, handler_read_property_multiple);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_WRITE_PROPERTY, handler_write_property);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE, handler_write_property_multiple);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_READ_RANGE, handler_read_range);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_REINITIALIZE_DEVICE, handler_reinitialize_device);
|
||||
apdu_set_unconfirmed_handler(
|
||||
SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, handler_timesync_utc);
|
||||
apdu_set_unconfirmed_handler(
|
||||
SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, handler_timesync);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_SUBSCRIBE_COV, handler_cov_subscribe);
|
||||
apdu_set_unconfirmed_handler(
|
||||
SERVICE_UNCONFIRMED_COV_NOTIFICATION, handler_ucov_notification);
|
||||
/* handle communication so we can shutup when asked */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
|
||||
handler_device_communication_control);
|
||||
/* handle the data coming back from private requests */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_PRIVATE_TRANSFER,
|
||||
handler_unconfirmed_private_transfer);
|
||||
}
|
||||
|
||||
static void piface_init(void)
|
||||
{
|
||||
int hw_addr = 0; /**< PiFaceDigital hardware address */
|
||||
#ifdef PIFACE_INTERRUPT_ENABLE
|
||||
int intenable = 1; /**< Whether or not interrupts are enabled */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Open piface digital SPI connection(s)
|
||||
*/
|
||||
printf("Opening piface digital connection at location %d\n", hw_addr);
|
||||
pifacedigital_open(hw_addr);
|
||||
|
||||
#ifdef PIFACE_INTERRUPT_ENABLE
|
||||
/**
|
||||
* Enable interrupt processing (only required for all
|
||||
* blocking/interrupt methods)
|
||||
*/
|
||||
intenable = pifacedigital_enable_interrupts();
|
||||
if (intenable == 0) {
|
||||
printf("Interrupts enabled.\n");
|
||||
} else {
|
||||
printf("Could not enable interrupts. "
|
||||
"Try running using sudo to enable PiFaceDigital interrupts.\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* track the Piface pin state to react on changes only */
|
||||
static bool PiFace_Pin_Status[MAX_BINARY_INPUTS];
|
||||
|
||||
/**
|
||||
* Clean up the PiFace interface
|
||||
*/
|
||||
static void piface_cleanup(void)
|
||||
{
|
||||
pifacedigital_close(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a periodic task for the PiFace card
|
||||
*/
|
||||
static void piface_task(void)
|
||||
{
|
||||
unsigned i = 0;
|
||||
BACNET_BINARY_PV present_value = BINARY_INACTIVE;
|
||||
bool pin_status = false;
|
||||
|
||||
for (i = 0; i < MAX_BINARY_INPUTS; i++) {
|
||||
if (!Binary_Input_Out_Of_Service(i)) {
|
||||
present_value = Binary_Input_Present_Value(i);
|
||||
pin_status = false;
|
||||
if (pifacedigital_digital_read(i)) {
|
||||
pin_status = true;
|
||||
}
|
||||
if (pin_status != PiFace_Pin_Status[i]) {
|
||||
PiFace_Pin_Status[i] = pin_status;
|
||||
if (pin_status) {
|
||||
/* toggle the input only when button is pressed */
|
||||
if (present_value == BINARY_INACTIVE) {
|
||||
present_value = BINARY_ACTIVE;
|
||||
} else {
|
||||
present_value = BINARY_INACTIVE;
|
||||
}
|
||||
Binary_Input_Present_Value_Set(i, present_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < MAX_BINARY_OUTPUTS; i++) {
|
||||
if (!Binary_Output_Out_Of_Service(i)) {
|
||||
present_value = Binary_Output_Present_Value(i);
|
||||
if (present_value == BINARY_INACTIVE) {
|
||||
pifacedigital_digital_write(i, 0);
|
||||
} else {
|
||||
pifacedigital_digital_write(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Main function of server demo.
|
||||
*
|
||||
* @see Device_Set_Object_Instance_Number, dlenv_init, Send_I_Am,
|
||||
* datalink_receive, npdu_handler,
|
||||
* dcc_timer_seconds, bvlc_maintenance_timer,
|
||||
* handler_cov_task,
|
||||
* tsm_timer_milliseconds
|
||||
*
|
||||
* @param argc [in] Arg count.
|
||||
* @param argv [in] Takes one argument: the Device Instance #.
|
||||
* @return 0 on success.
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
BACNET_ADDRESS src = { 0 }; /* address where message came from */
|
||||
uint16_t pdu_len = 0;
|
||||
unsigned timeout = 1; /* milliseconds */
|
||||
time_t last_seconds = 0;
|
||||
time_t current_seconds = 0;
|
||||
uint32_t elapsed_seconds = 0;
|
||||
uint32_t elapsed_milliseconds = 0;
|
||||
uint32_t address_binding_tmr = 0;
|
||||
|
||||
/* allow the device ID to be set */
|
||||
if (argc > 1) {
|
||||
Device_Set_Object_Instance_Number(strtol(argv[1], NULL, 0));
|
||||
}
|
||||
printf("BACnet Raspberry Pi PiFace Digital Demo\n"
|
||||
"BACnet Stack Version %s\n"
|
||||
"BACnet Device ID: %u\n"
|
||||
"Max APDU: %d\n",
|
||||
BACnet_Version, Device_Object_Instance_Number(), MAX_APDU);
|
||||
/* load any static address bindings to show up
|
||||
in our device bindings list */
|
||||
address_init();
|
||||
Init_Service_Handlers();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
piface_init();
|
||||
atexit(piface_cleanup);
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
/* broadcast an I-Am on startup */
|
||||
Send_I_Am(&Handler_Transmit_Buffer[0]);
|
||||
/* loop forever */
|
||||
for (;;) {
|
||||
/* input */
|
||||
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 */
|
||||
elapsed_seconds = (uint32_t)(current_seconds - last_seconds);
|
||||
if (elapsed_seconds) {
|
||||
last_seconds = current_seconds;
|
||||
dcc_timer_seconds(elapsed_seconds);
|
||||
#if defined(BACDL_BIP) && BBMD_ENABLED
|
||||
bvlc_maintenance_timer(elapsed_seconds);
|
||||
#endif
|
||||
dlenv_maintenance_timer(elapsed_seconds);
|
||||
elapsed_milliseconds = elapsed_seconds * 1000;
|
||||
handler_cov_timer_seconds(elapsed_seconds);
|
||||
tsm_timer_milliseconds(elapsed_milliseconds);
|
||||
}
|
||||
handler_cov_task();
|
||||
/* scan cache address */
|
||||
address_binding_tmr += elapsed_seconds;
|
||||
if (address_binding_tmr >= 60) {
|
||||
address_cache_timer(address_binding_tmr);
|
||||
address_binding_tmr = 0;
|
||||
}
|
||||
/* output/input */
|
||||
piface_task();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* @} */
|
||||
|
||||
/* End group ServerDemo */
|
||||
@@ -0,0 +1,10 @@
|
||||
This demo is designed for a Raspberry Pi connected to a PiFace card.
|
||||
|
||||
The demo uses libpifacedigital from github.com/piface repository.
|
||||
|
||||
To build, start with the configure script:
|
||||
|
||||
$ ./configure.sh
|
||||
$ make clean all
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#Makefile to build BACnet Application using GCC compiler
|
||||
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
# AR = ar
|
||||
# MAKE = make
|
||||
# SIZE = size
|
||||
#
|
||||
# Assumes rm and cp are available
|
||||
|
||||
# Executable file name
|
||||
TARGET = ptransfer
|
||||
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = main.c \
|
||||
../object/netport.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 -rf core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET}
|
||||
|
||||
include: .depend
|
||||
@@ -0,0 +1,283 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/abort.h"
|
||||
#include "bacnet/ptransfer.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
|
||||
/** @file h_pt.c Handles Confirmed Private Transfer requests. */
|
||||
|
||||
#define MYMAXSTR 32
|
||||
#define MYMAXBLOCK 8
|
||||
|
||||
DATABLOCK MyData[MYMAXBLOCK];
|
||||
|
||||
uint8_t IOBufferPT[MAX_APDU]; /* Buffer for building response in */
|
||||
|
||||
static void ProcessPT(BACNET_PRIVATE_TRANSFER_DATA *data)
|
||||
{
|
||||
int iLen; /* Index to current location in data */
|
||||
char cBlockNumber;
|
||||
uint32_t ulTemp;
|
||||
int tag_len;
|
||||
uint8_t tag_number;
|
||||
uint32_t len_value_type;
|
||||
BACNET_CHARACTER_STRING bsTemp;
|
||||
|
||||
iLen = 0;
|
||||
|
||||
/* Decode the block number */
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&data->serviceParameters[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
/* Bail out early if wrong type */
|
||||
/* and signal unexpected error */
|
||||
data->serviceParametersLen = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
iLen += decode_unsigned(
|
||||
&data->serviceParameters[iLen], len_value_type, &ulTemp);
|
||||
cBlockNumber = (char)ulTemp;
|
||||
if (cBlockNumber < MY_MAX_BLOCK) {
|
||||
if (data->serviceNumber == MY_SVC_READ) {
|
||||
/* Read Response is an unsigned int with
|
||||
0 for success or a non 0 error code
|
||||
For a successful read the 0 success
|
||||
code is followed by the block number
|
||||
and then the block contents which
|
||||
consist of 2 unsigned ints (in 0 to 255
|
||||
range as they are really chars) a single
|
||||
precision real and a string which
|
||||
will be up to 32 chars + a nul */
|
||||
|
||||
iLen = 0;
|
||||
|
||||
/* Signal success */
|
||||
iLen += encode_application_unsigned(&IOBufferPT[iLen], MY_ERR_OK);
|
||||
/* Followed by the block number */
|
||||
iLen +=
|
||||
encode_application_unsigned(&IOBufferPT[iLen], cBlockNumber);
|
||||
/* And Then the block contents */
|
||||
iLen += encode_application_unsigned(
|
||||
&IOBufferPT[iLen], MyData[(int8_t)cBlockNumber].cMyByte1);
|
||||
iLen += encode_application_unsigned(
|
||||
&IOBufferPT[iLen], MyData[(int8_t)cBlockNumber].cMyByte2);
|
||||
iLen += encode_application_real(
|
||||
&IOBufferPT[iLen], MyData[(int8_t)cBlockNumber].fMyReal);
|
||||
characterstring_init_ansi(
|
||||
&bsTemp, (char *)MyData[(int8_t)cBlockNumber].sMyString);
|
||||
iLen +=
|
||||
encode_application_character_string(&IOBufferPT[iLen], &bsTemp);
|
||||
} else {
|
||||
/* Write operation */
|
||||
/* Write block consists of the block number
|
||||
followed by the block contents as
|
||||
described above for the read operation.
|
||||
The returned result is an unsigned
|
||||
response which is 0 for success and
|
||||
a non 0 error code otherwise. */
|
||||
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&data->serviceParameters[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
data->serviceParametersLen = 0;
|
||||
return;
|
||||
}
|
||||
iLen += decode_unsigned(
|
||||
&data->serviceParameters[iLen], len_value_type, &ulTemp);
|
||||
MyData[(int8_t)cBlockNumber].cMyByte1 = (char)ulTemp;
|
||||
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&data->serviceParameters[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
data->serviceParametersLen = 0;
|
||||
return;
|
||||
}
|
||||
iLen += decode_unsigned(
|
||||
&data->serviceParameters[iLen], len_value_type, &ulTemp);
|
||||
MyData[(int8_t)cBlockNumber].cMyByte2 = (char)ulTemp;
|
||||
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&data->serviceParameters[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_REAL) {
|
||||
data->serviceParametersLen = 0;
|
||||
return;
|
||||
}
|
||||
iLen += decode_real(&data->serviceParameters[iLen],
|
||||
&MyData[(int8_t)cBlockNumber].fMyReal);
|
||||
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&data->serviceParameters[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_CHARACTER_STRING) {
|
||||
data->serviceParametersLen = 0;
|
||||
return;
|
||||
}
|
||||
decode_character_string(
|
||||
&data->serviceParameters[iLen], len_value_type, &bsTemp);
|
||||
/* Only copy as much as we can accept */
|
||||
strncpy((char *)MyData[(int8_t)cBlockNumber].sMyString,
|
||||
characterstring_value(&bsTemp), MY_MAX_STR);
|
||||
/* Make sure it is nul terminated */
|
||||
MyData[(int8_t)cBlockNumber].sMyString[MY_MAX_STR] = '\0';
|
||||
/* Signal success */
|
||||
iLen = encode_application_unsigned(&IOBufferPT[0], MY_ERR_OK);
|
||||
}
|
||||
} else {
|
||||
/* Signal bad index */
|
||||
iLen = encode_application_unsigned(&IOBufferPT[0], MY_ERR_BAD_INDEX);
|
||||
}
|
||||
data->serviceParametersLen = iLen;
|
||||
data->serviceParameters = IOBufferPT;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called when we receive a private transfer packet.
|
||||
* We parse the data, send the private part for processing and then send the
|
||||
* response which the application generates.
|
||||
* If there are any BACnet level errors we send an error response from here.
|
||||
* If there are any application level errors they will be packeged up in the
|
||||
* response block which we send back to the originator of the request.
|
||||
*
|
||||
*/
|
||||
|
||||
void handler_conf_private_trans(uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS *src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA *service_data)
|
||||
{
|
||||
BACNET_PRIVATE_TRANSFER_DATA data;
|
||||
int len;
|
||||
int pdu_len;
|
||||
bool error;
|
||||
int bytes_sent;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
BACNET_ERROR_CLASS error_class;
|
||||
BACNET_ERROR_CODE error_code;
|
||||
|
||||
len = 0;
|
||||
pdu_len = 0;
|
||||
error = false;
|
||||
bytes_sent = 0;
|
||||
error_class = ERROR_CLASS_OBJECT;
|
||||
error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Received Confirmed Private Transfer Request!\n");
|
||||
#endif
|
||||
/* encode the NPDU portion of the response packet as it will be needed */
|
||||
/* no matter what the outcome. */
|
||||
|
||||
datalink_get_my_address(&my_address);
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len = npdu_encode_pdu(
|
||||
&Handler_Transmit_Buffer[0], src, &my_address, &npdu_data);
|
||||
|
||||
if (service_data->segmented_message) {
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED,
|
||||
true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "CPT: Segmented Message. Sending Abort!\n");
|
||||
#endif
|
||||
goto CPT_ABORT;
|
||||
}
|
||||
|
||||
len = ptransfer_decode_service_request(service_request, service_len, &data);
|
||||
/* bad decoding - send an abort */
|
||||
if (len < 0) {
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_OTHER, true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "CPT: Bad Encoding. Sending Abort!\n");
|
||||
#endif
|
||||
goto CPT_ABORT;
|
||||
}
|
||||
|
||||
/* Simple example with service number of 0 for
|
||||
read block and 1 for write block
|
||||
We also only support our own vendor ID.
|
||||
In theory we could support others
|
||||
for compatability purposes but these
|
||||
interfaces are rarely documented... */
|
||||
if ((data.vendorID == BACNET_VENDOR_ID) &&
|
||||
(data.serviceNumber <= MY_SVC_WRITE)) {
|
||||
/* We only try to understand our own IDs and service numbers */
|
||||
/* Will either return a result block or an app level status block */
|
||||
ProcessPT(&data);
|
||||
if (data.serviceParametersLen == 0) {
|
||||
/* No respopnse means fatal error */
|
||||
error = true;
|
||||
error_class = ERROR_CLASS_SERVICES;
|
||||
error_code = ERROR_CODE_OTHER;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "CPT: Error servicing request!\n");
|
||||
#endif
|
||||
}
|
||||
len = ptransfer_ack_encode_apdu(
|
||||
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, &data);
|
||||
} else { /* Not our vendor ID or bad service parameter */
|
||||
|
||||
error = true;
|
||||
error_class = ERROR_CLASS_SERVICES;
|
||||
error_code = ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "CPT: Not our Vendor ID or invalid service code!\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (error) {
|
||||
len = ptransfer_error_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, error_class, error_code, &data);
|
||||
}
|
||||
CPT_ABORT:
|
||||
pdu_len += len;
|
||||
bytes_sent = datalink_send_pdu(
|
||||
src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
|
||||
|
||||
#if PRINT_ENABLED
|
||||
if (bytes_sent <= 0) {
|
||||
fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno));
|
||||
}
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg
|
||||
* @date October 2019
|
||||
* @brief Header file for a basic ConfirmedPrivateTransfer service handler
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef HANDLER_CONFIRMED_PRIVATE_TRANSFER_H
|
||||
#define HANDLER_CONFIRMED_PRIVATE_TRANSFER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/apdu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void handler_conf_private_trans(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,221 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/abort.h"
|
||||
#include "bacnet/ptransfer.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
|
||||
/** @file h_pt_a.c Handles Confirmed Private Transfer Acknowledgment. */
|
||||
|
||||
extern uint8_t IOBufferPT[300]; /* Somewhere to build the encoded result block
|
||||
for Private Transfers */
|
||||
|
||||
static void DecodeBlock(char cBlockNum, uint8_t *pData)
|
||||
{
|
||||
int iLen;
|
||||
uint32_t ulTemp;
|
||||
int tag_len;
|
||||
uint8_t tag_number;
|
||||
uint32_t len_value_type;
|
||||
BACNET_CHARACTER_STRING bsName;
|
||||
DATABLOCK Response;
|
||||
|
||||
iLen = 0;
|
||||
|
||||
if (cBlockNum >= MY_MAX_BLOCK)
|
||||
return;
|
||||
|
||||
tag_len =
|
||||
decode_tag_number_and_value(&pData[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT)
|
||||
return;
|
||||
|
||||
iLen += decode_unsigned(&pData[iLen], len_value_type, &ulTemp);
|
||||
Response.cMyByte1 = (char)ulTemp;
|
||||
|
||||
tag_len =
|
||||
decode_tag_number_and_value(&pData[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT)
|
||||
return;
|
||||
|
||||
iLen += decode_unsigned(&pData[iLen], len_value_type, &ulTemp);
|
||||
Response.cMyByte2 = (char)ulTemp;
|
||||
|
||||
tag_len =
|
||||
decode_tag_number_and_value(&pData[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_REAL)
|
||||
return;
|
||||
|
||||
iLen += decode_real(&pData[iLen], &Response.fMyReal);
|
||||
|
||||
tag_len =
|
||||
decode_tag_number_and_value(&pData[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_CHARACTER_STRING)
|
||||
return;
|
||||
|
||||
iLen += decode_character_string(&pData[iLen], len_value_type, &bsName);
|
||||
strncpy(
|
||||
(char *)Response.sMyString, characterstring_value(&bsName), MY_MAX_STR);
|
||||
Response.sMyString[MY_MAX_STR] = '\0'; /* Make sure it is nul terminated */
|
||||
|
||||
printf("Private Transfer Read Block Response\n");
|
||||
printf("Data Block: %d\n", (int)cBlockNum);
|
||||
printf(" First Byte : %d\n", (int)Response.cMyByte1);
|
||||
printf(" Second Byte : %d\n", (int)Response.cMyByte2);
|
||||
printf(" Real : %f\n", Response.fMyReal);
|
||||
printf(" String : %s\n\n", Response.sMyString);
|
||||
}
|
||||
|
||||
static void ProcessPTA(BACNET_PRIVATE_TRANSFER_DATA *data)
|
||||
{
|
||||
int iLen; /* Index to current location in data */
|
||||
uint32_t uiErrorCode;
|
||||
char cBlockNumber;
|
||||
uint32_t ulTemp;
|
||||
int tag_len;
|
||||
uint8_t tag_number;
|
||||
uint32_t len_value_type;
|
||||
|
||||
iLen = 0;
|
||||
|
||||
/* Error code is returned for read and write operations */
|
||||
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&data->serviceParameters[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
#if PRINT_ENABLED
|
||||
printf("CPTA: Bad Encoding!\n");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
iLen += decode_unsigned(
|
||||
&data->serviceParameters[iLen], len_value_type, &uiErrorCode);
|
||||
|
||||
if (data->serviceNumber == MY_SVC_READ) { /* Read I/O block so should be
|
||||
full block of data or error */
|
||||
/* Decode the error type and if necessary block number and then fetch
|
||||
* the info */
|
||||
|
||||
if (uiErrorCode == MY_ERR_OK) {
|
||||
/* Block Number */
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&data->serviceParameters[iLen], &tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
#if PRINT_ENABLED
|
||||
printf("CPTA: Bad Encoding!\n");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
iLen += decode_unsigned(
|
||||
&data->serviceParameters[iLen], len_value_type, &ulTemp);
|
||||
cBlockNumber = (char)ulTemp;
|
||||
DecodeBlock(cBlockNumber, &data->serviceParameters[iLen]);
|
||||
} else { /* Read error */
|
||||
printf("Private Transfer read operation returned error code: %lu\n",
|
||||
(unsigned long)uiErrorCode);
|
||||
return;
|
||||
}
|
||||
} else { /* Write I/O block - should just be an OK type message */
|
||||
printf("Private Transfer write operation returned error code: %lu\n",
|
||||
(unsigned long)uiErrorCode);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called when we receive a private transfer packet ack.
|
||||
* We parse the response which the remote application generated
|
||||
* and decide what to do next...
|
||||
*/
|
||||
|
||||
void handler_conf_private_trans_ack(uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS *src,
|
||||
BACNET_CONFIRMED_SERVICE_ACK_DATA *service_data)
|
||||
{
|
||||
BACNET_PRIVATE_TRANSFER_DATA data;
|
||||
int len;
|
||||
|
||||
/*
|
||||
* Note:
|
||||
* We currently don't look at the source address and service data
|
||||
* but we probably should to verify that the ack is oneit is what
|
||||
* we were expecting. But this is just to silence some compiler
|
||||
* warnings from Borland.
|
||||
*/
|
||||
src = src;
|
||||
service_data = service_data;
|
||||
|
||||
len = 0;
|
||||
|
||||
#if PRINT_ENABLED
|
||||
printf("Received Confirmed Private Transfer Ack!\n");
|
||||
#endif
|
||||
|
||||
len = ptransfer_decode_service_request(service_request, service_len,
|
||||
&data); /* Same decode for ack as for service request! */
|
||||
if (len < 0) {
|
||||
#if PRINT_ENABLED
|
||||
printf("cpta: Bad Encoding!\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
ProcessPTA(&data); /* See what to do with the response */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void PTErrorHandler(
|
||||
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;
|
||||
printf("BACnet Error: %s: %s\r\n",
|
||||
bactext_error_class_name((int) error_class),
|
||||
bactext_error_code_name((int) error_code));
|
||||
Error_Detected = true;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg
|
||||
* @date October 2019
|
||||
* @brief Header file for a basic ConfirmedPrivateTransfer-Ack service handler
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef HANDLER_CONFIRMED_PRIVATE_TRANSFER_ACK_H
|
||||
#define HANDLER_CONFIRMED_PRIVATE_TRANSFER_ACK_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/apdu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void handler_conf_private_trans_ack(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,401 @@
|
||||
/*************************************************************************
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <time.h> /* for time */
|
||||
#include <ctype.h> /* for tupper */
|
||||
#if defined(WIN32) || defined(__BORLANDC__)
|
||||
#include <conio.h>
|
||||
#endif
|
||||
#define PRINT_ENABLED 1
|
||||
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/arf.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/whois.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/sys/filename.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/datalink/dlenv.h"
|
||||
|
||||
#if defined(__BORLANDC__)
|
||||
#define _kbhit kbhit
|
||||
#define _stricmp stricmp
|
||||
#endif
|
||||
|
||||
#define MY_MAX_STR 32
|
||||
#define MY_MAX_BLOCK 8
|
||||
|
||||
#define MY_SVC_READ 0
|
||||
#define MY_SVC_WRITE 1
|
||||
|
||||
#define MY_ERR_OK 0
|
||||
#define MY_ERR_BAD_INDEX 1
|
||||
|
||||
typedef struct MyData {
|
||||
uint8_t cMyByte1;
|
||||
uint8_t cMyByte2;
|
||||
float fMyReal;
|
||||
int8_t sMyString[MY_MAX_STR + 1]; /* A little extra for the nul */
|
||||
} DATABLOCK;
|
||||
|
||||
/* 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 int Target_Mode = 0;
|
||||
|
||||
static BACNET_ADDRESS Target_Address;
|
||||
static bool Error_Detected = false;
|
||||
|
||||
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;
|
||||
printf("BACnet Error: %s: %s\r\n",
|
||||
bactext_error_class_name((int)error_class),
|
||||
bactext_error_code_name((int)error_code));
|
||||
/* Error_Detected = true; */
|
||||
}
|
||||
|
||||
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((int)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((int)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);
|
||||
/* 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);
|
||||
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_PRIVATE_TRANSFER, handler_conf_private_trans);
|
||||
/* handle the data coming back from confirmed requests */
|
||||
apdu_set_confirmed_ack_handler(
|
||||
SERVICE_CONFIRMED_READ_PROPERTY, handler_read_property_ack);
|
||||
|
||||
apdu_set_confirmed_ack_handler(
|
||||
SERVICE_CONFIRMED_PRIVATE_TRANSFER, handler_conf_private_trans_ack);
|
||||
|
||||
/* handle any errors coming back */
|
||||
apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler);
|
||||
apdu_set_error_handler(SERVICE_CONFIRMED_PRIVATE_TRANSFER, MyErrorHandler);
|
||||
apdu_set_abort_handler(MyAbortHandler);
|
||||
apdu_set_reject_handler(MyRejectHandler);
|
||||
}
|
||||
|
||||
len += encode_application_unsigned(
|
||||
&pt_req_buffer[len], block_number); /* The block number */
|
||||
len += encode_application_unsigned(
|
||||
&pt_req_buffer[len], block->cMyByte1); /* And Then the block contents */
|
||||
len += encode_application_unsigned(&pt_req_buffer[len], block->cMyByte2);
|
||||
len += encode_application_real(&pt_req_buffer[len], block->fMyReal);
|
||||
characterstring_init_ansi(&bsTemp, (char *)block->sMyString);
|
||||
len += encode_application_character_string(&pt_req_buffer[len], &bsTemp);
|
||||
}
|
||||
|
||||
pt_block.serviceParameters = &pt_req_buffer[0];
|
||||
pt_block.serviceParametersLen = len;
|
||||
|
||||
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;
|
||||
DATABLOCK NewData;
|
||||
int iCount = 0;
|
||||
int iType = 0;
|
||||
int iKey;
|
||||
|
||||
if (((argc != 2) && (argc != 3)) ||
|
||||
((argc >= 2) && (strcmp(argv[1], "--help") == 0))) {
|
||||
printf("%s\n", argv[0]);
|
||||
printf("Usage: %s server local-device-instance\r\n or\r\n"
|
||||
" %s remote-device-instance\r\n",
|
||||
filename_remove_path(argv[0]), filename_remove_path(argv[0]));
|
||||
if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) {
|
||||
printf(
|
||||
"\r\nServer mode:\r\n\r\n"
|
||||
"local-device-instance determins the device id of the "
|
||||
"application\r\n"
|
||||
"when running as the server end of a test set up.\r\n\r\n"
|
||||
"Non server:\r\n\r\n"
|
||||
"remote-device-instance indicates the device id of the "
|
||||
"server\r\n"
|
||||
"instance of the application.\r\n"
|
||||
"The non server application will write a series of blocks to "
|
||||
"the\r\n"
|
||||
"server and then retrieve them for display locally\r\n"
|
||||
"First it writes all 8 blocks plus a 9th which should "
|
||||
"trigger\r\n"
|
||||
"an out of range error response. Then it reads all the "
|
||||
"blocks\r\n"
|
||||
"including the ninth and finally it repeats the read "
|
||||
"operation\r\n"
|
||||
"with some deliberate errors to trigger a nack response\r\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* decode the command line parameters */
|
||||
if (_stricmp(argv[1], "server") == 0) {
|
||||
Target_Mode = 1;
|
||||
} else {
|
||||
Target_Mode = 0;
|
||||
}
|
||||
|
||||
Target_Device_Object_Instance = strtol(argv[1 + Target_Mode], 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 */
|
||||
if (Target_Mode) {
|
||||
Device_Set_Object_Instance_Number(Target_Device_Object_Instance);
|
||||
} else {
|
||||
Device_Set_Object_Instance_Number
|
||||
}
|
||||
(BACNET_MAX_INSTANCE);
|
||||
|
||||
address_init();
|
||||
Init_Service_Handlers();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
timeout_seconds = (apdu_timeout() / 1000) * apdu_retries();
|
||||
|
||||
if (Target_Mode) {
|
||||
printf("Entering server mode. press q to quit program\r\n\r\n");
|
||||
|
||||
for (;;) {
|
||||
/* increment timer - exit if timed out */
|
||||
current_seconds = time(NULL);
|
||||
if (current_seconds != last_seconds) {
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
putchar('.'); /* Just to show that time is passing... */
|
||||
last_seconds = current_seconds;
|
||||
tsm_timer_milliseconds(
|
||||
((current_seconds - last_seconds) * 1000));
|
||||
}
|
||||
|
||||
if (_kbhit()) {
|
||||
iKey = toupper(_getch());
|
||||
if (iKey == 'Q') {
|
||||
printf("\r\nExiting program now\r\n");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* try to bind with the device */
|
||||
found = address_bind_request(
|
||||
Target_Device_Object_Instance, &max_apdu, &Target_Address);
|
||||
if (!found) {
|
||||
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));
|
||||
}
|
||||
if (Error_Detected)
|
||||
break;
|
||||
/* wait until the device is bound, or timeout and quit */
|
||||
if (!found)
|
||||
found = address_bind_request(
|
||||
Target_Device_Object_Instance, &max_apdu, &Target_Address);
|
||||
if (found) {
|
||||
if (invoke_id == 0) { /* Safe to send a new request */
|
||||
switch (iType) {
|
||||
case 0: /* Write blocks to server */
|
||||
NewData.cMyByte1 = iCount;
|
||||
NewData.cMyByte2 = 255 - iCount;
|
||||
NewData.fMyReal = (float)iCount;
|
||||
strcpy(
|
||||
(char *)NewData.sMyString, "Test Data - [x]");
|
||||
NewData.sMyString[13] = 'a' + iCount;
|
||||
pt_write_block_to_server()
|
||||
printf("Sending block %d\n", iCount);
|
||||
invoke_id = Send_Private_Transfer_Request(
|
||||
Target_Device_Object_Instance, BACNET_VENDOR_ID,
|
||||
1, iCount, &NewData);
|
||||
break;
|
||||
|
||||
case 1: /* Read blocks from server */
|
||||
printf("Requesting block %d\n", iCount);
|
||||
invoke_id = Send_Private_Transfer_Request(
|
||||
Target_Device_Object_Instance, BACNET_VENDOR_ID,
|
||||
0, iCount, &NewData);
|
||||
break;
|
||||
|
||||
case 2: /* Generate some error responses */
|
||||
switch (iCount) {
|
||||
case 0: /* Bad service number i.e. 2 */
|
||||
case 2:
|
||||
case 4:
|
||||
case 6:
|
||||
case 8:
|
||||
printf(
|
||||
"Requesting block %d with bad service "
|
||||
"number\n",
|
||||
iCount);
|
||||
invoke_id = Send_Private_Transfer_Request(
|
||||
Target_Device_Object_Instance,
|
||||
BACNET_VENDOR_ID, 2, iCount, &NewData);
|
||||
break;
|
||||
|
||||
case 1: /* Bad vendor ID number */
|
||||
case 3:
|
||||
case 5:
|
||||
case 7:
|
||||
printf("Requesting block %d with invalid "
|
||||
"Vendor ID\n",
|
||||
iCount);
|
||||
invoke_id = Send_Private_Transfer_Request(
|
||||
Target_Device_Object_Instance,
|
||||
BACNET_VENDOR_ID + 1, 0, iCount,
|
||||
&NewData);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} else if (tsm_invoke_id_free(invoke_id)) {
|
||||
if (iCount != MY_MAX_BLOCK) {
|
||||
iCount++;
|
||||
invoke_id = 0;
|
||||
} else {
|
||||
iType++;
|
||||
iCount = 0;
|
||||
invoke_id = 0;
|
||||
|
||||
if (iType > 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (tsm_invoke_id_failed(invoke_id)) {
|
||||
fprintf(stderr, "\rError: TSM Timeout!\r\n");
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
Error_Detected = true;
|
||||
/* try again or abort? */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* increment timer - exit if timed out */
|
||||
elapsed_seconds += (current_seconds - last_seconds);
|
||||
if (elapsed_seconds > timeout_seconds) {
|
||||
printf("\rError: APDU Timeout!\r\n");
|
||||
Error_Detected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* keep track of time for next check */
|
||||
last_seconds = current_seconds;
|
||||
}
|
||||
}
|
||||
if (Error_Detected)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
||||
# Visual C++ Express 2008
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rdproperty", "rdproperty.vcproj", "{7AC60281-278A-4060-B900-A1343E438701}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{7AC60281-278A-4060-B900-A1343E438701}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{7AC60281-278A-4060-B900-A1343E438701}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{7AC60281-278A-4060-B900-A1343E438701}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{7AC60281-278A-4060-B900-A1343E438701}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,123 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/dcc.h"
|
||||
#include "bacnet/ptransfer.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
|
||||
/** @file s_ptransfer.c Send a Private Transfer request. */
|
||||
|
||||
uint8_t Send_Private_Transfer_Request(uint32_t device_id,
|
||||
uint16_t vendor_id,
|
||||
uint32_t service_number,
|
||||
unsigned int block_number,
|
||||
char *block)
|
||||
{ /* NULL=optional */
|
||||
BACNET_ADDRESS dest;
|
||||
BACNET_ADDRESS my_address;
|
||||
unsigned max_apdu = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
bool status = false;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
static uint8_t
|
||||
pt_req_buffer[300]; /* Somewhere to build the request packet */
|
||||
BACNET_PRIVATE_TRANSFER_DATA private_data;
|
||||
|
||||
/* if we are forbidden to send, don't send! */
|
||||
if (!dcc_communication_enabled()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* is the device bound? */
|
||||
status = address_get_by_device(device_id, &max_apdu, &dest);
|
||||
/* is there a tsm available? */
|
||||
if (status)
|
||||
invoke_id = tsm_next_free_invokeID();
|
||||
if (invoke_id) {
|
||||
/* encode the NPDU portion of the packet */
|
||||
datalink_get_my_address(&my_address);
|
||||
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len = npdu_encode_pdu(
|
||||
&Handler_Transmit_Buffer[0], &dest, &my_address, &npdu_data);
|
||||
/* encode the APDU portion of the packet */
|
||||
private_data.vendorID = vendor_id;
|
||||
private_data.serviceNumber = service_number;
|
||||
len = uptransfer_encode_apdu(
|
||||
&Handler_Transmit_Buffer[pdu_len], &private_data);
|
||||
pdu_len += len;
|
||||
|
||||
if (service_number == MY_SVC_READ) {
|
||||
} else {
|
||||
len = ptransfer_encode_apdu(
|
||||
&Handler_Transmit_Buffer[pdu_len], invoke_id, &pt_block);
|
||||
pdu_len += len;
|
||||
|
||||
/* will it fit in the sender?
|
||||
note: if there is a bottleneck router in between
|
||||
us and the destination, we won't know unless
|
||||
we have a way to check for that and update the
|
||||
max_apdu in the address binding table. */
|
||||
|
||||
if ((unsigned)pdu_len < max_apdu) {
|
||||
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
|
||||
&npdu_data, &Handler_Transmit_Buffer[0], (uint16_t)pdu_len);
|
||||
bytes_sent = datalink_send_pdu(
|
||||
&dest, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
|
||||
#if PRINT_ENABLED
|
||||
if (bytes_sent <= 0)
|
||||
fprintf(stderr,
|
||||
"Failed to Send Private Transfer Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send Private Transfer Request "
|
||||
"(exceeds destination maximum APDU)!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg
|
||||
* @date October 2019
|
||||
* @brief Header file for a basic WritePropertyMultiple service send
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef SEND_WRITE_PROPERTY_MULTIPLE_H
|
||||
#define SEND_WRITE_PROPERTY_MULTIPLE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/apdu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
uint8_t Send_Private_Transfer_Request(uint32_t device_id, uint16_t vendor_id,
|
||||
uint32_t service_number,
|
||||
char block_number, DATABLOCK *block);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
#Makefile to build BACnet Application for the GCC Port
|
||||
|
||||
TARGET = bacrbdt
|
||||
# BACnet objects that are used with this app
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/client/device-client.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c
|
||||
BACNET_BASIC_SRC += \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_apdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_noserv.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_whois.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_whois.c
|
||||
|
||||
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = $(SRC) $(BACNET_SRC) $(BACNET_BASIC_SRC) $(BACNET_PORT_SRC)
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h> /* for time */
|
||||
#include <errno.h>
|
||||
#include <ctype.h> /* for toupper */
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/datalink/bvlc.h"
|
||||
/* some demo stuff needed */
|
||||
#ifndef DEBUG_ENABLED
|
||||
#define DEBUG_ENABLED 0
|
||||
#endif
|
||||
#include "bacnet/basic/sys/debug.h"
|
||||
#include "bacnet/basic/sys/filename.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/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;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#Makefile to build BACnet Application for the Linux Port
|
||||
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
|
||||
TARGET = bacarf
|
||||
# BACnet objects that are used with this app
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/client/device-client.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c
|
||||
BACNET_BASIC_SRC += \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_apdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_noserv.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_whois.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_arfs.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_whois.c
|
||||
|
||||
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = $(SRC) $(BACNET_SRC) $(BACNET_BASIC_SRC) $(BACNET_PORT_SRC)
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
|
||||
@@ -0,0 +1,392 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h> /* for time */
|
||||
#include <errno.h>
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/arf.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacport.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/whois.h"
|
||||
#include "bacnet/version.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/sys/filename.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/dlenv.h"
|
||||
|
||||
/* buffer used for receive */
|
||||
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
|
||||
|
||||
/* global variables used in this file */
|
||||
static uint32_t Target_File_Object_Instance = BACNET_MAX_INSTANCE;
|
||||
static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE;
|
||||
static BACNET_ADDRESS Target_Address;
|
||||
static char *Local_File_Name = NULL;
|
||||
static int Target_File_Start_Position;
|
||||
static unsigned int Target_File_Requested_Octet_Count;
|
||||
static bool End_Of_File_Detected = false;
|
||||
static bool Error_Detected = false;
|
||||
static uint8_t Request_Invoke_ID = 0;
|
||||
|
||||
static void Atomic_Read_File_Error_Handler(BACNET_ADDRESS *src,
|
||||
uint8_t invoke_id,
|
||||
BACNET_ERROR_CLASS error_class,
|
||||
BACNET_ERROR_CODE error_code)
|
||||
{
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
printf("BACnet Error: %s: %s\n",
|
||||
bactext_error_class_name((int)error_class),
|
||||
bactext_error_code_name((int)error_code));
|
||||
Error_Detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void MyAbortHandler(
|
||||
BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t abort_reason, bool server)
|
||||
{
|
||||
(void)server;
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
printf(
|
||||
"BACnet Abort: %s\n", bactext_abort_reason_name((int)abort_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void MyRejectHandler(
|
||||
BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t reject_reason)
|
||||
{
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
printf("BACnet Reject: %s\n",
|
||||
bactext_reject_reason_name((int)reject_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void AtomicReadFileAckHandler(uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS *src,
|
||||
BACNET_CONFIRMED_SERVICE_ACK_DATA *service_data)
|
||||
{
|
||||
int len = 0;
|
||||
int result = 0;
|
||||
BACNET_ATOMIC_READ_FILE_DATA data;
|
||||
FILE *pFile = NULL; /* stream pointer */
|
||||
size_t octets_written = 0;
|
||||
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(service_data->invoke_id == Request_Invoke_ID)) {
|
||||
len =
|
||||
arf_ack_decode_service_request(service_request, service_len, &data);
|
||||
if ((len > 0) && (data.access == FILE_STREAM_ACCESS)) {
|
||||
if (data.type.stream.fileStartPosition == 0) {
|
||||
pFile = fopen(Local_File_Name, "wb");
|
||||
} else {
|
||||
pFile = fopen(Local_File_Name, "rb+");
|
||||
}
|
||||
if (pFile) {
|
||||
result =
|
||||
fseek(pFile, data.type.stream.fileStartPosition, SEEK_SET);
|
||||
if (result == 0) {
|
||||
/* unit to write in bytes -
|
||||
in our case, an octet is one byte */
|
||||
octets_written =
|
||||
fwrite(octetstring_value(&data.fileData[0]), 1,
|
||||
octetstring_length(&data.fileData[0]), pFile);
|
||||
if (octets_written !=
|
||||
octetstring_length(&data.fileData[0])) {
|
||||
fprintf(stderr,
|
||||
"Unable to write data to file \"%s\".\n",
|
||||
Local_File_Name);
|
||||
} else if (octets_written == 0) {
|
||||
fprintf(stderr, "Received 0 byte octet string!.\n");
|
||||
} else {
|
||||
Target_File_Start_Position =
|
||||
data.type.stream.fileStartPosition + octets_written;
|
||||
printf("\r%d bytes", (int)Target_File_Start_Position);
|
||||
}
|
||||
fflush(pFile);
|
||||
} else {
|
||||
fprintf(stderr, "Unable to seek to %d!\n",
|
||||
data.type.stream.fileStartPosition);
|
||||
}
|
||||
fclose(pFile);
|
||||
}
|
||||
if (data.endOfFile) {
|
||||
End_Of_File_Detected = true;
|
||||
printf("\n");
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Decode error! %d bytes decoded.\n", len);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Address & Invoke ID mismatch! Invoke ID=%d\n",
|
||||
Request_Invoke_ID);
|
||||
}
|
||||
}
|
||||
|
||||
static void LocalIAmHandler(
|
||||
uint8_t *service_request, uint16_t service_len, BACNET_ADDRESS *src)
|
||||
{
|
||||
int len = 0;
|
||||
uint32_t device_id = 0;
|
||||
unsigned max_apdu = 0;
|
||||
int segmentation = 0;
|
||||
uint16_t vendor_id = 0;
|
||||
|
||||
(void)src;
|
||||
(void)service_len;
|
||||
len = iam_decode_service_request(
|
||||
service_request, &device_id, &max_apdu, &segmentation, &vendor_id);
|
||||
if (len != -1) {
|
||||
address_add(device_id, max_apdu, src);
|
||||
} else {
|
||||
fprintf(stderr, "!\n");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
/* handle i-am to support binding to other devices */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, LocalIAmHandler);
|
||||
/* 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_ATOMIC_READ_FILE, AtomicReadFileAckHandler);
|
||||
/* handle any errors coming back */
|
||||
apdu_set_error_handler(
|
||||
SERVICE_CONFIRMED_ATOMIC_READ_FILE, Atomic_Read_File_Error_Handler);
|
||||
apdu_set_abort_handler(MyAbortHandler);
|
||||
apdu_set_reject_handler(MyRejectHandler);
|
||||
}
|
||||
|
||||
static void print_usage(char *filename)
|
||||
{
|
||||
printf("Usage: %s device-instance file-instance local-name\n", filename);
|
||||
printf(" [--version][--help]\n");
|
||||
}
|
||||
|
||||
static void print_help(char *filename)
|
||||
{
|
||||
printf(
|
||||
"Read a file from a BACnet device and save it locally.\n"
|
||||
"device-instance:\n"
|
||||
"BACnet Device Object Instance number that you are trying to\n"
|
||||
"communicate to. This number will be used to try and bind with\n"
|
||||
"the device using Who-Is and I-Am services. For example, if you were\n"
|
||||
"reading from Device Object 123, the device-instance would be 123.\n"
|
||||
"\n"
|
||||
"file-instance:\n"
|
||||
"This is the file object instance number that you are reading from.\n"
|
||||
"For example, if you were reading from File 2, \n"
|
||||
"the file-instance would be 2.\n"
|
||||
"\n"
|
||||
"local-name:\n"
|
||||
"The name of the file that will be stored locally.\n"
|
||||
"\n"
|
||||
"Example:\n"
|
||||
"If you want read File 2 from Device 123 and save it to temp.txt,\n"
|
||||
"use the following command:\n"
|
||||
"%s 123 2 temp.txt\n",
|
||||
filename);
|
||||
}
|
||||
|
||||
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;
|
||||
uint16_t my_max_apdu = 0;
|
||||
int argi = 0;
|
||||
char *filename = NULL;
|
||||
|
||||
/* print help if requested */
|
||||
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) 2014 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 (argc < 4) {
|
||||
print_usage(filename);
|
||||
return 0;
|
||||
}
|
||||
/* decode the command line parameters */
|
||||
Target_Device_Object_Instance = strtol(argv[1], NULL, 0);
|
||||
Target_File_Object_Instance = strtol(argv[2], NULL, 0);
|
||||
Local_File_Name = argv[3];
|
||||
if (Target_Device_Object_Instance >= BACNET_MAX_INSTANCE) {
|
||||
fprintf(stderr, "device-instance=%u - it must be less than %u\n",
|
||||
Target_Device_Object_Instance, BACNET_MAX_INSTANCE);
|
||||
return 1;
|
||||
}
|
||||
if (Target_File_Object_Instance >= BACNET_MAX_INSTANCE) {
|
||||
fprintf(stderr, "file-instance=%u - it must be less than %u\n",
|
||||
Target_File_Object_Instance, BACNET_MAX_INSTANCE + 1);
|
||||
return 1;
|
||||
}
|
||||
/* setup my info */
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
address_init();
|
||||
Init_Service_Handlers();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
timeout_seconds = (apdu_timeout() / 1000) * apdu_retries();
|
||||
/* try to bind with the device */
|
||||
found = address_bind_request(
|
||||
Target_Device_Object_Instance, &max_apdu, &Target_Address);
|
||||
if (!found) {
|
||||
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 */
|
||||
if (!found) {
|
||||
found = address_bind_request(
|
||||
Target_Device_Object_Instance, &max_apdu, &Target_Address);
|
||||
}
|
||||
if (found) {
|
||||
/* calculate the smaller of our APDU size or theirs
|
||||
and remove the overhead of the APDU (about 16 octets max).
|
||||
note: we could fail if there is a bottle neck (router)
|
||||
and smaller MPDU in betweeen. */
|
||||
if (max_apdu < MAX_APDU) {
|
||||
my_max_apdu = max_apdu;
|
||||
} else {
|
||||
my_max_apdu = MAX_APDU;
|
||||
}
|
||||
/* Typical sizes are 50, 128, 206, 480, 1024, and 1476 octets */
|
||||
if (my_max_apdu <= 50) {
|
||||
Target_File_Requested_Octet_Count = my_max_apdu - 20;
|
||||
} else if (my_max_apdu <= 480) {
|
||||
Target_File_Requested_Octet_Count = my_max_apdu - 32;
|
||||
} else if (my_max_apdu <= 1476) {
|
||||
Target_File_Requested_Octet_Count = my_max_apdu - 64;
|
||||
} else {
|
||||
Target_File_Requested_Octet_Count = my_max_apdu / 2;
|
||||
}
|
||||
/* has the previous invoke id expired or returned?
|
||||
note: invoke ID = 0 is invalid, so it will be idle */
|
||||
if ((invoke_id == 0) || tsm_invoke_id_free(invoke_id)) {
|
||||
if (End_Of_File_Detected || Error_Detected) {
|
||||
break;
|
||||
}
|
||||
/* the ACK will increment the start position if OK */
|
||||
/* we'll read the file in chunks
|
||||
less than max_apdu to keep unsegmented */
|
||||
invoke_id =
|
||||
Send_Atomic_Read_File_Stream(Target_Device_Object_Instance,
|
||||
Target_File_Object_Instance, Target_File_Start_Position,
|
||||
Target_File_Requested_Octet_Count);
|
||||
Request_Invoke_ID = invoke_id;
|
||||
} else if (tsm_invoke_id_failed(invoke_id)) {
|
||||
fprintf(stderr, "\rError: TSM Timeout!\n");
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
/* try again or abort? */
|
||||
Error_Detected = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* increment timer - exit if timed out */
|
||||
elapsed_seconds += (current_seconds - last_seconds);
|
||||
if (elapsed_seconds > timeout_seconds) {
|
||||
fprintf(stderr, "\rError: APDU Timeout!\n");
|
||||
Error_Detected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* keep track of time for next check */
|
||||
last_seconds = current_seconds;
|
||||
}
|
||||
|
||||
if (Error_Detected) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,50 @@
|
||||
#Makefile to build BACnet Application using GCC compiler
|
||||
|
||||
# Executable file name
|
||||
TARGET = bacrp
|
||||
# BACnet objects that are used with this app
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/client/device-client.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c
|
||||
BACNET_BASIC_SRC += \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_apdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_noserv.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp_a.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_whois.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_whois.c
|
||||
|
||||
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = $(SRC) $(BACNET_SRC) $(BACNET_BASIC_SRC) $(BACNET_PORT_SRC)
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<CodeBlocks_project_file>
|
||||
<FileVersion major="1" minor="6" />
|
||||
<Project>
|
||||
<Option title="BACnet ReadProperty Demo" />
|
||||
<Option pch_mode="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Build>
|
||||
<Target title="Debug">
|
||||
<Option output="bin\Debug\bacrp" prefix_auto="1" extension_auto="1" />
|
||||
<Option object_output="obj\Debug\" />
|
||||
<Option type="1" />
|
||||
<Option compiler="gcc" />
|
||||
<Compiler>
|
||||
<Add option="-g" />
|
||||
</Compiler>
|
||||
</Target>
|
||||
<Target title="Release">
|
||||
<Option output="bin\Release\bacrp" prefix_auto="1" extension_auto="1" />
|
||||
<Option object_output="obj\Release\" />
|
||||
<Option type="1" />
|
||||
<Option compiler="gcc" />
|
||||
<Compiler>
|
||||
<Add option="-O2" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add option="-s" />
|
||||
</Linker>
|
||||
</Target>
|
||||
</Build>
|
||||
<Compiler>
|
||||
<Add option="-Wall" />
|
||||
<Add option="-DBACDL_BIP" />
|
||||
<Add option="-DPRINT_ENABLED=1" />
|
||||
<Add option="-DBACAPP_ALL" />
|
||||
<Add directory="..\..\include" />
|
||||
<Add directory="..\handler" />
|
||||
<Add directory="..\object" />
|
||||
<Add directory="..\..\ports\win32" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add library="..\..\lib\libbacnet.a" />
|
||||
<Add library="ws2_32" />
|
||||
<Add library="iphlpapi" />
|
||||
</Linker>
|
||||
<Unit filename="..\object\device-client.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="main.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Extensions>
|
||||
<code_completion />
|
||||
<envvars />
|
||||
<debugger />
|
||||
</Extensions>
|
||||
</Project>
|
||||
</CodeBlocks_project_file>
|
||||
@@ -0,0 +1,418 @@
|
||||
/*************************************************************************
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <time.h> /* for time */
|
||||
|
||||
#define PRINT_ENABLED 1
|
||||
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/arf.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacport.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/whois.h"
|
||||
#include "bacnet/version.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/sys/filename.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/dlenv.h"
|
||||
|
||||
/* buffer used for receive */
|
||||
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
|
||||
|
||||
/* converted command line arguments */
|
||||
static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE;
|
||||
static uint32_t Target_Object_Instance = BACNET_MAX_INSTANCE;
|
||||
static BACNET_OBJECT_TYPE Target_Object_Type = OBJECT_ANALOG_INPUT;
|
||||
static BACNET_PROPERTY_ID Target_Object_Property = PROP_ACKED_TRANSITIONS;
|
||||
static int32_t Target_Object_Index = BACNET_ARRAY_ALL;
|
||||
/* the invoke id is needed to filter incoming messages */
|
||||
static uint8_t Request_Invoke_ID = 0;
|
||||
static BACNET_ADDRESS Target_Address;
|
||||
static bool Error_Detected = false;
|
||||
|
||||
static void MyErrorHandler(BACNET_ADDRESS *src,
|
||||
uint8_t invoke_id,
|
||||
BACNET_ERROR_CLASS error_class,
|
||||
BACNET_ERROR_CODE error_code)
|
||||
{
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
printf("BACnet Error: %s: %s\n",
|
||||
bactext_error_class_name((int)error_class),
|
||||
bactext_error_code_name((int)error_code));
|
||||
Error_Detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void MyAbortHandler(
|
||||
BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t abort_reason, bool server)
|
||||
{
|
||||
(void)server;
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
printf(
|
||||
"BACnet Abort: %s\n", bactext_abort_reason_name((int)abort_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void MyRejectHandler(
|
||||
BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t reject_reason)
|
||||
{
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
printf("BACnet Reject: %s\n",
|
||||
bactext_reject_reason_name((int)reject_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Handler for a ReadProperty ACK.
|
||||
* @ingroup DSRP
|
||||
* Doesn't actually do anything, except, for debugging, to
|
||||
* print out the ACK data of a matching request.
|
||||
*
|
||||
* @param service_request [in] The contents of the service request.
|
||||
* @param service_len [in] The length of the service_request.
|
||||
* @param src [in] BACNET_ADDRESS of the source of the message
|
||||
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
|
||||
* decoded from the APDU header of this message.
|
||||
*/
|
||||
static void My_Read_Property_Ack_Handler(uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS *src,
|
||||
BACNET_CONFIRMED_SERVICE_ACK_DATA *service_data)
|
||||
{
|
||||
int len = 0;
|
||||
BACNET_READ_PROPERTY_DATA data;
|
||||
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(service_data->invoke_id == Request_Invoke_ID)) {
|
||||
len =
|
||||
rp_ack_decode_service_request(service_request, service_len, &data);
|
||||
if (len < 0) {
|
||||
printf("<decode failed!>\n");
|
||||
} else {
|
||||
rp_ack_print_data(&data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
/* 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, My_Read_Property_Ack_Handler);
|
||||
/* 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 void print_usage(char *filename)
|
||||
{
|
||||
printf("Usage: %s device-instance object-type object-instance "
|
||||
"property [index]\n",
|
||||
filename);
|
||||
printf(" [--dnet][--dadr][--mac]\n");
|
||||
printf(" [--version][--help]\n");
|
||||
}
|
||||
|
||||
static void print_help(char *filename)
|
||||
{
|
||||
printf("Read a property from an object in a BACnet device\n"
|
||||
"and print the value.\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 Object Instance number that you are\n"
|
||||
"trying to communicate to. This number will be used\n"
|
||||
"to try and bind with the device using Who-Is and\n"
|
||||
"I-Am services. For example, if you were reading\n"
|
||||
"Device Object 123, the device-instance would be 123.\n"
|
||||
"\nobject-type:\n"
|
||||
"The object type is the integer value of the enumeration\n"
|
||||
"BACNET_OBJECT_TYPE in bacenum.h. It is the object\n"
|
||||
"that you are reading. For example if you were\n"
|
||||
"reading Analog Output 2, the object-type would be 1.\n"
|
||||
"\nobject-instance:\n"
|
||||
"This is the object instance number of the object that\n"
|
||||
"you are reading. For example, if you were reading\n"
|
||||
"Analog Output 2, the object-instance would be 2.\n"
|
||||
"\nproperty:\n"
|
||||
"The property is an integer value of the enumeration\n"
|
||||
"BACNET_PROPERTY_ID in bacenum.h. It is the property\n"
|
||||
"you are reading. For example, if you were reading the\n"
|
||||
"Present Value property, use 85 as the property.\n"
|
||||
"\nindex:\n"
|
||||
"This integer parameter is the index number of an array.\n"
|
||||
"If the property is an array, individual elements can\n"
|
||||
"be read. If this parameter is missing and the property\n"
|
||||
"is an array, the entire array will be read.\n"
|
||||
"\nExample:\n"
|
||||
"If you want read the Present-Value of Analog Output 101\n"
|
||||
"in Device 123, you could send the following command:\n"
|
||||
"%s 123 1 101 85\n"
|
||||
"If you want read the Priority-Array of Analog Output 101\n"
|
||||
"in Device 123, you could send the following command:\n"
|
||||
"%s 123 1 101 87\n",
|
||||
filename, filename);
|
||||
}
|
||||
|
||||
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;
|
||||
bool found = false;
|
||||
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) 2015 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_Object_Instance = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else if (target_args == 1) {
|
||||
Target_Object_Type = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else if (target_args == 2) {
|
||||
Target_Object_Instance = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else if (target_args == 3) {
|
||||
Target_Object_Property = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else if (target_args == 4) {
|
||||
Target_Object_Index = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else {
|
||||
print_usage(filename);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (target_args < 4) {
|
||||
print_usage(filename);
|
||||
return 0;
|
||||
}
|
||||
if (Target_Device_Object_Instance > BACNET_MAX_INSTANCE) {
|
||||
fprintf(stderr, "device-instance=%u - it must be less than %u\n",
|
||||
Target_Device_Object_Instance, BACNET_MAX_INSTANCE);
|
||||
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;
|
||||
}
|
||||
address_add(Target_Device_Object_Instance, MAX_APDU, &dest);
|
||||
}
|
||||
/* setup my info */
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
Init_Service_Handlers();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
timeout_seconds = (apdu_timeout() / 1000) * apdu_retries();
|
||||
/* try to bind with the device */
|
||||
found = address_bind_request(
|
||||
Target_Device_Object_Instance, &max_apdu, &Target_Address);
|
||||
if (!found) {
|
||||
Send_WhoIs(
|
||||
Target_Device_Object_Instance, Target_Device_Object_Instance);
|
||||
}
|
||||
/* loop forever */
|
||||
for (;;) {
|
||||
/* increment timer - exit if timed out */
|
||||
current_seconds = time(NULL);
|
||||
|
||||
/* at least one second has passed */
|
||||
if (current_seconds != last_seconds) {
|
||||
tsm_timer_milliseconds(
|
||||
(uint16_t)((current_seconds - last_seconds) * 1000));
|
||||
}
|
||||
if (Error_Detected)
|
||||
break;
|
||||
/* wait until the device is bound, or timeout and quit */
|
||||
if (!found) {
|
||||
found = address_bind_request(
|
||||
Target_Device_Object_Instance, &max_apdu, &Target_Address);
|
||||
}
|
||||
if (found) {
|
||||
if (Request_Invoke_ID == 0) {
|
||||
Request_Invoke_ID =
|
||||
Send_Read_Property_Request(Target_Device_Object_Instance,
|
||||
Target_Object_Type, Target_Object_Instance,
|
||||
Target_Object_Property, Target_Object_Index);
|
||||
} else if (tsm_invoke_id_free(Request_Invoke_ID)) {
|
||||
break;
|
||||
} else if (tsm_invoke_id_failed(Request_Invoke_ID)) {
|
||||
fprintf(stderr, "\rError: TSM Timeout!\n");
|
||||
tsm_free_invoke_id(Request_Invoke_ID);
|
||||
Error_Detected = true;
|
||||
/* try again or abort? */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* increment timer - exit if timed out */
|
||||
elapsed_seconds += (current_seconds - last_seconds);
|
||||
if (elapsed_seconds > timeout_seconds) {
|
||||
printf("\rError: APDU Timeout!\n");
|
||||
Error_Detected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* keep track of time for next check */
|
||||
last_seconds = current_seconds;
|
||||
}
|
||||
|
||||
if (Error_Detected)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#Makefile to build BACnet Application for the Linux Port
|
||||
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
|
||||
# Executable file name
|
||||
TARGET = bacrpm
|
||||
# BACnet objects that are used with this app
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/client/device-client.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c
|
||||
BACNET_BASIC_SRC += \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_apdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_noserv.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp_a.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rpm_a.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_whois.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_rpm.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_whois.c
|
||||
|
||||
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = $(SRC) $(BACNET_SRC) $(BACNET_BASIC_SRC) $(BACNET_PORT_SRC)
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<CodeBlocks_project_file>
|
||||
<FileVersion major="1" minor="6" />
|
||||
<Project>
|
||||
<Option title="BACnet ReadProperty Demo" />
|
||||
<Option pch_mode="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Build>
|
||||
<Target title="Debug">
|
||||
<Option output="bin\Debug\bacrpm" prefix_auto="1" extension_auto="1" />
|
||||
<Option object_output="obj\Debug\" />
|
||||
<Option type="1" />
|
||||
<Option compiler="gcc" />
|
||||
<Compiler>
|
||||
<Add option="-g" />
|
||||
</Compiler>
|
||||
</Target>
|
||||
<Target title="Release">
|
||||
<Option output="bin\Release\bacrpm" prefix_auto="1" extension_auto="1" />
|
||||
<Option object_output="obj\Release\" />
|
||||
<Option type="1" />
|
||||
<Option compiler="gcc" />
|
||||
<Compiler>
|
||||
<Add option="-O2" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add option="-s" />
|
||||
</Linker>
|
||||
</Target>
|
||||
</Build>
|
||||
<Compiler>
|
||||
<Add option="-Wall" />
|
||||
<Add option="-DBACDL_BIP" />
|
||||
<Add option="-DPRINT_ENABLED=1" />
|
||||
<Add option="-DBACAPP_ALL" />
|
||||
<Add directory="..\..\include" />
|
||||
<Add directory="..\handler" />
|
||||
<Add directory="..\object" />
|
||||
<Add directory="..\..\ports\win32" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add library="..\..\lib\libbacnet.a" />
|
||||
<Add library="ws2_32" />
|
||||
<Add library="iphlpapi" />
|
||||
</Linker>
|
||||
<Unit filename="main.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Extensions>
|
||||
<code_completion />
|
||||
<envvars />
|
||||
<debugger />
|
||||
</Extensions>
|
||||
</Project>
|
||||
</CodeBlocks_project_file>
|
||||
@@ -0,0 +1,479 @@
|
||||
/*************************************************************************
|
||||
* Copyright (C) 2008 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <time.h> /* for time */
|
||||
|
||||
#define PRINT_ENABLED 1
|
||||
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/arf.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacport.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/whois.h"
|
||||
#include "bacnet/version.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/rpm.h"
|
||||
#include "bacnet/basic/sys/filename.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/dlenv.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 BACNET_READ_ACCESS_DATA *Read_Access_Data;
|
||||
/* needed to filter incoming messages */
|
||||
static uint8_t Request_Invoke_ID = 0;
|
||||
static BACNET_ADDRESS Target_Address;
|
||||
/* needed for return value of main application */
|
||||
static bool Error_Detected = false;
|
||||
|
||||
static void MyErrorHandler(BACNET_ADDRESS *src,
|
||||
uint8_t invoke_id,
|
||||
BACNET_ERROR_CLASS error_class,
|
||||
BACNET_ERROR_CODE error_code)
|
||||
{
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
printf("BACnet Error: %s: %s\n",
|
||||
bactext_error_class_name((int)error_class),
|
||||
bactext_error_code_name((int)error_code));
|
||||
Error_Detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void MyAbortHandler(
|
||||
BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t abort_reason, bool server)
|
||||
{
|
||||
(void)server;
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
printf(
|
||||
"BACnet Abort: %s\n", bactext_abort_reason_name((int)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 */
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
printf("BACnet Reject: %s\n",
|
||||
bactext_reject_reason_name((int)reject_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Handler for a ReadPropertyMultiple ACK.
|
||||
* @ingroup DSRPM
|
||||
* For each read property, print out the ACK'd data,
|
||||
* and free the request data items from linked property list.
|
||||
*
|
||||
* @param service_request [in] The contents of the service request.
|
||||
* @param service_len [in] The length of the service_request.
|
||||
* @param src [in] BACNET_ADDRESS of the source of the message
|
||||
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
|
||||
* decoded from the APDU header of this message.
|
||||
*/
|
||||
static void My_Read_Property_Multiple_Ack_Handler(uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS *src,
|
||||
BACNET_CONFIRMED_SERVICE_ACK_DATA *service_data)
|
||||
{
|
||||
int len = 0;
|
||||
BACNET_READ_ACCESS_DATA *rpm_data;
|
||||
BACNET_READ_ACCESS_DATA *old_rpm_data;
|
||||
BACNET_PROPERTY_REFERENCE *rpm_property;
|
||||
BACNET_PROPERTY_REFERENCE *old_rpm_property;
|
||||
BACNET_APPLICATION_DATA_VALUE *value;
|
||||
BACNET_APPLICATION_DATA_VALUE *old_value;
|
||||
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(service_data->invoke_id == Request_Invoke_ID)) {
|
||||
rpm_data = calloc(1, sizeof(BACNET_READ_ACCESS_DATA));
|
||||
if (rpm_data) {
|
||||
len = rpm_ack_decode_service_request(
|
||||
service_request, service_len, rpm_data);
|
||||
}
|
||||
if (len > 0) {
|
||||
while (rpm_data) {
|
||||
rpm_ack_print_data(rpm_data);
|
||||
rpm_property = rpm_data->listOfProperties;
|
||||
while (rpm_property) {
|
||||
value = rpm_property->value;
|
||||
while (value) {
|
||||
old_value = value;
|
||||
value = value->next;
|
||||
free(old_value);
|
||||
}
|
||||
old_rpm_property = rpm_property;
|
||||
rpm_property = rpm_property->next;
|
||||
free(old_rpm_property);
|
||||
}
|
||||
old_rpm_data = rpm_data;
|
||||
rpm_data = rpm_data->next;
|
||||
free(old_rpm_data);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "RPM Ack Malformed! Freeing memory...\n");
|
||||
while (rpm_data) {
|
||||
rpm_property = rpm_data->listOfProperties;
|
||||
while (rpm_property) {
|
||||
value = rpm_property->value;
|
||||
while (value) {
|
||||
old_value = value;
|
||||
value = value->next;
|
||||
free(old_value);
|
||||
}
|
||||
old_rpm_property = rpm_property;
|
||||
rpm_property = rpm_property->next;
|
||||
free(old_rpm_property);
|
||||
}
|
||||
old_rpm_data = rpm_data;
|
||||
rpm_data = rpm_data->next;
|
||||
free(old_rpm_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
/* 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_PROP_MULTIPLE,
|
||||
My_Read_Property_Multiple_Ack_Handler);
|
||||
/* 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 void cleanup(void)
|
||||
{
|
||||
BACNET_READ_ACCESS_DATA *rpm_object;
|
||||
BACNET_READ_ACCESS_DATA *old_rpm_object;
|
||||
BACNET_PROPERTY_REFERENCE *rpm_property;
|
||||
BACNET_PROPERTY_REFERENCE *old_rpm_property;
|
||||
|
||||
rpm_object = Read_Access_Data;
|
||||
old_rpm_object = rpm_object;
|
||||
while (rpm_object) {
|
||||
rpm_property = rpm_object->listOfProperties;
|
||||
while (rpm_property) {
|
||||
old_rpm_property = rpm_property;
|
||||
rpm_property = rpm_property->next;
|
||||
free(old_rpm_property);
|
||||
}
|
||||
old_rpm_object = rpm_object;
|
||||
rpm_object = rpm_object->next;
|
||||
free(old_rpm_object);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_usage(char *filename)
|
||||
{
|
||||
printf("Usage: %s device-instance object-type object-instance "
|
||||
"property[index][,property[index]] [object-type ...]\n",
|
||||
filename);
|
||||
printf(" [--version][--help]\n");
|
||||
}
|
||||
|
||||
static void print_help(char *filename)
|
||||
{
|
||||
printf("Read one or more properties from one or more objects\n"
|
||||
"in a BACnet device and print the value(s).\n"
|
||||
"device-instance:\n"
|
||||
"BACnet Device Object Instance number that you are\n"
|
||||
"trying to communicate to. This number will be used\n"
|
||||
"to try and bind with the device using Who-Is and\n"
|
||||
"I-Am services. For example, if you were reading\n"
|
||||
"Device Object 123, the device-instance would be 123.\n"
|
||||
"\nobject-type:\n"
|
||||
"The object type is the integer value of the enumeration\n"
|
||||
"BACNET_OBJECT_TYPE in bacenum.h. It is the object\n"
|
||||
"that you are reading. For example if you were\n"
|
||||
"reading Analog Output 2, the object-type would be 1.\n"
|
||||
"\nobject-instance:\n"
|
||||
"This is the object instance number of the object that\n"
|
||||
"you are reading. For example, if you were reading\n"
|
||||
"Analog Output 2, the object-instance would be 2.\n"
|
||||
"\nproperty:\n"
|
||||
"The property is an integer value of the enumeration\n"
|
||||
"BACNET_PROPERTY_ID in bacenum.h. It is the property\n"
|
||||
"you are reading. For example, if you were reading the\n"
|
||||
"Present Value property, use 85 as the property.\n"
|
||||
"\n[index]:\n"
|
||||
"This optional integer parameter is the index number of \n"
|
||||
"an array property. Individual elements of an array can\n"
|
||||
"be read. If this parameter is missing and the property\n"
|
||||
"is an array, the entire array will be read.\n"
|
||||
"\nExample:\n"
|
||||
"If you want read the PRESENT_VALUE property and various\n"
|
||||
"array elements of the PRIORITY_ARRAY in Device 123\n"
|
||||
"Analog Output object 99, use the following command:\n"
|
||||
"%s 123 1 99 85,87[0],87\n"
|
||||
"If you want read the PRESENT_VALUE property in objects\n"
|
||||
"Analog Input 77 and Analog Input 78 in Device 123\n"
|
||||
"use the following command:\n"
|
||||
"%s 123 0 77 85 0 78 85\n"
|
||||
"If you want read the ALL property in\n"
|
||||
"Device object 123, you would use the following command:\n"
|
||||
"%s 123 8 123 8\n"
|
||||
"If you want read the OPTIONAL property in\n"
|
||||
"Device object 123, you would use the following command:\n"
|
||||
"%s 123 8 123 80\n"
|
||||
"If you want read the REQUIRED property in\n"
|
||||
"Device object 123, you would use the following command:\n"
|
||||
"%s 123 8 123 105\n",
|
||||
filename, filename, filename, filename, filename);
|
||||
}
|
||||
|
||||
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;
|
||||
int args_remaining = 0, tag_value_arg = 0, arg_sets = 0;
|
||||
time_t elapsed_seconds = 0;
|
||||
time_t last_seconds = 0;
|
||||
time_t current_seconds = 0;
|
||||
time_t timeout_seconds = 0;
|
||||
bool found = false;
|
||||
uint8_t buffer[MAX_PDU] = { 0 };
|
||||
BACNET_READ_ACCESS_DATA *rpm_object;
|
||||
BACNET_PROPERTY_REFERENCE *rpm_property;
|
||||
char *property_token = NULL;
|
||||
unsigned property_id = 0;
|
||||
unsigned property_array_index = 0;
|
||||
int scan_count = 0;
|
||||
int argi = 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) 2014 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 (argc < 5) {
|
||||
print_usage(filename);
|
||||
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\n",
|
||||
Target_Device_Object_Instance, BACNET_MAX_INSTANCE);
|
||||
return 1;
|
||||
}
|
||||
atexit(cleanup);
|
||||
Read_Access_Data = calloc(1, sizeof(BACNET_READ_ACCESS_DATA));
|
||||
rpm_object = Read_Access_Data;
|
||||
args_remaining = (argc - 2);
|
||||
arg_sets = 0;
|
||||
while (rpm_object) {
|
||||
tag_value_arg = 2 + (arg_sets * 3);
|
||||
rpm_object->object_type = strtol(argv[tag_value_arg], NULL, 0);
|
||||
tag_value_arg++;
|
||||
args_remaining--;
|
||||
if (args_remaining <= 0) {
|
||||
fprintf(stderr, "Error: not enough object property triples.\n");
|
||||
return 1;
|
||||
}
|
||||
if (rpm_object->object_type >= MAX_BACNET_OBJECT_TYPE) {
|
||||
fprintf(stderr, "object-type=%u - it must be less than %u\n",
|
||||
rpm_object->object_type, MAX_BACNET_OBJECT_TYPE);
|
||||
return 1;
|
||||
}
|
||||
rpm_object->object_instance = strtol(argv[tag_value_arg], NULL, 0);
|
||||
tag_value_arg++;
|
||||
args_remaining--;
|
||||
if (args_remaining <= 0) {
|
||||
fprintf(stderr, "Error: not enough object property triples.\n");
|
||||
return 1;
|
||||
}
|
||||
if (rpm_object->object_instance > BACNET_MAX_INSTANCE) {
|
||||
fprintf(stderr, "object-instance=%u - it must be less than %u\n",
|
||||
rpm_object->object_instance, BACNET_MAX_INSTANCE + 1);
|
||||
return 1;
|
||||
}
|
||||
rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE));
|
||||
rpm_object->listOfProperties = rpm_property;
|
||||
property_token = strtok(argv[tag_value_arg], ",");
|
||||
/* add all the properties and optional index to our list */
|
||||
while (rpm_property) {
|
||||
scan_count = sscanf(
|
||||
property_token, "%u[%u]", &property_id, &property_array_index);
|
||||
if (scan_count > 0) {
|
||||
rpm_property->propertyIdentifier = property_id;
|
||||
if (rpm_property->propertyIdentifier > MAX_BACNET_PROPERTY_ID) {
|
||||
fprintf(stderr, "property=%u - it must be less than %u\n",
|
||||
rpm_property->propertyIdentifier,
|
||||
MAX_BACNET_PROPERTY_ID + 1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (scan_count > 1) {
|
||||
rpm_property->propertyArrayIndex = property_array_index;
|
||||
} else {
|
||||
rpm_property->propertyArrayIndex = BACNET_ARRAY_ALL;
|
||||
}
|
||||
/* is there another property? */
|
||||
property_token = strtok(NULL, ",");
|
||||
if (property_token) {
|
||||
rpm_property->next =
|
||||
calloc(1, sizeof(BACNET_PROPERTY_REFERENCE));
|
||||
rpm_property = rpm_property->next;
|
||||
} else {
|
||||
rpm_property->next = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* used up another arg */
|
||||
tag_value_arg++;
|
||||
args_remaining--;
|
||||
if (args_remaining) {
|
||||
arg_sets++;
|
||||
rpm_object->next = calloc(1, sizeof(BACNET_READ_ACCESS_DATA));
|
||||
rpm_object = rpm_object->next;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* setup my info */
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
address_init();
|
||||
Init_Service_Handlers();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
timeout_seconds = (apdu_timeout() / 1000) * apdu_retries();
|
||||
/* try to bind with the device */
|
||||
found = address_bind_request(
|
||||
Target_Device_Object_Instance, &max_apdu, &Target_Address);
|
||||
if (!found) {
|
||||
Send_WhoIs(
|
||||
Target_Device_Object_Instance, Target_Device_Object_Instance);
|
||||
}
|
||||
/* loop forever */
|
||||
for (;;) {
|
||||
/* increment timer - exit if timed out */
|
||||
current_seconds = time(NULL);
|
||||
|
||||
/* at least one second has passed */
|
||||
if (current_seconds != last_seconds) {
|
||||
tsm_timer_milliseconds(((current_seconds - last_seconds) * 1000));
|
||||
}
|
||||
if (Error_Detected)
|
||||
break;
|
||||
/* wait until the device is bound, or timeout and quit */
|
||||
if (!found) {
|
||||
found = address_bind_request(
|
||||
Target_Device_Object_Instance, &max_apdu, &Target_Address);
|
||||
}
|
||||
if (found) {
|
||||
if (Request_Invoke_ID == 0) {
|
||||
Request_Invoke_ID = Send_Read_Property_Multiple_Request(
|
||||
&buffer[0], sizeof(buffer), Target_Device_Object_Instance,
|
||||
Read_Access_Data);
|
||||
} else if (tsm_invoke_id_free(Request_Invoke_ID)) {
|
||||
break;
|
||||
} else if (tsm_invoke_id_failed(Request_Invoke_ID)) {
|
||||
fprintf(stderr, "\rError: TSM Timeout!\n");
|
||||
tsm_free_invoke_id(Request_Invoke_ID);
|
||||
Error_Detected = true;
|
||||
/* try again or abort? */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* increment timer - exit if timed out */
|
||||
elapsed_seconds += (current_seconds - last_seconds);
|
||||
if (elapsed_seconds > timeout_seconds) {
|
||||
printf("\rError: APDU Timeout!\n");
|
||||
Error_Detected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* keep track of time for next check */
|
||||
last_seconds = current_seconds;
|
||||
}
|
||||
|
||||
if (Error_Detected)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#Makefile to build BACnet Application using GCC compiler
|
||||
|
||||
# Executable file name
|
||||
TARGET = bacrr
|
||||
# BACnet objects that are used with this app
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/client/device-client.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c
|
||||
BACNET_BASIC_SRC += \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_apdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_noserv.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rr_a.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_whois.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_readrange.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_whois.c
|
||||
|
||||
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = $(SRC) $(BACNET_SRC) $(BACNET_BASIC_SRC) $(BACNET_PORT_SRC)
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
|
||||
@@ -0,0 +1,381 @@
|
||||
/*************************************************************************
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
#define PRINT_ENABLED 1
|
||||
|
||||
/* command line tool that sends a BACnet service, and displays the reply */
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <time.h> /* for time */
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacport.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/whois.h"
|
||||
#include "bacnet/version.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/sys/filename.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/dlenv.h"
|
||||
#include "bacnet/readrange.h"
|
||||
|
||||
/* buffer used for receive */
|
||||
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
|
||||
|
||||
/* converted command line arguments */
|
||||
static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE;
|
||||
static uint32_t Target_Object_Instance = BACNET_MAX_INSTANCE;
|
||||
static BACNET_OBJECT_TYPE Target_Object_Type = OBJECT_ANALOG_INPUT;
|
||||
static BACNET_PROPERTY_ID Target_Object_Property = PROP_ACKED_TRANSITIONS;
|
||||
static long Target_Object_Range_Type = 0;
|
||||
static long Target_Object_Index = 0;
|
||||
static long Target_Object_Count = 0;
|
||||
/* the invoke id is needed to filter incoming messages */
|
||||
static uint8_t Request_Invoke_ID = 0;
|
||||
static BACNET_ADDRESS Target_Address;
|
||||
static bool Error_Detected = false;
|
||||
/* specific request data */
|
||||
static BACNET_READ_RANGE_DATA RR_Request;
|
||||
|
||||
static void MyErrorHandler(BACNET_ADDRESS *src,
|
||||
uint8_t invoke_id,
|
||||
BACNET_ERROR_CLASS error_class,
|
||||
BACNET_ERROR_CODE error_code)
|
||||
{
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
printf("BACnet Error: %s: %s\r\n",
|
||||
bactext_error_class_name((int)error_class),
|
||||
bactext_error_code_name((int)error_code));
|
||||
Error_Detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void MyAbortHandler(
|
||||
BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t abort_reason, bool server)
|
||||
{
|
||||
(void)server;
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
printf(
|
||||
"BACnet Abort: %s\n", bactext_abort_reason_name((int)abort_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void MyRejectHandler(
|
||||
BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t reject_reason)
|
||||
{
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
printf("BACnet Reject: %s\n",
|
||||
bactext_reject_reason_name((int)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);
|
||||
/* 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_RANGE, handler_read_range_ack);
|
||||
|
||||
/* handle any errors coming back */
|
||||
apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler);
|
||||
apdu_set_error_handler(SERVICE_CONFIRMED_PRIVATE_TRANSFER, MyErrorHandler);
|
||||
apdu_set_abort_handler(MyAbortHandler);
|
||||
apdu_set_reject_handler(MyRejectHandler);
|
||||
}
|
||||
|
||||
static void print_usage(char *filename)
|
||||
{
|
||||
printf("Usage: %s device-instance object-type object-instance property\n",
|
||||
filename);
|
||||
printf(" range-type <index|<date time>> count\n");
|
||||
printf(" [--version][--help]\n");
|
||||
}
|
||||
|
||||
static void print_help(char *filename)
|
||||
{
|
||||
printf("Read a range of properties from an array or list property\n"
|
||||
"in an object in a BACnet device and print the values.\n"
|
||||
"device-instance:\n"
|
||||
"BACnet Device Object Instance number that you are\n"
|
||||
"trying to communicate to. This number will be used\n"
|
||||
"to try and bind with the device using Who-Is and\n"
|
||||
"I-Am services. For example, if you were reading\n"
|
||||
"Device Object 123, the device-instance would be 123.\n"
|
||||
"\nobject-type:\n"
|
||||
"The object type is the integer value of the enumeration\n"
|
||||
"BACNET_OBJECT_TYPE in bacenum.h. It is the object\n"
|
||||
"that you are reading. For example if you were\n"
|
||||
"reading Trend Log 2, the object-type would be 20.\n"
|
||||
"\nobject-instance:\n"
|
||||
"This is the object instance number of the object that\n"
|
||||
"you are reading. For example, if you were reading\n"
|
||||
"Trend Log 2, the object-instance would be 2.\n"
|
||||
"\nproperty:\n"
|
||||
"The property is an integer value of the enumeration\n"
|
||||
"BACNET_PROPERTY_ID in bacenum.h. It is the property\n"
|
||||
"you are reading. For example, if you were reading the\n"
|
||||
"Log_Buffer property, use 131 as the property.\n"
|
||||
"\nrange-type:\n"
|
||||
"1=By Position\n"
|
||||
"2=By Sequence\n"
|
||||
"3=By Time\n"
|
||||
"4=All\n"
|
||||
"\nindex or date/time:\n"
|
||||
"This integer parameter is the starting index, or date & time.\n"
|
||||
"\ncount:\n"
|
||||
"This integer parameter is the number of elements to read.\n"
|
||||
"\nExample:\n"
|
||||
"If you want read the Log_Buffer of Trend Log 2\n"
|
||||
"in Device 123, from starting position 1 and read 10 entries,\n"
|
||||
"you could send the following command:\n"
|
||||
"%s 123 20 2 131 1 1 10\n",
|
||||
filename);
|
||||
printf("%s 123 20 2 131 2 1 10\n", filename);
|
||||
printf("%s 123 20 2 131 3 1/1/2014 00:00:01 10\n", filename);
|
||||
}
|
||||
|
||||
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;
|
||||
bool found = false;
|
||||
int argi = 0;
|
||||
int count = 0;
|
||||
int hour, min, sec, hundredths;
|
||||
int year, month, day, wday;
|
||||
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) 2014 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 (argc < 5) {
|
||||
print_usage(filename);
|
||||
return 0;
|
||||
}
|
||||
/* decode the command line parameters */
|
||||
Target_Device_Object_Instance = strtol(argv[1], NULL, 0);
|
||||
Target_Object_Type = strtol(argv[2], NULL, 0);
|
||||
Target_Object_Instance = strtol(argv[3], NULL, 0);
|
||||
Target_Object_Property = strtol(argv[4], NULL, 0);
|
||||
Target_Object_Range_Type = strtol(argv[5], NULL, 0);
|
||||
/* some bounds checking */
|
||||
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;
|
||||
}
|
||||
if (Target_Object_Range_Type == 1) {
|
||||
if (argc < 7) {
|
||||
print_usage(filename);
|
||||
return 0;
|
||||
}
|
||||
RR_Request.RequestType = RR_BY_POSITION;
|
||||
Target_Object_Index = strtol(argv[6], NULL, 0);
|
||||
Target_Object_Count = strtol(argv[7], NULL, 0);
|
||||
RR_Request.Range.RefIndex = Target_Object_Index;
|
||||
RR_Request.Count = Target_Object_Count;
|
||||
} else if (Target_Object_Range_Type == 2) {
|
||||
if (argc < 7) {
|
||||
print_usage(filename);
|
||||
return 0;
|
||||
}
|
||||
RR_Request.RequestType = RR_BY_SEQUENCE;
|
||||
Target_Object_Index = strtol(argv[6], NULL, 0);
|
||||
Target_Object_Count = strtol(argv[7], NULL, 0);
|
||||
RR_Request.Range.RefSeqNum = Target_Object_Index;
|
||||
RR_Request.Count = Target_Object_Count;
|
||||
} else if (Target_Object_Range_Type == 3) {
|
||||
if (argc < 8) {
|
||||
print_usage(filename);
|
||||
return 0;
|
||||
}
|
||||
RR_Request.RequestType = RR_BY_TIME;
|
||||
count = sscanf(argv[6], "%4d/%3d/%3d:%3d", &year, &month, &day, &wday);
|
||||
if (count == 3) {
|
||||
datetime_set_date(&RR_Request.Range.RefTime.date, (uint16_t)year,
|
||||
(uint8_t)month, (uint8_t)day);
|
||||
} else if (count == 4) {
|
||||
RR_Request.Range.RefTime.date.year = (uint16_t)year;
|
||||
RR_Request.Range.RefTime.date.month = (uint8_t)month;
|
||||
RR_Request.Range.RefTime.date.day = (uint8_t)day;
|
||||
RR_Request.Range.RefTime.date.wday = (uint8_t)wday;
|
||||
} else {
|
||||
fprintf(stderr, "Invalid date format!\r\n");
|
||||
return 1;
|
||||
}
|
||||
count =
|
||||
sscanf(argv[7], "%3d:%3d:%3d.%3d", &hour, &min, &sec, &hundredths);
|
||||
if (count == 4) {
|
||||
RR_Request.Range.RefTime.time.hour = (uint8_t)hour;
|
||||
RR_Request.Range.RefTime.time.min = (uint8_t)min;
|
||||
RR_Request.Range.RefTime.time.sec = (uint8_t)sec;
|
||||
RR_Request.Range.RefTime.time.hundredths = (uint8_t)hundredths;
|
||||
} else if (count == 3) {
|
||||
RR_Request.Range.RefTime.time.hour = (uint8_t)hour;
|
||||
RR_Request.Range.RefTime.time.min = (uint8_t)min;
|
||||
RR_Request.Range.RefTime.time.sec = (uint8_t)sec;
|
||||
RR_Request.Range.RefTime.time.hundredths = 0;
|
||||
} else if (count == 2) {
|
||||
RR_Request.Range.RefTime.time.hour = (uint8_t)hour;
|
||||
RR_Request.Range.RefTime.time.min = (uint8_t)min;
|
||||
RR_Request.Range.RefTime.time.sec = 0;
|
||||
RR_Request.Range.RefTime.time.hundredths = 0;
|
||||
} else {
|
||||
fprintf(stderr, "Invalid time format!\r\n");
|
||||
return 1;
|
||||
}
|
||||
Target_Object_Count = strtol(argv[8], NULL, 0);
|
||||
RR_Request.Count = Target_Object_Count;
|
||||
} else if (Target_Object_Range_Type == 4) {
|
||||
RR_Request.RequestType = RR_READ_ALL;
|
||||
RR_Request.Count = Target_Object_Count;
|
||||
} else {
|
||||
fprintf(stderr, "Invalid Range Type. Use 1, 2, 3, or 4.\r\n");
|
||||
return 1;
|
||||
}
|
||||
RR_Request.object_type = Target_Object_Type;
|
||||
RR_Request.object_instance = Target_Object_Instance;
|
||||
RR_Request.object_property = Target_Object_Property;
|
||||
RR_Request.array_index = BACNET_ARRAY_ALL;
|
||||
/* setup my info */
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
address_init();
|
||||
Init_Service_Handlers();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
timeout_seconds = (apdu_timeout() / 1000) * apdu_retries();
|
||||
/* try to bind with the device */
|
||||
found = address_bind_request(
|
||||
Target_Device_Object_Instance, &max_apdu, &Target_Address);
|
||||
if (!found) {
|
||||
Send_WhoIs(
|
||||
Target_Device_Object_Instance, Target_Device_Object_Instance);
|
||||
}
|
||||
/* loop forever */
|
||||
for (;;) {
|
||||
/* increment timer - exit if timed out */
|
||||
current_seconds = time(NULL);
|
||||
|
||||
/* at least one second has passed */
|
||||
if (current_seconds != last_seconds) {
|
||||
tsm_timer_milliseconds(
|
||||
(uint16_t)((current_seconds - last_seconds) * 1000));
|
||||
}
|
||||
if (Error_Detected)
|
||||
break;
|
||||
/* wait until the device is bound, or timeout and quit */
|
||||
if (!found) {
|
||||
found = address_bind_request(
|
||||
Target_Device_Object_Instance, &max_apdu, &Target_Address);
|
||||
}
|
||||
if (found) {
|
||||
if (Request_Invoke_ID == 0) {
|
||||
Request_Invoke_ID = Send_ReadRange_Request(
|
||||
Target_Device_Object_Instance, &RR_Request);
|
||||
} else if (tsm_invoke_id_free(Request_Invoke_ID)) {
|
||||
break;
|
||||
} else if (tsm_invoke_id_failed(Request_Invoke_ID)) {
|
||||
fprintf(stderr, "\rError: TSM Timeout!\n");
|
||||
tsm_free_invoke_id(Request_Invoke_ID);
|
||||
Error_Detected = true;
|
||||
/* try again or abort? */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* increment timer - exit if timed out */
|
||||
elapsed_seconds += (current_seconds - last_seconds);
|
||||
if (elapsed_seconds > timeout_seconds) {
|
||||
printf("\rError: APDU Timeout!\n");
|
||||
Error_Detected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* keep track of time for next check */
|
||||
last_seconds = current_seconds;
|
||||
}
|
||||
|
||||
if (Error_Detected)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
||||
# Visual C++ Express 2008
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "readrange", "readrange.vcproj", "{6017A2EA-1296-4E67-995F-6344A3CC27C2}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{6017A2EA-1296-4E67-995F-6344A3CC27C2}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{6017A2EA-1296-4E67-995F-6344A3CC27C2}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{6017A2EA-1296-4E67-995F-6344A3CC27C2}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{6017A2EA-1296-4E67-995F-6344A3CC27C2}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
||||
#Makefile to build BACnet Application for the Linux Port
|
||||
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
|
||||
# Executable file name
|
||||
TARGET = bacrd
|
||||
# BACnet objects that are used with this app
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/client/device-client.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c
|
||||
BACNET_BASIC_SRC += \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_apdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_noserv.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_whois.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_rd.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_whois.c
|
||||
|
||||
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = $(SRC) $(BACNET_SRC) $(BACNET_BASIC_SRC) $(BACNET_PORT_SRC)
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h> /* for time */
|
||||
#include <errno.h>
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/arf.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacport.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/whois.h"
|
||||
#include "bacnet/rd.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/sys/filename.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/dlenv.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 BACNET_ADDRESS Target_Address;
|
||||
static BACNET_REINITIALIZED_STATE Reinitialize_State = BACNET_REINIT_COLDSTART;
|
||||
static char *Reinitialize_Password = NULL;
|
||||
|
||||
static bool Error_Detected = false;
|
||||
|
||||
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;
|
||||
printf("BACnet Error: %s: %s\r\n", bactext_error_class_name(error_class),
|
||||
bactext_error_code_name(error_code));
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
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 MyReinitializeDeviceSimpleAckHandler(
|
||||
BACNET_ADDRESS *src, uint8_t invoke_id)
|
||||
{
|
||||
(void)src;
|
||||
(void)invoke_id;
|
||||
printf("ReinitializeDevice Acknowledged!\r\n");
|
||||
}
|
||||
|
||||
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);
|
||||
/* 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 ack coming back */
|
||||
apdu_set_confirmed_simple_ack_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE,
|
||||
MyReinitializeDeviceSimpleAckHandler);
|
||||
/* handle any errors coming back */
|
||||
apdu_set_error_handler(
|
||||
SERVICE_CONFIRMED_REINITIALIZE_DEVICE, MyErrorHandler);
|
||||
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 */
|
||||
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 < 3) {
|
||||
/* note: priority 16 and 0 should produce the same end results... */
|
||||
printf("Usage: %s device-instance state [password]\r\n"
|
||||
"Send BACnet ReinitializeDevice service to device.\r\n"
|
||||
"\r\n"
|
||||
"The device-instance can be 0 to %d.\r\n"
|
||||
"Possible state values:\r\n"
|
||||
" 0=coldstart\r\n"
|
||||
" 1=warmstart\r\n"
|
||||
" 2=startbackup\r\n"
|
||||
" 3=endbackup\r\n"
|
||||
" 4=startrestore\r\n"
|
||||
" 5=endrestore\r\n"
|
||||
" 6=abortrestore\r\n"
|
||||
"The optional password is a character string of 1 to 20 "
|
||||
"characters.\r\n",
|
||||
filename_remove_path(argv[0]), BACNET_MAX_INSTANCE - 1);
|
||||
return 0;
|
||||
}
|
||||
/* decode the command line parameters */
|
||||
Target_Device_Object_Instance = strtol(argv[1], NULL, 0);
|
||||
Reinitialize_State = strtol(argv[2], NULL, 0);
|
||||
/* optional password */
|
||||
if (argc > 3) {
|
||||
Reinitialize_Password = argv[3];
|
||||
}
|
||||
|
||||
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();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
timeout_seconds = (apdu_timeout() / 1000) * apdu_retries();
|
||||
/* try to bind with the device */
|
||||
found = address_bind_request(
|
||||
Target_Device_Object_Instance, &max_apdu, &Target_Address);
|
||||
if (!found) {
|
||||
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));
|
||||
}
|
||||
if (Error_Detected)
|
||||
break;
|
||||
/* wait until the device is bound, or timeout and quit */
|
||||
if (!found) {
|
||||
found = address_bind_request(
|
||||
Target_Device_Object_Instance, &max_apdu, &Target_Address);
|
||||
}
|
||||
if (found) {
|
||||
if (invoke_id == 0) {
|
||||
invoke_id = Send_Reinitialize_Device_Request(
|
||||
Target_Device_Object_Instance, Reinitialize_State,
|
||||
Reinitialize_Password);
|
||||
} else if (tsm_invoke_id_free(invoke_id)) {
|
||||
break;
|
||||
} else if (tsm_invoke_id_failed(invoke_id)) {
|
||||
fprintf(stderr, "\rError: TSM Timeout!\r\n");
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
/* try again or abort? */
|
||||
Error_Detected = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* increment timer - exit if timed out */
|
||||
elapsed_seconds += (current_seconds - last_seconds);
|
||||
if (elapsed_seconds > timeout_seconds) {
|
||||
fprintf(stderr, "\rError: APDU Timeout!\r\n");
|
||||
Error_Detected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* keep track of time for next check */
|
||||
last_seconds = current_seconds;
|
||||
}
|
||||
|
||||
if (Error_Detected)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
#Makefile to build BACnet Application for the GCC port
|
||||
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
|
||||
# Executable file name
|
||||
TARGET = bacroute
|
||||
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
# BACNET_PORT, BACNET_PORT_DIR, BACNET_PORT_SRC are defined in common Makefile
|
||||
# BACNET_SRC_DIR is defined in common apps Makefile
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c \
|
||||
$(BACNET_OBJECT_DIR)/client/device-client.c
|
||||
|
||||
PORT_BIP6_SRC = \
|
||||
$(BACNET_PORT_DIR)/bip6.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/datalink/bvlc6.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/bbmd6/h_bbmd6.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/bbmd6/vmac.c
|
||||
|
||||
PORT_BIP_SRC = \
|
||||
$(BACNET_PORT_DIR)/bip-init.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/datalink/bvlc.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/datalink/bip.c
|
||||
|
||||
# WARNINGS, DEBUGGING, OPTIMIZATION are defined in common apps Makefile
|
||||
# BACNET_DEFINES is defined in common apps Makefile
|
||||
# put all the flags together
|
||||
INCLUDES = -I$(BACNET_SRC_DIR) -I$(BACNET_PORT_DIR)
|
||||
CFLAGS += $(WARNINGS) $(DEBUGGING) $(OPTIMIZATION) $(BACNET_DEFINES) $(INCLUDES)
|
||||
LFLAGS += -Wl,$(SYSTEM_LIB)
|
||||
ifneq (${BACNET_LIB},)
|
||||
LFLAGS += -Wl,$(BACNET_LIB)
|
||||
endif
|
||||
# GCC dead code removal
|
||||
CFLAGS += -ffunction-sections -fdata-sections
|
||||
LFLAGS += -Wl,--gc-sections
|
||||
|
||||
BACNET_SRC ?= \
|
||||
$(wildcard $(BACNET_SRC_DIR)/bacnet/*.c) \
|
||||
$(wildcard $(BACNET_SRC_DIR)/bacnet/basic/*.c) \
|
||||
$(wildcard $(BACNET_SRC_DIR)/bacnet/basic/binding/*.c) \
|
||||
$(wildcard $(BACNET_SRC_DIR)/bacnet/basic/service/*.c) \
|
||||
$(wildcard $(BACNET_SRC_DIR)/bacnet/basic/sys/*.c) \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/npdu/h_routed_npdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/npdu/s_router.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/tsm/tsm.c
|
||||
|
||||
SRCS = ${SRC} $(BACNET_SRC) ${PORT_BIP6_SRC} ${PORT_BIP_SRC}
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
BACnet Simple Router Demo
|
||||
=========================
|
||||
|
||||
The Simple Router demo connects one BACnet/IP and one BACnet/IPv6 network.
|
||||
It also includes a BBMD so that Foreign Device Registration can be used
|
||||
to tunnel local command line demos to BACnet/IP and BACnet IPv6 networks.
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
It uses environment variables to configure
|
||||
the BACnet/IP port and BACnet/IPv6 address on Linux:
|
||||
|
||||
export BACNET_IFACE=eth0
|
||||
export BACNET_BIP6_IFACE=eth0
|
||||
|
||||
Also uses these configurations, but defaults to these values if not set:
|
||||
export BACNET_IP_PORT=47808
|
||||
export BACNET_BIP6_PORT=47808
|
||||
export BACNET_BIP6_BROADCAST=FF05
|
||||
export BACNET_IP_NET=1
|
||||
export BACNET_IP6_NET=2
|
||||
@@ -0,0 +1,63 @@
|
||||
#Makefile to build BACnet Application for the Linux Port
|
||||
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
|
||||
# Executable file name
|
||||
TARGET = router
|
||||
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
ifeq (${BACNET_PORT},linux)
|
||||
#PFLAGS =
|
||||
# -pthread
|
||||
TARGET_EXT =
|
||||
LIBS = -lpthread -lconfig
|
||||
LFLAGS += $(LIBS)
|
||||
endif
|
||||
|
||||
#DEFINES = $(BACNET_DEFINES) -DBACDL_MSTP -DBACDL_BIP
|
||||
BACNET_SOURCE_DIR = ../../src
|
||||
|
||||
SRCS = main.c \
|
||||
${BACNET_PORT_DIR}/rs485.c \
|
||||
${BACNET_PORT_DIR}/timer.c \
|
||||
${BACNET_PORT_DIR}/bip-init.c \
|
||||
${BACNET_PORT_DIR}/dlmstp_linux.c \
|
||||
${BACNET_SOURCE_DIR}/bip.c \
|
||||
${BACNET_SOURCE_DIR}/bvlc.c \
|
||||
${BACNET_SOURCE_DIR}/fifo.c \
|
||||
${BACNET_SOURCE_DIR}/mstp.c \
|
||||
${BACNET_SOURCE_DIR}/mstptext.c \
|
||||
${BACNET_SOURCE_DIR}/debug.c \
|
||||
${BACNET_SOURCE_DIR}/indtext.c \
|
||||
${BACNET_SOURCE_DIR}/ringbuf.c \
|
||||
${BACNET_SOURCE_DIR}/crc.c \
|
||||
mstpmodule.c \
|
||||
ipmodule.c \
|
||||
portthread.c \
|
||||
msgqueue.c \
|
||||
network_layer.c
|
||||
|
||||
|
||||
OBJS = ${SRCS:.c=.o}
|
||||
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS} Makefile
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
include: .depend
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
configuration file that stores values for router ports initialization
|
||||
|
||||
Common arguments:
|
||||
device_type - "bip" or "mstp" (with quotes)
|
||||
device - Connection device, for example "eth0" or "/dev/ttyS0"
|
||||
network - Network number [1..65534]. Do not use network number 65535, it is broadcast number
|
||||
|
||||
bip arguments:
|
||||
port - bip UDP port, default 47808
|
||||
|
||||
mstp arguments:
|
||||
mac - MSTP MAC
|
||||
max_master - MSTP max master
|
||||
max_frames - 1
|
||||
baud - one from the list: 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400
|
||||
parity - one from the list (with quotes): "None", "Even", "Odd"
|
||||
databits - one from the list: 5, 6, 7, 8
|
||||
stopbits - 1 or 2
|
||||
|
||||
|
||||
Example:
|
||||
ports =
|
||||
(
|
||||
{
|
||||
device_type = "bip";
|
||||
device = "eth0";
|
||||
port = 47808;
|
||||
network = 1;
|
||||
},
|
||||
|
||||
{
|
||||
device_type = "mstp";
|
||||
device = "/dev/ttyS0";
|
||||
mac = 1;
|
||||
max_master = 127;
|
||||
max_frames = 1;
|
||||
baud = 38400;
|
||||
parity = "None";
|
||||
databits = 8;
|
||||
stopbits = 1;
|
||||
network = 2;
|
||||
}
|
||||
);
|
||||
*/
|
||||
|
||||
ports =
|
||||
(
|
||||
{
|
||||
device_type = "bip";
|
||||
device = "eth0";
|
||||
port = 47808;
|
||||
network = 1;
|
||||
},
|
||||
|
||||
{
|
||||
device_type = "mstp";
|
||||
device = "/dev/ttyS0";
|
||||
mac = 2;
|
||||
max_master = 127;
|
||||
max_frames = 1;
|
||||
baud = 38400;
|
||||
parity = "None";
|
||||
databits = 8;
|
||||
stopbits = 1;
|
||||
network = 2;
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,392 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
|
||||
* @date 2012
|
||||
* @brief Datalink IP module
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "ipmodule.h"
|
||||
#include "bacnet/bacint.h"
|
||||
|
||||
#ifdef TEST_PACKET
|
||||
uint8_t test_packet[] = { 0x81, 0x0a, 0x00, 0x16, /* BVLC header */
|
||||
0x01, 0x24, 0x00, 0x01, 0x01, 0x0b, 0xff, /* NPDU */
|
||||
0x00, 0x03, 0x01, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x19,
|
||||
0x55 }; /* APDU */
|
||||
#endif
|
||||
|
||||
extern int get_local_address_ioctl(
|
||||
char *ifname, struct in_addr *addr, int request);
|
||||
|
||||
void *dl_ip_thread(void *pArgs)
|
||||
{
|
||||
MSGBOX_ID msgboxid;
|
||||
BACMSG msg_storage, *bacmsg = NULL;
|
||||
MSG_DATA *msg_data;
|
||||
ROUTER_PORT *port = (ROUTER_PORT *)pArgs;
|
||||
IP_DATA ip_data; /* port specific parameters */
|
||||
BACNET_ADDRESS address = { 0 };
|
||||
int status;
|
||||
uint8_t shutdown = 0;
|
||||
|
||||
/* initialize router port */
|
||||
if (!dl_ip_init(port, &ip_data)) {
|
||||
port->state = INIT_FAILED;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* allocate buffer */
|
||||
ip_data.max_buff = MAX_BIP_MPDU;
|
||||
ip_data.buff = (uint8_t *)malloc(ip_data.max_buff);
|
||||
|
||||
if (ip_data.buff == NULL) {
|
||||
port->state = INIT_FAILED;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
msgboxid = create_msgbox();
|
||||
if (msgboxid == INVALID_MSGBOX_ID) {
|
||||
PRINT(ERROR, "Error: Failed to create message box");
|
||||
port->state = INIT_FAILED;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
port->port_id = msgboxid;
|
||||
port->state = RUNNING;
|
||||
|
||||
while (!shutdown) {
|
||||
/* check for incoming messages */
|
||||
bacmsg = recv_from_msgbox(port->port_id, &msg_storage);
|
||||
|
||||
if (bacmsg) {
|
||||
switch (bacmsg->type) {
|
||||
case DATA: {
|
||||
msg_data = (MSG_DATA *)bacmsg->data;
|
||||
memmove(&address.net, &msg_data->dest.net, 2);
|
||||
memmove(&address.mac_len, &msg_data->dest.len, 1);
|
||||
memmove(
|
||||
&address.mac[0], &msg_data->dest.adr[0], MAX_MAC_LEN);
|
||||
|
||||
dl_ip_send(
|
||||
&ip_data, &address, msg_data->pdu, msg_data->pdu_len);
|
||||
|
||||
check_data(msg_data);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SERVICE: {
|
||||
switch (bacmsg->subtype) {
|
||||
case SHUTDOWN:
|
||||
del_msgbox(port->port_id);
|
||||
shutdown = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
status = dl_ip_recv(&ip_data, &msg_data, &address, 5);
|
||||
if (status > 0) {
|
||||
memmove(&msg_data->src.len, &address.mac_len, 1);
|
||||
memmove(&msg_data->src.adr[0], &address.mac[0], MAX_MAC_LEN);
|
||||
msg_storage.origin = port->port_id;
|
||||
msg_storage.type = DATA;
|
||||
msg_storage.data = msg_data;
|
||||
|
||||
if (!send_to_msgbox(port->main_id, &msg_storage)) {
|
||||
free_data(msg_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* cleanup procedure */
|
||||
dl_ip_cleanup(&ip_data);
|
||||
port->state = FINISHED;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool dl_ip_init(ROUTER_PORT *port, IP_DATA *ip_data)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
int socket_opt = 0;
|
||||
int status = 0; /* for error checking */
|
||||
|
||||
/* setup port for later use */
|
||||
ip_data->port = htons(port->params.bip_params.port);
|
||||
|
||||
/* get local address */
|
||||
status =
|
||||
get_local_address_ioctl(port->iface, &ip_data->local_addr, SIOCGIFADDR);
|
||||
if (status < 0) {
|
||||
return false;
|
||||
}
|
||||
/* get broadcast address */
|
||||
status = get_local_address_ioctl(
|
||||
port->iface, &ip_data->broadcast_addr, SIOCGIFBRDADDR);
|
||||
if (status < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ip_data->socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (ip_data->socket < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* setup socket options */
|
||||
|
||||
socket_opt = 1;
|
||||
status = setsockopt(ip_data->socket, SOL_SOCKET, SO_REUSEADDR, &socket_opt,
|
||||
sizeof(socket_opt));
|
||||
if (status < 0) {
|
||||
close(ip_data->socket);
|
||||
return false;
|
||||
}
|
||||
|
||||
status = setsockopt(ip_data->socket, SOL_SOCKET, SO_BROADCAST, &socket_opt,
|
||||
sizeof(socket_opt));
|
||||
if (status < 0) {
|
||||
close(ip_data->socket);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* bind the socket to the local port number */
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
sin.sin_port = ip_data->port;
|
||||
|
||||
memset(&sin.sin_zero, '\0', sizeof(sin.sin_zero));
|
||||
|
||||
status = bind(ip_data->socket, (const struct sockaddr *)&sin,
|
||||
sizeof(struct sockaddr));
|
||||
if (status < 0) {
|
||||
close(ip_data->socket);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* add BIP address to router port structure */
|
||||
memcpy(&port->route_info.mac[0], &ip_data->local_addr.s_addr, 4);
|
||||
memcpy(&port->route_info.mac[4], &port->params.bip_params.port, 2);
|
||||
port->route_info.mac_len = 6;
|
||||
|
||||
PRINT(INFO, "Interface: %s\n", port->iface);
|
||||
PRINT(INFO, "IP Address: %s\n", inet_ntoa(ip_data->local_addr));
|
||||
PRINT(
|
||||
INFO, "IP Broadcast Address: %s\n", inet_ntoa(ip_data->broadcast_addr));
|
||||
PRINT(INFO, "UDP Port: 0x%04X [%hu]\n", (port->params.bip_params.port),
|
||||
(port->params.bip_params.port));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int dl_ip_send(
|
||||
IP_DATA *data, BACNET_ADDRESS *dest, uint8_t *pdu, unsigned pdu_len)
|
||||
{
|
||||
struct sockaddr_in bip_dest = { 0 };
|
||||
int buff_len = 0;
|
||||
int bytes_sent = 0;
|
||||
|
||||
if (data->socket < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
data->buff[0] = BVLL_TYPE_BACNET_IP;
|
||||
bip_dest.sin_family = AF_INET;
|
||||
if (dest->net == BACNET_BROADCAST_NETWORK) {
|
||||
/* broadcast */
|
||||
bip_dest.sin_addr.s_addr = data->broadcast_addr.s_addr;
|
||||
bip_dest.sin_port = data->port;
|
||||
data->buff[1] = BVLC_ORIGINAL_BROADCAST_NPDU;
|
||||
} else if (dest->mac_len == 6) {
|
||||
memcpy(&bip_dest.sin_addr.s_addr, &dest->mac[0], 4);
|
||||
memcpy(&bip_dest.sin_port, &dest->mac[4], 2);
|
||||
data->buff[1] = BVLC_ORIGINAL_UNICAST_NPDU;
|
||||
} else {
|
||||
/* invalid address */
|
||||
return -1;
|
||||
}
|
||||
|
||||
buff_len = 2;
|
||||
buff_len += encode_unsigned16(
|
||||
&data->buff[buff_len], (uint16_t)(pdu_len + 4 /*inclusive */));
|
||||
memcpy(&data->buff[buff_len], pdu, pdu_len);
|
||||
buff_len += pdu_len;
|
||||
|
||||
/* send the packet */
|
||||
bytes_sent = sendto(data->socket, (char *)data->buff, buff_len, 0,
|
||||
(struct sockaddr *)&bip_dest, sizeof(struct sockaddr));
|
||||
|
||||
PRINT(DEBUG, "send to %s\n", inet_ntoa(bip_dest.sin_addr));
|
||||
|
||||
return bytes_sent;
|
||||
}
|
||||
|
||||
int dl_ip_recv(
|
||||
IP_DATA *data, MSG_DATA **msg_data, BACNET_ADDRESS *src, unsigned timeout)
|
||||
{
|
||||
int received_bytes = 0;
|
||||
uint16_t buff_len = 0; /* return value */
|
||||
fd_set read_fds;
|
||||
struct timeval select_timeout;
|
||||
struct sockaddr_in sin = { 0 };
|
||||
socklen_t sin_len = sizeof(sin);
|
||||
|
||||
/* make sure the socket is open */
|
||||
if (data->socket < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (timeout >= 1000) {
|
||||
select_timeout.tv_sec = timeout / 1000;
|
||||
select_timeout.tv_usec =
|
||||
1000 * (timeout - select_timeout.tv_sec * 1000);
|
||||
} else {
|
||||
select_timeout.tv_sec = 0;
|
||||
select_timeout.tv_usec = 1000 * timeout;
|
||||
}
|
||||
|
||||
FD_ZERO(&read_fds);
|
||||
FD_SET(data->socket, &read_fds);
|
||||
|
||||
#ifdef TEST_PACKET
|
||||
received_bytes = sizeof(test_packet);
|
||||
memmove(data->buff, &test_packet, received_bytes);
|
||||
sin.sin_addr.s_addr = 0x7E1D40A;
|
||||
sin.sin_port = 0xC0BA;
|
||||
#else
|
||||
int ret = select(data->socket + 1, &read_fds, NULL, NULL, &select_timeout);
|
||||
/* see if there is a packet for us */
|
||||
if (ret > 0) {
|
||||
received_bytes = recvfrom(data->socket, (char *)&data->buff[0],
|
||||
data->max_buff, 0, (struct sockaddr *)&sin, &sin_len);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
PRINT(DEBUG, "received from %s\n", inet_ntoa(sin.sin_addr));
|
||||
|
||||
/* check for errors */
|
||||
if (received_bytes <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* the signature of a BACnet/IP packet */
|
||||
if (data->buff[0] != BVLL_TYPE_BACNET_IP)
|
||||
return 0;
|
||||
|
||||
switch (data->buff[1]) {
|
||||
case BVLC_ORIGINAL_UNICAST_NPDU:
|
||||
case BVLC_ORIGINAL_BROADCAST_NPDU: {
|
||||
if ((sin.sin_addr.s_addr == data->local_addr.s_addr) &&
|
||||
(sin.sin_port == data->port)) {
|
||||
buff_len = 0;
|
||||
|
||||
PRINT(DEBUG, "BIP: src is me. Discarded!\n");
|
||||
|
||||
} else {
|
||||
src->mac_len = 6;
|
||||
memcpy(&src->mac[0], &sin.sin_addr.s_addr, 4);
|
||||
memcpy(&src->mac[4], &sin.sin_port, 2);
|
||||
|
||||
(void)decode_unsigned16(&data->buff[2], &buff_len);
|
||||
/* subtract off the BVLC header */
|
||||
buff_len -= 4;
|
||||
if (buff_len < data->max_buff) {
|
||||
/* allocate data message stucture */
|
||||
(*msg_data) = (MSG_DATA *)malloc(sizeof(MSG_DATA));
|
||||
(*msg_data)->pdu_len = buff_len;
|
||||
(*msg_data)->pdu = (uint8_t *)malloc((*msg_data)->pdu_len);
|
||||
/* fill up data message structure */
|
||||
memmove(&(*msg_data)->pdu[0], &data->buff[4],
|
||||
(*msg_data)->pdu_len);
|
||||
memmove(&(*msg_data)->src, src, sizeof(BACNET_ADDRESS));
|
||||
}
|
||||
/* ignore packets that are too large */
|
||||
else {
|
||||
buff_len = 0;
|
||||
|
||||
PRINT(ERROR, "BIP: PDU too large. Discarded!.\n");
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case BVLC_FORWARDED_NPDU: {
|
||||
memcpy(&sin.sin_addr.s_addr, &data->buff[4], 4);
|
||||
memcpy(&sin.sin_port, &data->buff[8], 2);
|
||||
if ((sin.sin_addr.s_addr == data->local_addr.s_addr) &&
|
||||
(sin.sin_port == data->port)) {
|
||||
buff_len = 0;
|
||||
} else {
|
||||
src->mac_len = 6;
|
||||
memcpy(&src->mac[0], &sin.sin_addr.s_addr, 4);
|
||||
memcpy(&src->mac[4], &sin.sin_port, 2);
|
||||
|
||||
(void)decode_unsigned16(&data->buff[2], &buff_len);
|
||||
/* subtract off the BVLC header */
|
||||
buff_len -= 10;
|
||||
if (buff_len < data->max_buff) {
|
||||
/* allocate data message stucture */
|
||||
(*msg_data) = (MSG_DATA *)malloc(sizeof(MSG_DATA));
|
||||
(*msg_data)->pdu_len = buff_len;
|
||||
(*msg_data)->pdu = (uint8_t *)malloc((*msg_data)->pdu_len);
|
||||
/* fill up data message structure */
|
||||
memmove(&(*msg_data)->pdu[0], &data->buff[4 + 6],
|
||||
(*msg_data)->pdu_len);
|
||||
memmove(&(*msg_data)->src, src, sizeof(BACNET_ADDRESS));
|
||||
} else {
|
||||
/* ignore packets that are too large */
|
||||
buff_len = 0;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
|
||||
PRINT(ERROR, "BIP: BVLC discarded!\n");
|
||||
|
||||
break;
|
||||
}
|
||||
return buff_len;
|
||||
}
|
||||
|
||||
void dl_ip_cleanup(IP_DATA *ip_data)
|
||||
{
|
||||
/* free buffer */
|
||||
if (ip_data->buff) {
|
||||
free(ip_data->buff);
|
||||
}
|
||||
/* close socket */
|
||||
if (ip_data->socket > 0) {
|
||||
close(ip_data->socket);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
|
||||
* @date 2012
|
||||
* @brief Datalink IP module
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#ifndef UDPMODULE_H
|
||||
#define UDPMODULE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "portthread.h"
|
||||
#include "bacnet/datalink/bip.h"
|
||||
|
||||
#define MAX_BIP_APDU 1476
|
||||
#define MAX_BIP_PDU (MAX_NPDU + MAX_BIP_APDU)
|
||||
#define MAX_BIP_MPDU (MAX_HEADER + MAX_BIP_PDU)
|
||||
/* Yes, we know this is longer than an Ethernet Frame,
|
||||
a UDP payload and an IPv6 packet.
|
||||
Grandfathered in from BACnet Ethernet days,
|
||||
and we can rely on the lower layers of the
|
||||
Ethernet stack to fragment/reassemble the BACnet MPDUs */
|
||||
|
||||
typedef struct ip_data {
|
||||
int socket;
|
||||
uint16_t port;
|
||||
struct in_addr local_addr;
|
||||
struct in_addr broadcast_addr;
|
||||
uint8_t *buff;
|
||||
uint16_t max_buff;
|
||||
} IP_DATA;
|
||||
|
||||
|
||||
void *dl_ip_thread(
|
||||
void *pArgs);
|
||||
|
||||
bool dl_ip_init(
|
||||
ROUTER_PORT * port,
|
||||
IP_DATA * data);
|
||||
|
||||
int dl_ip_send(
|
||||
IP_DATA * data,
|
||||
BACNET_ADDRESS * dest,
|
||||
uint8_t * pdu,
|
||||
unsigned pdu_len);
|
||||
|
||||
int dl_ip_recv(
|
||||
IP_DATA * data,
|
||||
MSG_DATA ** msg, /* on recieve fill up message */
|
||||
BACNET_ADDRESS * src,
|
||||
unsigned timeout);
|
||||
|
||||
void dl_ip_cleanup(
|
||||
IP_DATA * data);
|
||||
|
||||
#endif /* end of UDPMODULE_H */
|
||||
@@ -0,0 +1,857 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
|
||||
* @date 2012
|
||||
* @brief BACnet/IP to MS/TP Router example application.
|
||||
* The Router connects two or more BACnet/IP and BACnet MS/TP networks.
|
||||
* Number of netwoks is limited only by available hardware communication
|
||||
* devices (or ports for Ethernet).
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h> /* for time */
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <libconfig.h> /* read config files */
|
||||
#include <unistd.h> /* for getopt */
|
||||
#include <termios.h> /* used in kbhit() */
|
||||
#include <getopt.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <pthread.h>
|
||||
#include <termios.h>
|
||||
#include "msgqueue.h"
|
||||
#include "portthread.h"
|
||||
#include "network_layer.h"
|
||||
#include "ipmodule.h"
|
||||
#include "mstpmodule.h"
|
||||
|
||||
#define KEY_ESC 27
|
||||
|
||||
ROUTER_PORT *head = NULL; /* pointer to list of router ports */
|
||||
|
||||
int port_count;
|
||||
|
||||
void print_help();
|
||||
|
||||
bool read_config(char *filepath);
|
||||
|
||||
bool parse_cmd(int argc, char *argv[]);
|
||||
|
||||
void init_port_threads(ROUTER_PORT *port_list);
|
||||
|
||||
bool init_router();
|
||||
|
||||
void cleanup();
|
||||
|
||||
void print_msg(BACMSG *msg);
|
||||
|
||||
uint16_t process_msg(BACMSG *msg, MSG_DATA *data, uint8_t **buff);
|
||||
|
||||
uint16_t get_next_free_dnet();
|
||||
|
||||
int kbhit();
|
||||
|
||||
inline bool is_network_msg(BACMSG *msg);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("I am router\n");
|
||||
|
||||
ROUTER_PORT *port;
|
||||
BACMSG msg_storage, *bacmsg = NULL;
|
||||
MSG_DATA *msg_data = NULL;
|
||||
uint8_t *buff = NULL;
|
||||
int16_t buff_len = 0;
|
||||
|
||||
atexit(cleanup);
|
||||
|
||||
if (!parse_cmd(argc, argv)) {
|
||||
printf("parse cmd failed\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!init_router()) {
|
||||
printf("init_router failed\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
send_network_message(
|
||||
NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, msg_data, &buff, NULL);
|
||||
|
||||
while (true) {
|
||||
if (kbhit()) {
|
||||
char ch = getchar();
|
||||
if (ch == KEY_ESC) {
|
||||
PRINT(INFO, "Received shutdown. Exiting...\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bacmsg = recv_from_msgbox(head->main_id, &msg_storage);
|
||||
if (bacmsg) {
|
||||
switch (bacmsg->type) {
|
||||
case DATA: {
|
||||
MSGBOX_ID msg_src = bacmsg->origin;
|
||||
|
||||
/* allocate message structure */
|
||||
msg_data = malloc(sizeof(MSG_DATA));
|
||||
if (!msg_data) {
|
||||
PRINT(ERROR, "Error: Could not allocate memory\n");
|
||||
break;
|
||||
}
|
||||
|
||||
print_msg(bacmsg);
|
||||
|
||||
if (is_network_msg(bacmsg)) {
|
||||
buff_len =
|
||||
process_network_message(bacmsg, msg_data, &buff);
|
||||
if (buff_len == 0) {
|
||||
free_data(bacmsg->data);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
buff_len = process_msg(bacmsg, msg_data, &buff);
|
||||
}
|
||||
|
||||
/* if buff_len */
|
||||
/* >0 - form new message and send */
|
||||
/* =-1 - try to find next router */
|
||||
/* other value - discard message */
|
||||
|
||||
if (buff_len > 0) {
|
||||
/* form new message */
|
||||
msg_data->pdu = buff;
|
||||
msg_data->pdu_len = buff_len;
|
||||
msg_storage.origin = head->main_id;
|
||||
msg_storage.type = DATA;
|
||||
msg_storage.data = msg_data;
|
||||
|
||||
print_msg(bacmsg);
|
||||
|
||||
if (is_network_msg(bacmsg)) {
|
||||
msg_data->ref_count = 1;
|
||||
send_to_msgbox(msg_src, &msg_storage);
|
||||
} else if (msg_data->dest.net !=
|
||||
BACNET_BROADCAST_NETWORK) {
|
||||
msg_data->ref_count = 1;
|
||||
port =
|
||||
find_dnet(msg_data->dest.net, &msg_data->dest);
|
||||
send_to_msgbox(port->port_id, &msg_storage);
|
||||
} else {
|
||||
port = head;
|
||||
msg_data->ref_count = port_count - 1;
|
||||
while (port != NULL) {
|
||||
if (port->port_id == msg_src ||
|
||||
port->state == FINISHED) {
|
||||
port = port->next;
|
||||
continue;
|
||||
}
|
||||
send_to_msgbox(port->port_id, &msg_storage);
|
||||
port = port->next;
|
||||
}
|
||||
}
|
||||
} else if (buff_len == -1) {
|
||||
uint16_t net = msg_data->dest.net; /* NET to find */
|
||||
PRINT(INFO, "Searching NET...\n");
|
||||
send_network_message(
|
||||
NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK, msg_data,
|
||||
&buff, &net);
|
||||
} else {
|
||||
/* if invalid message send Reject-Message-To-Network */
|
||||
PRINT(ERROR, "Error: Invalid message\n");
|
||||
free_data(msg_data);
|
||||
}
|
||||
} break;
|
||||
case SERVICE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void print_help()
|
||||
{
|
||||
printf(
|
||||
"Usage: router <init_method> [init_parameters]\n"
|
||||
"\ninit_method:\n"
|
||||
"-c, --config <filepath>\n\tinitialize router with a configuration "
|
||||
"file (.cfg) located at <filepath>\n"
|
||||
"-D, --device <dev_type> <iface> [params]\n\tinitialize a <dev_type> "
|
||||
"device using an <iface> interface specified with\n\t[params]\n"
|
||||
"\ninit_parameters:\n"
|
||||
"-n, --network <net>\n\tspecify device network number\n"
|
||||
"-P, --port <port>\n\tspecify udp port for BIP device\n"
|
||||
"-m, --mac <mac_address> [max_master] [max_frames]\n\tspecify MSTP "
|
||||
"port parameters\n"
|
||||
"-b, --baud <baud>\n\tspecify MSTP port baud rate\n"
|
||||
"-p, --parity <None|Even|Odd>\n\tspecify MSTP port parity\n"
|
||||
"-d, --databits <5|6|7|8>\n\tspecify MSTP port databits\n"
|
||||
"-s, --stopbits <1|2>\n\tspecify MSTP port stopbits\n");
|
||||
}
|
||||
|
||||
bool read_config(char *filepath)
|
||||
{
|
||||
config_t cfg;
|
||||
config_setting_t *setting;
|
||||
ROUTER_PORT *current = head;
|
||||
int result, fd;
|
||||
|
||||
config_init(&cfg);
|
||||
|
||||
/* open configuration file */
|
||||
if (!config_read_file(&cfg, filepath)) {
|
||||
PRINT(ERROR, "Config file error: %d - %s\n", config_error_line(&cfg),
|
||||
config_error_text(&cfg));
|
||||
config_destroy(&cfg);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* get router "port" count */
|
||||
setting = config_lookup(&cfg, "ports");
|
||||
if (setting != NULL) {
|
||||
int count = config_setting_length(setting);
|
||||
int i;
|
||||
|
||||
/* lookup and initialize router "port" parameters */
|
||||
for (i = 0; i < count; i++) {
|
||||
const char *dev_type;
|
||||
const char *iface;
|
||||
long int param;
|
||||
const char *str_param;
|
||||
config_setting_t *port = config_setting_get_elem(setting, i);
|
||||
|
||||
/* create new list node to store port information */
|
||||
if (head == NULL) {
|
||||
head = (ROUTER_PORT *)malloc(sizeof(ROUTER_PORT));
|
||||
head->next = NULL;
|
||||
current = head;
|
||||
} else {
|
||||
ROUTER_PORT *tmp = current;
|
||||
current = current->next;
|
||||
current = (ROUTER_PORT *)malloc(sizeof(ROUTER_PORT));
|
||||
current->next = NULL;
|
||||
tmp->next = current;
|
||||
}
|
||||
|
||||
port_count++;
|
||||
config_setting_lookup_string(port, "device_type", &dev_type);
|
||||
printf("dev_type = %s\r\n", dev_type);
|
||||
if (strcmp(dev_type, "bip") == 0) {
|
||||
current->type = BIP;
|
||||
|
||||
result = config_setting_lookup_string(port, "device", &iface);
|
||||
if (result) {
|
||||
current->iface =
|
||||
(char *)malloc((strlen(iface) + 1) * sizeof(char));
|
||||
strcpy(current->iface, iface);
|
||||
|
||||
/* check if interface is valid */
|
||||
fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd) {
|
||||
struct ifreq ifr;
|
||||
strncpy(ifr.ifr_name, current->iface,
|
||||
sizeof(ifr.ifr_name) - 1);
|
||||
result = ioctl(fd, SIOCGIFADDR, &ifr);
|
||||
if (result != -1) {
|
||||
close(fd);
|
||||
} else {
|
||||
PRINT(ERROR,
|
||||
"Error: Invalid interface for BIP device\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
current->iface = "eth0";
|
||||
}
|
||||
|
||||
result = config_setting_lookup_int(port, "port", (int *)¶m);
|
||||
if (result) {
|
||||
current->params.bip_params.port = param;
|
||||
} else {
|
||||
current->params.bip_params.port = 0xBAC0;
|
||||
}
|
||||
result =
|
||||
config_setting_lookup_int(port, "network", (int *)¶m);
|
||||
if (result) {
|
||||
current->route_info.net = param;
|
||||
} else {
|
||||
current->route_info.net = get_next_free_dnet();
|
||||
}
|
||||
|
||||
} else if (strcmp(dev_type, "mstp") == 0) {
|
||||
current->type = MSTP;
|
||||
|
||||
result = config_setting_lookup_string(port, "device", &iface);
|
||||
if (result) {
|
||||
current->iface =
|
||||
(char *)malloc((strlen(iface) + 1) * sizeof(char));
|
||||
strcpy(current->iface, iface);
|
||||
|
||||
/* check if interface is valid */
|
||||
fd = open(current->iface, O_NOCTTY | O_NONBLOCK);
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
} else {
|
||||
PRINT(ERROR,
|
||||
"Error: Invalid interface for MSTP device\n");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
current->iface = "/dev/ttyS0";
|
||||
}
|
||||
result = config_setting_lookup_int(port, "mac", (int *)¶m);
|
||||
if (result) {
|
||||
current->route_info.mac[0] = param;
|
||||
current->route_info.mac_len = 1;
|
||||
} else {
|
||||
current->route_info.mac[0] = 127;
|
||||
current->route_info.mac_len = 1;
|
||||
}
|
||||
result = config_setting_lookup_int(
|
||||
port, "max_master", (int *)¶m);
|
||||
if (result) {
|
||||
current->params.mstp_params.max_master = param;
|
||||
} else {
|
||||
current->params.mstp_params.max_master = 127;
|
||||
}
|
||||
result = config_setting_lookup_int(
|
||||
port, "max_frames", (int *)¶m);
|
||||
if (result) {
|
||||
current->params.mstp_params.max_frames = param;
|
||||
} else {
|
||||
current->params.mstp_params.max_frames = 1;
|
||||
}
|
||||
result = config_setting_lookup_int(port, "baud", (int *)¶m);
|
||||
if (result) {
|
||||
current->params.mstp_params.baudrate = param;
|
||||
} else {
|
||||
current->params.mstp_params.baudrate = 9600;
|
||||
}
|
||||
result =
|
||||
config_setting_lookup_string(port, "parity", &str_param);
|
||||
if (result) {
|
||||
switch (str_param[0]) {
|
||||
case 'E':
|
||||
current->params.mstp_params.parity = PARITY_EVEN;
|
||||
break;
|
||||
case 'O':
|
||||
current->params.mstp_params.parity = PARITY_ODD;
|
||||
break;
|
||||
default:
|
||||
current->params.mstp_params.parity = PARITY_NONE;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
current->params.mstp_params.parity = PARITY_NONE;
|
||||
}
|
||||
result =
|
||||
config_setting_lookup_int(port, "databits", (int *)¶m);
|
||||
if (result && param >= 5 && param <= 8) {
|
||||
current->params.mstp_params.databits = param;
|
||||
} else {
|
||||
current->params.mstp_params.databits = 8;
|
||||
}
|
||||
result =
|
||||
config_setting_lookup_int(port, "stopbits", (int *)¶m);
|
||||
if (result && param >= 1 && param <= 2) {
|
||||
current->params.mstp_params.stopbits = param;
|
||||
} else {
|
||||
current->params.mstp_params.stopbits = 1;
|
||||
}
|
||||
result =
|
||||
config_setting_lookup_int(port, "network", (int *)¶m);
|
||||
if (result) {
|
||||
current->route_info.net = param;
|
||||
} else {
|
||||
current->route_info.net = get_next_free_dnet();
|
||||
}
|
||||
|
||||
} else {
|
||||
PRINT(ERROR, "Error: %s unsuported\n", dev_type);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
config_destroy(&cfg);
|
||||
return false;
|
||||
}
|
||||
|
||||
config_destroy(&cfg);
|
||||
printf("cmd file parse success\r\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parse_cmd(int argc, char *argv[])
|
||||
{
|
||||
const char *optString = "hc:D:";
|
||||
const char *bipString = "p:n:D:";
|
||||
const char *mstpString = "m:b:p:d:s:n:D:";
|
||||
const struct option Options[] = {
|
||||
{ "config", required_argument, NULL, 'c' },
|
||||
{ "device", required_argument, NULL, 'D' },
|
||||
{ "network", required_argument, NULL, 'n' },
|
||||
{ "port", required_argument, NULL, 'P' },
|
||||
{ "mac", required_argument, NULL, 'm' },
|
||||
{ "baud", required_argument, NULL, 'b' },
|
||||
{ "parity", required_argument, NULL, 'p' },
|
||||
{ "databits", required_argument, NULL, 'd' },
|
||||
{ "stopbits", required_argument, NULL, 's' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ NULL, no_argument, NULL, 0 },
|
||||
};
|
||||
|
||||
int opt, dev_opt, index, result, fd;
|
||||
ROUTER_PORT *current = head;
|
||||
|
||||
if (argc < 2) {
|
||||
print_help();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* begin checking cmd parameters */
|
||||
opt = getopt_long(argc, argv, optString, Options, &index);
|
||||
printf("opt = %c\r\n", opt);
|
||||
while (opt != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
print_help();
|
||||
return false;
|
||||
break;
|
||||
case 'c':
|
||||
return read_config(optarg);
|
||||
break;
|
||||
case 'D':
|
||||
|
||||
/* create new list node to store port information */
|
||||
if (head == NULL) {
|
||||
head = (ROUTER_PORT *)malloc(sizeof(ROUTER_PORT));
|
||||
head->next = NULL;
|
||||
current = head;
|
||||
} else {
|
||||
ROUTER_PORT *tmp = current;
|
||||
current = current->next;
|
||||
current = (ROUTER_PORT *)malloc(sizeof(ROUTER_PORT));
|
||||
current->next = NULL;
|
||||
tmp->next = current;
|
||||
}
|
||||
|
||||
port_count++;
|
||||
if (strcmp(optarg, "bip") == 0) {
|
||||
current->type = BIP;
|
||||
|
||||
if (optind < argc && argv[optind][0] != '-') {
|
||||
current->iface = argv[optind];
|
||||
} else {
|
||||
current->iface = "eth0";
|
||||
}
|
||||
|
||||
/* setup default parameters */
|
||||
current->params.bip_params.port = 0xBAC0; /* 47808 */
|
||||
current->route_info.net = get_next_free_dnet();
|
||||
|
||||
/* check if interface is valid */
|
||||
fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (fd) {
|
||||
struct ifreq ifr;
|
||||
strncpy(ifr.ifr_name, current->iface,
|
||||
sizeof(ifr.ifr_name) - 1);
|
||||
result = ioctl(fd, SIOCGIFADDR, &ifr);
|
||||
if (result != -1) {
|
||||
close(fd);
|
||||
} else {
|
||||
PRINT(ERROR,
|
||||
"Error: Invalid interface for BIP device \n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
dev_opt =
|
||||
getopt_long(argc, argv, bipString, Options, &index);
|
||||
while (dev_opt != -1 && dev_opt != 'd') {
|
||||
switch (dev_opt) {
|
||||
case 'P':
|
||||
result = atoi(optarg);
|
||||
if (result) {
|
||||
current->params.bip_params.port =
|
||||
(uint16_t)result;
|
||||
} else {
|
||||
current->params.bip_params.port =
|
||||
0xBAC0; /* 47808 */
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
result = atoi(optarg);
|
||||
if (result) {
|
||||
current->route_info.net = (uint16_t)result;
|
||||
} else {
|
||||
current->route_info.net = port_count;
|
||||
}
|
||||
break;
|
||||
}
|
||||
dev_opt =
|
||||
getopt_long(argc, argv, bipString, Options, &index);
|
||||
}
|
||||
opt = dev_opt;
|
||||
} else if (strcmp(optarg, "mstp") == 0) {
|
||||
current->type = MSTP;
|
||||
|
||||
if (optind < argc && argv[optind][0] != '-') {
|
||||
current->iface = argv[optind];
|
||||
} else {
|
||||
current->iface = "/dev/ttyS0";
|
||||
}
|
||||
|
||||
/* check if interface is valid */
|
||||
fd = open(current->iface, O_NOCTTY | O_NONBLOCK);
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
} else {
|
||||
PRINT(ERROR,
|
||||
"Error: Invalid interface for MSTP device\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* setup default parameters */
|
||||
current->route_info.mac[0] = 127;
|
||||
current->route_info.mac_len = 1;
|
||||
current->params.mstp_params.max_master = 127;
|
||||
current->params.mstp_params.max_frames = 1;
|
||||
current->params.mstp_params.baudrate = 9600;
|
||||
current->params.mstp_params.parity = PARITY_NONE;
|
||||
current->params.mstp_params.databits = 8;
|
||||
current->params.mstp_params.stopbits = 1;
|
||||
current->route_info.net = get_next_free_dnet();
|
||||
|
||||
dev_opt =
|
||||
getopt_long(argc, argv, mstpString, Options, &index);
|
||||
while (dev_opt != -1 && dev_opt != 'D') {
|
||||
switch (dev_opt) {
|
||||
case 'm':
|
||||
result = atoi(optarg);
|
||||
if (result) {
|
||||
current->route_info.mac[0] =
|
||||
(uint8_t)result;
|
||||
}
|
||||
if (argv[optind][0] != '-') {
|
||||
current->params.mstp_params.max_master =
|
||||
(uint8_t)atoi(argv[optind]);
|
||||
if (current->params.mstp_params.max_master <
|
||||
current->route_info.mac[0])
|
||||
current->params.mstp_params.max_master =
|
||||
current->route_info.mac[0];
|
||||
|
||||
if (argv[optind + 1][0] != '-') {
|
||||
current->params.mstp_params.max_frames =
|
||||
(uint8_t)atoi(argv[optind + 1]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
result = atoi(optarg);
|
||||
if (result) {
|
||||
current->params.mstp_params.baudrate =
|
||||
(uint32_t)result;
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
switch (optarg[0]) {
|
||||
case 'E':
|
||||
current->params.mstp_params.parity =
|
||||
PARITY_EVEN;
|
||||
break;
|
||||
case 'O':
|
||||
current->params.mstp_params.parity =
|
||||
PARITY_ODD;
|
||||
break;
|
||||
default:
|
||||
current->params.mstp_params.parity =
|
||||
PARITY_NONE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
result = atoi(optarg);
|
||||
if (result >= 5 && result <= 8) {
|
||||
current->params.mstp_params.databits =
|
||||
(uint8_t)result;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
result = atoi(optarg);
|
||||
if (result >= 1 && result <= 2) {
|
||||
current->params.mstp_params.stopbits =
|
||||
(uint8_t)result;
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
result = atoi(optarg);
|
||||
if (result) {
|
||||
current->route_info.net = (uint16_t)result;
|
||||
}
|
||||
break;
|
||||
}
|
||||
dev_opt = getopt_long(
|
||||
argc, argv, mstpString, Options, &index);
|
||||
}
|
||||
opt = dev_opt;
|
||||
} else {
|
||||
PRINT(ERROR, "Error: %s unknown\n", optarg);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void init_port_threads(ROUTER_PORT *port_list)
|
||||
{
|
||||
ROUTER_PORT *port = port_list;
|
||||
pthread_t *thread;
|
||||
|
||||
while (port != NULL) {
|
||||
switch (port->type) {
|
||||
case BIP:
|
||||
port->func = &dl_ip_thread;
|
||||
break;
|
||||
case MSTP:
|
||||
port->func = &dl_mstp_thread;
|
||||
break;
|
||||
}
|
||||
|
||||
port->state = INIT;
|
||||
thread = (pthread_t *)malloc(sizeof(pthread_t));
|
||||
pthread_create(thread, NULL, port->func, port);
|
||||
|
||||
pthread_detach(*thread); /* for proper thread termination */
|
||||
|
||||
port = port->next;
|
||||
}
|
||||
}
|
||||
|
||||
bool init_router()
|
||||
{
|
||||
MSGBOX_ID msgboxid;
|
||||
ROUTER_PORT *port;
|
||||
|
||||
msgboxid = create_msgbox();
|
||||
if (msgboxid == INVALID_MSGBOX_ID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
port = head;
|
||||
/* add main message box id to all ports */
|
||||
while (port != NULL) {
|
||||
port->main_id = msgboxid;
|
||||
port = port->next;
|
||||
}
|
||||
|
||||
init_port_threads(head);
|
||||
|
||||
/* wait for port initialization */
|
||||
port = head;
|
||||
while (port != NULL) {
|
||||
if (port->state == RUNNING) {
|
||||
port = port->next;
|
||||
continue;
|
||||
} else if (port->state == INIT_FAILED) {
|
||||
PRINT(ERROR, "Error: Failed to initialize %s\n", port->iface);
|
||||
return false;
|
||||
} else {
|
||||
PRINT(INFO, "Initializing...\n");
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
ROUTER_PORT *port;
|
||||
BACMSG msg;
|
||||
|
||||
if (head == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
msg.origin = head->main_id;
|
||||
msg.type = SERVICE;
|
||||
msg.subtype = SHUTDOWN;
|
||||
|
||||
del_msgbox(head->main_id); /* close routers message box */
|
||||
|
||||
/* send shutdown message to all router ports */
|
||||
port = head;
|
||||
while (port != NULL) {
|
||||
if (port->state == RUNNING) {
|
||||
send_to_msgbox(port->port_id, &msg);
|
||||
}
|
||||
port = port->next;
|
||||
}
|
||||
|
||||
port = head;
|
||||
while (port != NULL) {
|
||||
if (port->state == FINISHED) {
|
||||
cleanup_dnets(port->route_info.dnets);
|
||||
port = port->next;
|
||||
free(head->iface);
|
||||
free(head);
|
||||
head = port;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&msg_lock);
|
||||
}
|
||||
|
||||
void print_msg(BACMSG *msg)
|
||||
{
|
||||
if (msg->type == DATA) {
|
||||
int i;
|
||||
MSG_DATA *data = (MSG_DATA *)msg->data;
|
||||
|
||||
if (data->pdu_len) {
|
||||
PRINT(DEBUG, "Message PDU: ");
|
||||
for (i = 0; i < data->pdu_len; i++) {
|
||||
PRINT(DEBUG, "%02X ", data->pdu[i]);
|
||||
}
|
||||
PRINT(DEBUG, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t process_msg(BACMSG *msg, MSG_DATA *data, uint8_t **buff)
|
||||
{
|
||||
BACNET_ADDRESS addr;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
ROUTER_PORT *srcport;
|
||||
ROUTER_PORT *destport;
|
||||
uint8_t npdu[MAX_NPDU];
|
||||
int16_t buff_len = 0;
|
||||
int apdu_offset;
|
||||
int apdu_len;
|
||||
int npdu_len;
|
||||
|
||||
memmove(data, msg->data, sizeof(MSG_DATA));
|
||||
|
||||
apdu_offset = npdu_decode(data->pdu, &data->dest, &addr, &npdu_data);
|
||||
apdu_len = data->pdu_len - apdu_offset;
|
||||
|
||||
srcport = find_snet(msg->origin);
|
||||
destport = find_dnet(data->dest.net, NULL);
|
||||
assert(srcport);
|
||||
|
||||
if (srcport && destport) {
|
||||
data->src.net = srcport->route_info.net;
|
||||
|
||||
/* if received from another router save real source address (not other
|
||||
* router source address) */
|
||||
if (addr.net > 0 && addr.net < BACNET_BROADCAST_NETWORK &&
|
||||
data->src.net != addr.net)
|
||||
memmove(&data->src, &addr, sizeof(BACNET_ADDRESS));
|
||||
|
||||
/* encode both source and destination for broadcast and router-to-router
|
||||
* communication */
|
||||
if (data->dest.net == BACNET_BROADCAST_NETWORK ||
|
||||
destport->route_info.net != data->dest.net) {
|
||||
npdu_len =
|
||||
npdu_encode_pdu(npdu, &data->dest, &data->src, &npdu_data);
|
||||
} else {
|
||||
npdu_len = npdu_encode_pdu(npdu, NULL, &data->src, &npdu_data);
|
||||
}
|
||||
|
||||
buff_len = npdu_len + data->pdu_len - apdu_offset;
|
||||
|
||||
*buff = (uint8_t *)malloc(buff_len);
|
||||
memmove(*buff, npdu, npdu_len); /* copy newly formed NPDU */
|
||||
memmove(*buff + npdu_len, &data->pdu[apdu_offset],
|
||||
apdu_len); /* copy APDU */
|
||||
|
||||
} else {
|
||||
/* request net search */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* delete received message */
|
||||
free_data((MSG_DATA *)msg->data);
|
||||
|
||||
return buff_len;
|
||||
}
|
||||
|
||||
int kbhit()
|
||||
{
|
||||
static const int STDIN = 0;
|
||||
static bool initialized = false;
|
||||
|
||||
if (!initialized) {
|
||||
/* use termios to turn off line buffering */
|
||||
struct termios term;
|
||||
tcgetattr(STDIN, &term);
|
||||
term.c_lflag &= ~ICANON;
|
||||
tcsetattr(STDIN, TCSANOW, &term);
|
||||
setbuf(stdin, NULL);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
int bytesWaiting;
|
||||
ioctl(STDIN, FIONREAD, &bytesWaiting);
|
||||
return bytesWaiting;
|
||||
}
|
||||
|
||||
bool is_network_msg(BACMSG *msg)
|
||||
{
|
||||
uint8_t control_byte; /* NPDU control byte */
|
||||
MSG_DATA *data = (MSG_DATA *)msg->data;
|
||||
|
||||
control_byte = data->pdu[1];
|
||||
|
||||
return control_byte & 0x80; /* check 7th bit */
|
||||
}
|
||||
|
||||
uint16_t get_next_free_dnet()
|
||||
{
|
||||
ROUTER_PORT *port = head;
|
||||
uint16_t i = 1;
|
||||
while (port) {
|
||||
if (port->route_info.net == i) {
|
||||
port = head;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
port = port->next;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
|
||||
* @date 2012
|
||||
* @brief Message queue module
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include "msgqueue.h"
|
||||
|
||||
pthread_mutex_t msg_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
MSGBOX_ID create_msgbox()
|
||||
{
|
||||
MSGBOX_ID msgboxid;
|
||||
|
||||
msgboxid = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
|
||||
if (msgboxid == INVALID_MSGBOX_ID) {
|
||||
return INVALID_MSGBOX_ID;
|
||||
}
|
||||
|
||||
return msgboxid;
|
||||
}
|
||||
|
||||
bool send_to_msgbox(MSGBOX_ID dest, BACMSG *msg)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = msgsnd(dest, msg, sizeof(BACMSG) - sizeof(MSGTYPE), 0);
|
||||
if (err) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
BACMSG *recv_from_msgbox(MSGBOX_ID src, BACMSG *msg)
|
||||
{
|
||||
int recv_bytes;
|
||||
|
||||
recv_bytes =
|
||||
msgrcv(src, msg, sizeof(BACMSG) - sizeof(MSGTYPE), 0, IPC_NOWAIT);
|
||||
if (recv_bytes > 0) {
|
||||
return msg;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void del_msgbox(MSGBOX_ID msgboxid)
|
||||
{
|
||||
if (msgboxid == INVALID_MSGBOX_ID) {
|
||||
return;
|
||||
} else {
|
||||
msgctl(msgboxid, IPC_RMID, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void free_data(MSG_DATA *data)
|
||||
{
|
||||
if (data->pdu) {
|
||||
free(data->pdu);
|
||||
data->pdu = NULL;
|
||||
}
|
||||
if (data) {
|
||||
free(data);
|
||||
data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void check_data(MSG_DATA *data)
|
||||
{
|
||||
/* lock and decrement messages reference count */
|
||||
pthread_mutex_lock(&msg_lock);
|
||||
if (--data->ref_count == 0) {
|
||||
free_data(data);
|
||||
}
|
||||
pthread_mutex_unlock(&msg_lock);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
|
||||
* @date 2012
|
||||
* @brief Message queue module
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#ifndef MSGQUEUE_H
|
||||
#define MSGQUEUE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/msg.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
|
||||
extern pthread_mutex_t msg_lock;
|
||||
|
||||
#define INVALID_MSGBOX_ID -1
|
||||
|
||||
typedef int MSGBOX_ID;
|
||||
|
||||
typedef enum {
|
||||
DATA = 1,
|
||||
SERVICE
|
||||
} MSGTYPE;
|
||||
|
||||
typedef enum {
|
||||
SHUTDOWN,
|
||||
CHG_IP,
|
||||
CHG_MAC
|
||||
} MSGSUBTYPE;
|
||||
|
||||
typedef struct _message {
|
||||
MSGTYPE type;
|
||||
MSGBOX_ID origin;
|
||||
MSGSUBTYPE subtype;
|
||||
void *data;
|
||||
/* add timestamp */
|
||||
} BACMSG;
|
||||
|
||||
/* specific message type data structures */
|
||||
typedef struct _msg_data {
|
||||
BACNET_ADDRESS dest;
|
||||
BACNET_ADDRESS src;
|
||||
uint8_t *pdu;
|
||||
uint16_t pdu_len;
|
||||
uint8_t ref_count;
|
||||
} MSG_DATA;
|
||||
|
||||
MSGBOX_ID create_msgbox(
|
||||
);
|
||||
|
||||
/* returns sent byte count */
|
||||
bool send_to_msgbox(
|
||||
MSGBOX_ID dest,
|
||||
BACMSG * msg);
|
||||
|
||||
/* returns received message */
|
||||
BACMSG *recv_from_msgbox(
|
||||
MSGBOX_ID src,
|
||||
BACMSG * msg);
|
||||
|
||||
void del_msgbox(
|
||||
MSGBOX_ID msgboxid);
|
||||
|
||||
/* free message data structure */
|
||||
void free_data(
|
||||
MSG_DATA * data);
|
||||
|
||||
/* check message reference counter and delete data if needed */
|
||||
void check_data(
|
||||
MSG_DATA * data);
|
||||
|
||||
#endif /* end of MSGQUEUE_H */
|
||||
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
|
||||
* @date 2012
|
||||
* @brief Datalink for MS/TP module
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "mstpmodule.h"
|
||||
#include "bacnet/bacint.h"
|
||||
#include "dlmstp_linux.h"
|
||||
#include <termios.h>
|
||||
|
||||
#define MSTP_THREAD_PRINT_ENABLED
|
||||
#ifdef MSTP_THREAD_PRINT_ENABLED
|
||||
#define mstp_thread_debug(...) fprintf(stderr, __VA_ARGS__)
|
||||
#else
|
||||
#define mstp_thread_debug(...)
|
||||
#endif
|
||||
|
||||
void *dl_mstp_thread(void *pArgs)
|
||||
{
|
||||
ROUTER_PORT *port = (ROUTER_PORT *)pArgs;
|
||||
struct mstp_port_struct_t mstp_port = { (MSTP_RECEIVE_STATE)0 };
|
||||
volatile SHARED_MSTP_DATA shared_port_data = { 0 };
|
||||
uint16_t pdu_len;
|
||||
uint8_t shutdown = 0;
|
||||
|
||||
shared_port_data.Treply_timeout = 260;
|
||||
shared_port_data.MSTP_Packets = 0;
|
||||
shared_port_data.Tusage_timeout = 50;
|
||||
shared_port_data.RS485_Handle = -1;
|
||||
shared_port_data.RS485_Baud = B38400;
|
||||
shared_port_data.RS485MOD = 0;
|
||||
|
||||
switch (port->params.mstp_params.databits) {
|
||||
case 5:
|
||||
shared_port_data.RS485MOD = CS5;
|
||||
break;
|
||||
case 6:
|
||||
shared_port_data.RS485MOD = CS6;
|
||||
break;
|
||||
case 7:
|
||||
shared_port_data.RS485MOD = CS7;
|
||||
break;
|
||||
default:
|
||||
shared_port_data.RS485MOD = CS8;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (port->params.mstp_params.parity) {
|
||||
case PARITY_EVEN:
|
||||
shared_port_data.RS485MOD |= PARENB;
|
||||
break;
|
||||
case PARITY_ODD:
|
||||
shared_port_data.RS485MOD |= PARENB | PARODD;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (port->params.mstp_params.stopbits == 2)
|
||||
shared_port_data.RS485MOD |= CSTOPB;
|
||||
|
||||
mstp_port.UserData = (void *)&shared_port_data;
|
||||
dlmstp_set_baud_rate(&mstp_port, port->params.mstp_params.baudrate);
|
||||
dlmstp_set_mac_address(&mstp_port, port->route_info.mac[0]);
|
||||
dlmstp_set_max_info_frames(&mstp_port, port->params.mstp_params.max_frames);
|
||||
dlmstp_set_max_master(&mstp_port, port->params.mstp_params.max_master);
|
||||
if (!dlmstp_init(&mstp_port, port->iface))
|
||||
printf("MSTP %s init failed. Stop.\n", port->iface);
|
||||
|
||||
port->port_id = create_msgbox();
|
||||
if (port->port_id == INVALID_MSGBOX_ID) {
|
||||
port->state = INIT_FAILED;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
port->state = RUNNING;
|
||||
|
||||
while (!shutdown) {
|
||||
/* message loop */
|
||||
BACMSG msg_storage, *bacmsg;
|
||||
MSG_DATA *msg_data;
|
||||
|
||||
bacmsg = recv_from_msgbox(port->port_id, &msg_storage);
|
||||
|
||||
if (bacmsg) {
|
||||
switch (bacmsg->type) {
|
||||
case DATA:
|
||||
msg_data = (MSG_DATA *)bacmsg->data;
|
||||
|
||||
if (msg_data->dest.net == BACNET_BROADCAST_NETWORK) {
|
||||
dlmstp_get_broadcast_address(&(msg_data->dest));
|
||||
} else {
|
||||
msg_data->dest.mac[0] = msg_data->dest.adr[0];
|
||||
msg_data->dest.mac_len = 1;
|
||||
}
|
||||
|
||||
dlmstp_send_pdu(&mstp_port, &(msg_data->dest),
|
||||
msg_data->pdu, msg_data->pdu_len);
|
||||
|
||||
check_data(msg_data);
|
||||
|
||||
break;
|
||||
case SERVICE:
|
||||
switch (bacmsg->subtype) {
|
||||
case SHUTDOWN:
|
||||
shutdown = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
pdu_len = dlmstp_receive(&mstp_port, NULL, NULL, 0, 5);
|
||||
|
||||
if (pdu_len > 0) {
|
||||
msg_data = (MSG_DATA *)malloc(sizeof(MSG_DATA));
|
||||
memmove(&(msg_data->src),
|
||||
(const void *)&(shared_port_data.Receive_Packet.address),
|
||||
sizeof(shared_port_data.Receive_Packet.address));
|
||||
msg_data->src.adr[0] = msg_data->src.mac[0];
|
||||
msg_data->src.len = 1;
|
||||
msg_data->pdu = (uint8_t *)malloc(pdu_len);
|
||||
memmove(msg_data->pdu,
|
||||
(const void *)&(shared_port_data.Receive_Packet.pdu),
|
||||
pdu_len);
|
||||
msg_data->pdu_len = pdu_len;
|
||||
|
||||
msg_storage.type = DATA;
|
||||
msg_storage.subtype = (MSGSUBTYPE)0;
|
||||
msg_storage.origin = port->port_id;
|
||||
msg_storage.data = msg_data;
|
||||
|
||||
if (!send_to_msgbox(port->main_id, &msg_storage)) {
|
||||
free_data(msg_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dlmstp_cleanup(&mstp_port);
|
||||
port->state = FINISHED;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
|
||||
* @date 2012
|
||||
* @brief Datalink for MS/TP module
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#ifndef MSTPMODULE_H
|
||||
#define MSTPMODULE_H
|
||||
|
||||
#include "portthread.h"
|
||||
|
||||
void *dl_mstp_thread(
|
||||
void *pArgs);
|
||||
|
||||
#endif /* end of MSTPMODULE_H */
|
||||
@@ -0,0 +1,359 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
|
||||
* @date 2012
|
||||
* @brief Network layer for BACnet routing
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "network_layer.h"
|
||||
#include "bacnet/bacint.h"
|
||||
|
||||
uint16_t process_network_message(BACMSG *msg, MSG_DATA *data, uint8_t **buff)
|
||||
{
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
ROUTER_PORT *srcport;
|
||||
ROUTER_PORT *destport;
|
||||
uint16_t net;
|
||||
uint8_t error_code;
|
||||
int16_t buff_len = 0;
|
||||
int apdu_offset;
|
||||
int apdu_len;
|
||||
|
||||
memmove(data, msg->data, sizeof(MSG_DATA));
|
||||
|
||||
apdu_offset = npdu_decode(data->pdu, &data->dest, NULL, &npdu_data);
|
||||
apdu_len = data->pdu_len - apdu_offset;
|
||||
|
||||
srcport = find_snet(msg->origin);
|
||||
data->src.net = srcport->route_info.net;
|
||||
|
||||
switch (npdu_data.network_message_type) {
|
||||
case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK:
|
||||
PRINT(INFO, "Recieved Who-Is-Router-To-Network message\n");
|
||||
if (apdu_len) {
|
||||
/* if NET specified */
|
||||
decode_unsigned16(&data->pdu[apdu_offset], &net);
|
||||
if (srcport->route_info.net == net) {
|
||||
PRINT(INFO, "Message discarded: NET directly connected\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
destport = find_dnet(net, NULL); /* see if NET can be reached */
|
||||
if (destport) {
|
||||
/* if TRUE send reply */
|
||||
PRINT(INFO, "Sending I-Am-Router-To-Network message\n");
|
||||
buff_len = create_network_message(
|
||||
NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, data, buff,
|
||||
&net);
|
||||
} else {
|
||||
data->dest.net = net; /* NET to look for */
|
||||
return -1; /* else initiate NET search procedure */
|
||||
}
|
||||
} else {
|
||||
/* if NET is omitted (message sent with -1) */
|
||||
PRINT(INFO, "Sending I-Am-Router-To-Network message\n");
|
||||
buff_len = create_network_message(
|
||||
NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, data, buff, NULL);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: {
|
||||
PRINT(INFO, "Recieved I-Am-Router-To-Network message\n");
|
||||
int net_count = apdu_len / 2;
|
||||
int i;
|
||||
for (i = 0; i < net_count; i++) {
|
||||
decode_unsigned16(&data->pdu[apdu_offset + 2 * i],
|
||||
&net); /* decode received NET values */
|
||||
add_dnet(&srcport->route_info, net,
|
||||
data->src); /* and update routing table */
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK: {
|
||||
/* first octet of the message contains rejection reason */
|
||||
/* next two octets contain NET (can be decoded for additional info
|
||||
* on error) */
|
||||
error_code = data->pdu[apdu_offset];
|
||||
switch (error_code) {
|
||||
case 0:
|
||||
PRINT(ERROR, "Error!\n");
|
||||
break;
|
||||
case 1:
|
||||
PRINT(ERROR, "Error: Network unreachable\n");
|
||||
break;
|
||||
case 2:
|
||||
PRINT(ERROR, "Error: Network is busy\n");
|
||||
break;
|
||||
case 3:
|
||||
PRINT(ERROR, "Error: Unknown network message type\n");
|
||||
break;
|
||||
case 4:
|
||||
PRINT(ERROR, "Error: Message too long\n");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE:
|
||||
PRINT(INFO, "Recieved Initialize-Routing-Table message\n");
|
||||
if (data->pdu[apdu_offset] > 0) {
|
||||
int net_count = data->pdu[apdu_offset];
|
||||
while (net_count--) {
|
||||
int i = 1;
|
||||
decode_unsigned16(&data->pdu[apdu_offset + i],
|
||||
&net); /* decode received NET values */
|
||||
add_dnet(&srcport->route_info, net,
|
||||
data->src); /* and update routing table */
|
||||
if (data->pdu[apdu_offset + i + 3] >
|
||||
0) /* find next NET value */
|
||||
i = data->pdu[apdu_offset + i + 3] + 4;
|
||||
else
|
||||
i = i + 4;
|
||||
}
|
||||
buff_len = create_network_message(
|
||||
NETWORK_MESSAGE_INIT_RT_TABLE_ACK, data, buff, NULL);
|
||||
} else
|
||||
buff_len = create_network_message(
|
||||
NETWORK_MESSAGE_INIT_RT_TABLE_ACK, data, buff, &buff);
|
||||
break;
|
||||
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
|
||||
PRINT(INFO, "Recieved Initialize-Routing-Table-Ack message\n");
|
||||
if (data->pdu[apdu_offset] > 0) {
|
||||
int net_count = data->pdu[apdu_offset];
|
||||
while (net_count--) {
|
||||
int i = 1;
|
||||
decode_unsigned16(&data->pdu[apdu_offset + i],
|
||||
&net); /* decode received NET values */
|
||||
add_dnet(&srcport->route_info, net,
|
||||
data->src); /* and update routing table */
|
||||
if (data->pdu[apdu_offset + i + 3] >
|
||||
0) /* find next NET value */
|
||||
i = data->pdu[apdu_offset + i + 3] + 4;
|
||||
else
|
||||
i = i + 4;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NETWORK_MESSAGE_INVALID:
|
||||
case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK:
|
||||
/* hell if I know what to do with these messages */
|
||||
break;
|
||||
case NETWORK_MESSAGE_CHALLENGE_REQUEST:
|
||||
case NETWORK_MESSAGE_SECURITY_PAYLOAD:
|
||||
case NETWORK_MESSAGE_SECURITY_RESPONSE:
|
||||
case NETWORK_MESSAGE_REQUEST_KEY_UPDATE:
|
||||
case NETWORK_MESSAGE_UPDATE_KEY_SET:
|
||||
case NETWORK_MESSAGE_UPDATE_DISTRIBUTION_KEY:
|
||||
case NETWORK_MESSAGE_REQUEST_MASTER_KEY:
|
||||
case NETWORK_MESSAGE_SET_MASTER_KEY:
|
||||
case NETWORK_MESSAGE_NETWORK_NUMBER_IS:
|
||||
/* security messages */
|
||||
break;
|
||||
case NETWORK_MESSAGE_WHAT_IS_NETWORK_NUMBER:
|
||||
buff_len = create_network_message(
|
||||
NETWORK_MESSAGE_NETWORK_NUMBER_IS, data, buff, &buff);
|
||||
break;
|
||||
|
||||
default:
|
||||
PRINT(ERROR, "Error: Message unsupported\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return buff_len;
|
||||
}
|
||||
|
||||
uint16_t create_network_message(
|
||||
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
|
||||
MSG_DATA *data,
|
||||
uint8_t **buff,
|
||||
void *val)
|
||||
{
|
||||
int16_t buff_len;
|
||||
bool data_expecting_reply = false;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
|
||||
if (network_message_type == NETWORK_MESSAGE_INIT_RT_TABLE)
|
||||
data_expecting_reply = true;
|
||||
init_npdu(&npdu_data, network_message_type, data_expecting_reply);
|
||||
|
||||
*buff = (uint8_t *)malloc(128); /* resolve different length */
|
||||
|
||||
/* manual destination setup for Init-RT-Table-Ack message */
|
||||
data->dest.net = BACNET_BROADCAST_NETWORK;
|
||||
buff_len = npdu_encode_pdu(*buff, &data->dest, NULL, &npdu_data);
|
||||
|
||||
switch (network_message_type) {
|
||||
case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK:
|
||||
if (val != NULL) {
|
||||
uint8_t *valptr = (uint8_t *)val;
|
||||
uint16_t val16 = (valptr[0]) + (valptr[1] << 8);
|
||||
buff_len += encode_unsigned16(*buff + buff_len, val16);
|
||||
}
|
||||
break;
|
||||
|
||||
case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK:
|
||||
if (val != NULL) {
|
||||
uint8_t *valptr = (uint8_t *)val;
|
||||
uint16_t val16 = (valptr[0]) + (valptr[1] << 8);
|
||||
buff_len += encode_unsigned16(*buff + buff_len, val16);
|
||||
} else {
|
||||
ROUTER_PORT *port = head;
|
||||
DNET *dnet;
|
||||
while (port != NULL) {
|
||||
if (port->route_info.net != data->src.net) {
|
||||
buff_len += encode_unsigned16(
|
||||
*buff + buff_len, port->route_info.net);
|
||||
dnet = port->route_info.dnets;
|
||||
while (dnet != NULL) {
|
||||
buff_len +=
|
||||
encode_unsigned16(*buff + buff_len, dnet->net);
|
||||
dnet = dnet->next;
|
||||
}
|
||||
port = port->next;
|
||||
} else {
|
||||
dnet = port->route_info.dnets;
|
||||
while (dnet != NULL) {
|
||||
buff_len +=
|
||||
encode_unsigned16(*buff + buff_len, dnet->net);
|
||||
dnet = dnet->next;
|
||||
}
|
||||
port = port->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK: {
|
||||
uint8_t *valptr = (uint8_t *)val;
|
||||
uint16_t val16 = (valptr[0]) + (valptr[1] << 8);
|
||||
buff_len += encode_unsigned16(*buff + buff_len, val16);
|
||||
break;
|
||||
}
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE:
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
|
||||
if ((uint8_t *)val) {
|
||||
(*buff)[buff_len++] = (uint8_t)port_count;
|
||||
|
||||
if (port_count > 0) {
|
||||
ROUTER_PORT *port = head;
|
||||
uint8_t portID = 1;
|
||||
|
||||
while (port != NULL) {
|
||||
buff_len += encode_unsigned16(
|
||||
*buff + buff_len, port->route_info.net);
|
||||
(*buff)[buff_len++] = portID++;
|
||||
(*buff)[buff_len++] = 0;
|
||||
port = port->next;
|
||||
}
|
||||
}
|
||||
} else
|
||||
(*buff)[buff_len++] = (uint8_t)0;
|
||||
break;
|
||||
|
||||
case NETWORK_MESSAGE_INVALID:
|
||||
case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK:
|
||||
/* hell if I know what to do with these messages */
|
||||
break;
|
||||
case NETWORK_MESSAGE_CHALLENGE_REQUEST:
|
||||
case NETWORK_MESSAGE_SECURITY_PAYLOAD:
|
||||
case NETWORK_MESSAGE_SECURITY_RESPONSE:
|
||||
case NETWORK_MESSAGE_REQUEST_KEY_UPDATE:
|
||||
case NETWORK_MESSAGE_UPDATE_KEY_SET:
|
||||
case NETWORK_MESSAGE_UPDATE_DISTRIBUTION_KEY:
|
||||
case NETWORK_MESSAGE_REQUEST_MASTER_KEY:
|
||||
case NETWORK_MESSAGE_SET_MASTER_KEY:
|
||||
/* security messages */
|
||||
break;
|
||||
case NETWORK_MESSAGE_NETWORK_NUMBER_IS:
|
||||
/* fixme: needs message constructed */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return buff_len;
|
||||
}
|
||||
|
||||
void send_network_message(BACNET_NETWORK_MESSAGE_TYPE network_message_type,
|
||||
MSG_DATA *data,
|
||||
uint8_t **buff,
|
||||
void *val)
|
||||
{
|
||||
BACMSG msg;
|
||||
ROUTER_PORT *port = head;
|
||||
int16_t buff_len;
|
||||
|
||||
if (!data) {
|
||||
data = (MSG_DATA *)malloc(sizeof(MSG_DATA));
|
||||
data->dest.net = BACNET_BROADCAST_NETWORK;
|
||||
data->dest.len = 0;
|
||||
}
|
||||
|
||||
buff_len = create_network_message(network_message_type, data, buff, val);
|
||||
|
||||
/* form network message */
|
||||
data->pdu = *buff;
|
||||
data->pdu_len = buff_len;
|
||||
msg.origin = head->main_id;
|
||||
msg.type = DATA;
|
||||
msg.data = data;
|
||||
|
||||
data->ref_count = port_count;
|
||||
while (port != NULL) {
|
||||
if (port->state == FINISHED) {
|
||||
port = port->next;
|
||||
continue;
|
||||
}
|
||||
send_to_msgbox(port->port_id, &msg);
|
||||
port = port->next;
|
||||
}
|
||||
}
|
||||
|
||||
void init_npdu(BACNET_NPDU_DATA *npdu_data,
|
||||
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
|
||||
bool data_expecting_reply)
|
||||
{
|
||||
if (npdu_data) {
|
||||
npdu_data->data_expecting_reply = data_expecting_reply;
|
||||
npdu_data->protocol_version = BACNET_PROTOCOL_VERSION;
|
||||
npdu_data->network_layer_message = true;
|
||||
npdu_data->network_message_type = network_message_type;
|
||||
npdu_data->vendor_id = 0;
|
||||
npdu_data->priority = MESSAGE_PRIORITY_NORMAL;
|
||||
npdu_data->hop_count = HOP_COUNT_DEFAULT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
|
||||
* @date 2012
|
||||
* @brief Network layer for BACnet routing
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#ifndef NETWORK_LAYER_H
|
||||
#define NETWORK_LAYER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacport.h"
|
||||
#include "portthread.h"
|
||||
|
||||
uint16_t process_network_message(
|
||||
BACMSG * msg,
|
||||
MSG_DATA * data,
|
||||
uint8_t ** buff);
|
||||
|
||||
uint16_t create_network_message(
|
||||
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
|
||||
MSG_DATA * data,
|
||||
uint8_t ** buff,
|
||||
void *val);
|
||||
|
||||
void send_network_message(
|
||||
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
|
||||
MSG_DATA * data,
|
||||
uint8_t ** buff,
|
||||
void *val);
|
||||
|
||||
void init_npdu(
|
||||
BACNET_NPDU_DATA * npdu_data,
|
||||
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
|
||||
bool data_expecting_reply);
|
||||
|
||||
#endif /* end of NETWORK_LAYER_H */
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
|
||||
* @date 2012
|
||||
* @brief Network port storage and handling
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "portthread.h"
|
||||
|
||||
ROUTER_PORT *find_snet(MSGBOX_ID id)
|
||||
{
|
||||
ROUTER_PORT *port = head;
|
||||
|
||||
while (port != NULL) {
|
||||
if (port->port_id == id) {
|
||||
return port;
|
||||
}
|
||||
port = port->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ROUTER_PORT *find_dnet(uint16_t net, BACNET_ADDRESS *addr)
|
||||
{
|
||||
ROUTER_PORT *port = head;
|
||||
DNET *dnet;
|
||||
|
||||
/* for broadcast messages no search is needed */
|
||||
if (net == BACNET_BROADCAST_NETWORK)
|
||||
return port;
|
||||
|
||||
while (port != NULL) {
|
||||
/* check if DNET is directly connected to the router */
|
||||
if (net == port->route_info.net)
|
||||
return port;
|
||||
|
||||
/* else search router ports DNET list */
|
||||
else if (port->route_info.dnets) {
|
||||
dnet = port->route_info.dnets;
|
||||
while (dnet != NULL) {
|
||||
if (net == dnet->net) {
|
||||
if (addr) {
|
||||
memmove(&addr->len, &dnet->mac_len, 1);
|
||||
memmove(&addr->adr[0], &dnet->mac[0], MAX_MAC_LEN);
|
||||
}
|
||||
return port;
|
||||
}
|
||||
dnet = dnet->next;
|
||||
}
|
||||
}
|
||||
port = port->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void add_dnet(RT_ENTRY *route_info, uint16_t net, BACNET_ADDRESS addr)
|
||||
{
|
||||
DNET *dnet = route_info->dnets;
|
||||
DNET *tmp;
|
||||
|
||||
if (dnet == NULL) {
|
||||
route_info->dnets = (DNET *)malloc(sizeof(DNET));
|
||||
memmove(&route_info->dnets->mac_len, &addr.len, 1);
|
||||
memmove(&route_info->dnets->mac[0], &addr.adr[0], MAX_MAC_LEN);
|
||||
route_info->dnets->net = net;
|
||||
route_info->dnets->state = true;
|
||||
route_info->dnets->next = NULL;
|
||||
} else {
|
||||
while (dnet != NULL) {
|
||||
if (dnet->net == net) { /* make sure NETs are not repeated */
|
||||
return;
|
||||
}
|
||||
tmp = dnet;
|
||||
dnet = dnet->next;
|
||||
}
|
||||
|
||||
dnet = (DNET *)malloc(sizeof(DNET));
|
||||
memmove(&dnet->mac_len, &addr.len, 1);
|
||||
memmove(&dnet->mac[0], &addr.adr[0], MAX_MAC_LEN);
|
||||
dnet->net = net;
|
||||
dnet->state = true;
|
||||
dnet->next = NULL;
|
||||
tmp->next = dnet;
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup_dnets(DNET *dnets)
|
||||
{
|
||||
DNET *dnet = dnets;
|
||||
while (dnet != NULL) {
|
||||
dnet = dnet->next;
|
||||
free(dnets);
|
||||
dnets = dnet;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
|
||||
* @date 2012
|
||||
* @brief Network port storage and handling
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#ifndef PORTTHREAD_H
|
||||
#define PORTTHREAD_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
#include "msgqueue.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
|
||||
#define ERROR 1
|
||||
#define INFO 2
|
||||
#define DEBUG 3
|
||||
|
||||
#define DEBUG_LEVEL 3
|
||||
#ifdef DEBUG_LEVEL
|
||||
#define PRINT(debug_level, ...) if(debug_level <= DEBUG_LEVEL) fprintf(stderr, __VA_ARGS__)
|
||||
#else
|
||||
#define PRINT(...)
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
BIP = 1,
|
||||
MSTP = 2
|
||||
} DL_TYPE;
|
||||
|
||||
typedef enum {
|
||||
INIT,
|
||||
INIT_FAILED,
|
||||
RUNNING,
|
||||
FINISHED
|
||||
} PORT_STATE;
|
||||
|
||||
/* router port thread function */
|
||||
typedef void *(
|
||||
*PORT_FUNC) (
|
||||
void *);
|
||||
|
||||
typedef enum {
|
||||
PARITY_NONE,
|
||||
PARITY_EVEN,
|
||||
PARITY_ODD
|
||||
} PARITY;
|
||||
|
||||
/* port specific parameters */
|
||||
typedef union _port_params {
|
||||
struct {
|
||||
uint16_t port;
|
||||
} bip_params;
|
||||
struct {
|
||||
uint32_t baudrate;
|
||||
PARITY parity;
|
||||
uint8_t databits;
|
||||
uint8_t stopbits;
|
||||
uint8_t max_master;
|
||||
uint8_t max_frames;
|
||||
} mstp_params;
|
||||
} PORT_PARAMS;
|
||||
|
||||
/* list node for reacheble networks */
|
||||
typedef struct _dnet {
|
||||
uint8_t mac[MAX_MAC_LEN];
|
||||
uint8_t mac_len;
|
||||
uint16_t net;
|
||||
bool state; /* enabled or disabled */
|
||||
struct _dnet *next;
|
||||
} DNET;
|
||||
|
||||
/* information for routing table */
|
||||
typedef struct _routing_table_entry {
|
||||
uint8_t mac[MAX_MAC_LEN];
|
||||
uint8_t mac_len;
|
||||
uint16_t net;
|
||||
DNET *dnets;
|
||||
} RT_ENTRY;
|
||||
|
||||
typedef struct _port {
|
||||
DL_TYPE type;
|
||||
PORT_STATE state;
|
||||
MSGBOX_ID main_id; /* same for every router port */
|
||||
MSGBOX_ID port_id; /* different for every router port */
|
||||
char *iface;
|
||||
PORT_FUNC func;
|
||||
RT_ENTRY route_info;
|
||||
PORT_PARAMS params;
|
||||
struct _port *next; /* pointer to next list node */
|
||||
} ROUTER_PORT;
|
||||
|
||||
extern ROUTER_PORT *head;
|
||||
extern int port_count;
|
||||
|
||||
/* get recieving router port */
|
||||
ROUTER_PORT *find_snet(
|
||||
MSGBOX_ID id);
|
||||
|
||||
/* get sending router port */
|
||||
ROUTER_PORT *find_dnet(
|
||||
uint16_t net,
|
||||
BACNET_ADDRESS * addr);
|
||||
|
||||
/* add reacheble network for specified router port */
|
||||
void add_dnet(
|
||||
RT_ENTRY * route_info,
|
||||
uint16_t net,
|
||||
BACNET_ADDRESS addr);
|
||||
|
||||
void cleanup_dnets(
|
||||
DNET * dnets);
|
||||
|
||||
#endif /* end of PORTTHREAD_H */
|
||||
@@ -0,0 +1,149 @@
|
||||
-----------------------
|
||||
1. About
|
||||
-----------------------
|
||||
|
||||
The Router connects two or more BACnet/IP and BACnet MS/TP networks.
|
||||
Number of netwoks is limited only by available hardware communication devices (or ports for Ethernet).
|
||||
|
||||
-----------------------
|
||||
2. License
|
||||
-----------------------
|
||||
|
||||
Copyright (C) 2012 Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
|
||||
|
||||
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.
|
||||
|
||||
-----------------------
|
||||
3. Build
|
||||
-----------------------
|
||||
|
||||
1. Download, build and install libconfig C/C++ Configuration File Library
|
||||
from http://www.hyperrealm.com/libconfig or use APT to install
|
||||
sudo apt-get install libconfig-dev
|
||||
2. Run "make clean all" from library root directory
|
||||
3. Run "make router" from library root directory
|
||||
|
||||
-----------------------
|
||||
4. Router configuration
|
||||
-----------------------
|
||||
|
||||
4.1. Configuration file format.
|
||||
|
||||
//single line comment
|
||||
|
||||
/*
|
||||
multiline comment
|
||||
*/
|
||||
|
||||
ports =
|
||||
(
|
||||
//route_1
|
||||
{
|
||||
device_type = "<value>";
|
||||
//route specific arguments, see below
|
||||
},
|
||||
|
||||
//route_2
|
||||
{
|
||||
device_type = "<value>";
|
||||
//route specific arguments, see below
|
||||
},
|
||||
|
||||
//.....
|
||||
|
||||
//route_n
|
||||
{
|
||||
device_type = "<value>";
|
||||
//route specific arguments, see below
|
||||
}
|
||||
);
|
||||
|
||||
Note: - arguments are separeted with ';'
|
||||
- routes are separeted with ','
|
||||
- no ',' after the last route
|
||||
|
||||
4.2. Configuration file arguments.
|
||||
|
||||
Common arguments:
|
||||
device_type - Describes a type of route, may be "bip" (Etherent) or "mstp" (Serial port). Use quotes.
|
||||
device - Connection device, for example "eth0" or "/dev/ttyS0"; default values: for BIP:"eth0", for MSTP: "/dev/ttyS0". Use quotes.
|
||||
network - Network number [1..65534]. Do not use network number 65535, it is broadcast number; default begins from 1 to routes count.
|
||||
|
||||
bip arguments:
|
||||
port - bip UDP port; default port is 47808 (0xBAC0).
|
||||
|
||||
mstp arguments:
|
||||
mac - MSTP MAC; default value is 127.
|
||||
max_master - MSTP max master; default value is 127.
|
||||
max_frames - 1. Segmentation does not supported.
|
||||
baud - one from the list: 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400; default baud is 9600
|
||||
parity - one from the list (with quotes): "None", "Even", "Odd"; default parity "None". Use quotes.
|
||||
databits - one from the list: 5, 6, 7, 8; default 8.
|
||||
stopbits - 1 or 2; default 1.
|
||||
|
||||
4.3. Example of configuration file.
|
||||
|
||||
ports =
|
||||
(
|
||||
{
|
||||
device_type = "bip";
|
||||
device = "eth0";
|
||||
port = 47808;
|
||||
network = 1;
|
||||
},
|
||||
{
|
||||
device_type = "bip";
|
||||
device = "eth1";
|
||||
port = 47808;
|
||||
network = 2;
|
||||
},
|
||||
{
|
||||
device_type = "bip";
|
||||
device = "eth1";
|
||||
port = 47809;
|
||||
network = 3;
|
||||
},
|
||||
{
|
||||
device_type = "mstp";
|
||||
device = "/dev/ttyS0";
|
||||
mac = 1;
|
||||
max_master = 127;
|
||||
max_frames = 1;
|
||||
baud = 38400;
|
||||
parity = "None";
|
||||
databits = 8;
|
||||
stopbits = 1;
|
||||
network = 4;
|
||||
}
|
||||
);
|
||||
|
||||
-----------------------
|
||||
5. Start
|
||||
-----------------------
|
||||
|
||||
5.1. With configuration file
|
||||
1. Copy configuration file in the router executable directory
|
||||
2. Start the router with "sudo ./router -c init.cfg" command in terminal
|
||||
|
||||
5.2. Passing params in command line
|
||||
1. sudo ./router -D "mstp" "/dev/ttyS0" --mac 1 127 1 --baud 38400 --network 4 -D "bip" "eth0" --network 1
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
#Makefile to build BACnet Application using GCC compiler
|
||||
|
||||
# Executable file name
|
||||
TARGET = bacscov
|
||||
# BACnet objects that are used with this app
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/client/device-client.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c
|
||||
BACNET_BASIC_SRC += \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_apdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_ccov.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_ucov.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_noserv.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_whois.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_cov.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_whois.c
|
||||
|
||||
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = $(SRC) $(BACNET_SRC) $(BACNET_BASIC_SRC) $(BACNET_PORT_SRC)
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
|
||||
@@ -0,0 +1,417 @@
|
||||
/*************************************************************************
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <time.h> /* for time */
|
||||
#include <ctype.h> /* for toupper */
|
||||
|
||||
#define PRINT_ENABLED 1
|
||||
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/arf.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacport.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/whois.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/sys/filename.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/dlenv.h"
|
||||
|
||||
/* buffer used for receive */
|
||||
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
|
||||
|
||||
/* converted command line arguments */
|
||||
static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE;
|
||||
/* Process identifier for matching replies */
|
||||
static uint32_t Target_Device_Process_Identifier = 0;
|
||||
/* the invoke id is needed to filter incoming messages */
|
||||
static uint8_t Request_Invoke_ID = 0;
|
||||
/* MAC and SNET address of target */
|
||||
static BACNET_ADDRESS Target_Address;
|
||||
/* indication of error, reject, or abort */
|
||||
static bool Error_Detected = false;
|
||||
/* data used in COV subscription request */
|
||||
BACNET_SUBSCRIBE_COV_DATA *COV_Subscribe_Data = NULL;
|
||||
/* flags to signal early termination */
|
||||
static bool Simple_Ack_Detected = false;
|
||||
static bool Cancel_Requested = false;
|
||||
|
||||
static void MyErrorHandler(BACNET_ADDRESS *src,
|
||||
uint8_t invoke_id,
|
||||
BACNET_ERROR_CLASS error_class,
|
||||
BACNET_ERROR_CODE error_code)
|
||||
{
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
printf("BACnet Error: %s: %s\r\n",
|
||||
bactext_error_class_name((int)error_class),
|
||||
bactext_error_code_name((int)error_code));
|
||||
Error_Detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void MyAbortHandler(
|
||||
BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t abort_reason, bool server)
|
||||
{
|
||||
(void)server;
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
printf("BACnet Abort: %s\r\n",
|
||||
bactext_abort_reason_name((int)abort_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void MyRejectHandler(
|
||||
BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t reject_reason)
|
||||
{
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
printf("BACnet Reject: %s\r\n",
|
||||
bactext_reject_reason_name((int)reject_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void My_Unconfirmed_COV_Notification_Handler(
|
||||
uint8_t *service_request, uint16_t service_len, BACNET_ADDRESS *src)
|
||||
{
|
||||
handler_ucov_notification(service_request, service_len, src);
|
||||
}
|
||||
|
||||
static void My_Confirmed_COV_Notification_Handler(uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS *src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA *service_data)
|
||||
{
|
||||
handler_ccov_notification(service_request, service_len, src, service_data);
|
||||
}
|
||||
|
||||
static void MyWritePropertySimpleAckHandler(
|
||||
BACNET_ADDRESS *src, uint8_t invoke_id)
|
||||
{
|
||||
if (address_match(&Target_Address, src) &&
|
||||
(invoke_id == Request_Invoke_ID)) {
|
||||
printf("SubscribeCOV Acknowledged!\r\n");
|
||||
Simple_Ack_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);
|
||||
/* 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 COV subscriptions */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_COV_NOTIFICATION,
|
||||
My_Confirmed_COV_Notification_Handler);
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_COV_NOTIFICATION,
|
||||
My_Unconfirmed_COV_Notification_Handler);
|
||||
/* handle the Simple ack coming back from SubscribeCOV */
|
||||
apdu_set_confirmed_simple_ack_handler(
|
||||
SERVICE_CONFIRMED_SUBSCRIBE_COV, MyWritePropertySimpleAckHandler);
|
||||
/* handle any errors coming back */
|
||||
apdu_set_error_handler(SERVICE_CONFIRMED_SUBSCRIBE_COV, MyErrorHandler);
|
||||
apdu_set_abort_handler(MyAbortHandler);
|
||||
apdu_set_reject_handler(MyRejectHandler);
|
||||
}
|
||||
|
||||
static void cleanup(void)
|
||||
{
|
||||
BACNET_SUBSCRIBE_COV_DATA *cov_data = NULL;
|
||||
BACNET_SUBSCRIBE_COV_DATA *cov_data_old = NULL;
|
||||
|
||||
cov_data = COV_Subscribe_Data;
|
||||
while (cov_data) {
|
||||
cov_data_old = cov_data;
|
||||
cov_data = cov_data->next;
|
||||
free(cov_data_old);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
time_t delta_seconds = 0;
|
||||
bool found = false;
|
||||
char *filename = NULL;
|
||||
bool print_usage_terse = false;
|
||||
bool print_usage_verbose = false;
|
||||
BACNET_SUBSCRIBE_COV_DATA *cov_data = NULL;
|
||||
int argi = 0;
|
||||
int arg_remaining = 0;
|
||||
|
||||
if (argc < 6) {
|
||||
print_usage_terse = true;
|
||||
}
|
||||
if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) {
|
||||
print_usage_terse = true;
|
||||
print_usage_verbose = true;
|
||||
}
|
||||
if (print_usage_terse) {
|
||||
filename = filename_remove_path(argv[0]);
|
||||
printf("Usage: %s device-id object-type object-instance "
|
||||
"process-id <[un]confirmed lifetime|cancel>\r\n",
|
||||
filename);
|
||||
if (!print_usage_verbose) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (print_usage_verbose) {
|
||||
printf("\r\n"
|
||||
"device-id:\r\n"
|
||||
"The subscriber BACnet Device Object Instance number.\r\n"
|
||||
"\r\n"
|
||||
"object-type:\r\n"
|
||||
"The monitored object type is the integer value of the\r\n"
|
||||
"enumeration BACNET_OBJECT_TYPE in bacenum.h. For example,\r\n"
|
||||
"if you were monitoring Analog Output 2, the object-type\r\n"
|
||||
"would be 1.\r\n"
|
||||
"\r\n"
|
||||
"object-instance:\r\n"
|
||||
"The monitored object instance number.\r\n"
|
||||
"\r\n"
|
||||
"process-id:\r\n"
|
||||
"Process Identifier for this COV subscription.\r\n"
|
||||
"\r\n"
|
||||
"confirmed:\r\n"
|
||||
"Optional flag to subscribe using Confirmed notifications.\r\n"
|
||||
"Use the word \'confirmed\' or \'unconfirmed\'.\r\n"
|
||||
"\r\n"
|
||||
"lifetime:\r\n"
|
||||
"Optional subscription lifetime is conveyed in seconds.\r\n"
|
||||
"\r\n"
|
||||
"cancel:\r\n"
|
||||
"Use the word \'cancel\' instead of confirm and lifetime.\r\n"
|
||||
"This shall indicate a cancellation request.\r\n"
|
||||
"\r\n"
|
||||
"Example:\r\n"
|
||||
"If you want subscribe to Device 123 Analog Input 9 object\r\n"
|
||||
"using confirmed COV notifications for 5 minutes,\r\n"
|
||||
"you could send the following command:\r\n"
|
||||
"%s 123 0 9 1 confirmed 600\r\n"
|
||||
"To send the same COV subscription request for unconfirmed\r\n"
|
||||
"notifications, send the following command:\r\n"
|
||||
"%s 123 0 9 1 unconfirmed 600\r\n"
|
||||
"To cancel the same COV subscription request,\r\n"
|
||||
"send the following command:\r\n"
|
||||
"%s 123 0 9 1 cancel\r\n",
|
||||
filename, filename, filename);
|
||||
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;
|
||||
}
|
||||
atexit(cleanup);
|
||||
COV_Subscribe_Data = calloc(1, sizeof(BACNET_SUBSCRIBE_COV_DATA));
|
||||
cov_data = COV_Subscribe_Data;
|
||||
argi = 2;
|
||||
while (cov_data) {
|
||||
cov_data->monitoredObjectIdentifier.type = strtol(argv[argi], NULL, 0);
|
||||
if (cov_data->monitoredObjectIdentifier.type >=
|
||||
MAX_BACNET_OBJECT_TYPE) {
|
||||
fprintf(stderr, "object-type=%u - it must be less than %u\r\n",
|
||||
cov_data->monitoredObjectIdentifier.type,
|
||||
MAX_BACNET_OBJECT_TYPE);
|
||||
return 1;
|
||||
}
|
||||
argi++;
|
||||
cov_data->monitoredObjectIdentifier.instance =
|
||||
strtol(argv[argi], NULL, 0);
|
||||
if (cov_data->monitoredObjectIdentifier.instance >
|
||||
BACNET_MAX_INSTANCE) {
|
||||
fprintf(stderr, "object-instance=%u - it must be less than %u\r\n",
|
||||
cov_data->monitoredObjectIdentifier.instance,
|
||||
BACNET_MAX_INSTANCE + 1);
|
||||
return 1;
|
||||
}
|
||||
argi++;
|
||||
cov_data->subscriberProcessIdentifier = strtol(argv[argi], NULL, 0);
|
||||
argi++;
|
||||
if (strcmp(argv[argi], "cancel") == 0) {
|
||||
cov_data->cancellationRequest = true;
|
||||
argi++;
|
||||
} else {
|
||||
cov_data->cancellationRequest = false;
|
||||
if (strcmp(argv[argi], "confirmed") == 0) {
|
||||
cov_data->issueConfirmedNotifications = true;
|
||||
} else if (strcmp(argv[argi], "unconfirmed") == 0) {
|
||||
cov_data->issueConfirmedNotifications = false;
|
||||
} else {
|
||||
fprintf(stderr, "unknown option: %s\r\n", argv[argi]);
|
||||
return 1;
|
||||
}
|
||||
argi++;
|
||||
arg_remaining = argc - argi;
|
||||
if (arg_remaining > 0) {
|
||||
cov_data->lifetime = strtol(argv[argi], NULL, 0);
|
||||
argi++;
|
||||
} else {
|
||||
cov_data->lifetime = 0;
|
||||
}
|
||||
}
|
||||
arg_remaining = argc - argi;
|
||||
if (arg_remaining < 5) {
|
||||
break;
|
||||
} else {
|
||||
cov_data->next = calloc(1, sizeof(BACNET_SUBSCRIBE_COV_DATA));
|
||||
cov_data = cov_data->next;
|
||||
}
|
||||
}
|
||||
/* setup my info */
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
address_init();
|
||||
Init_Service_Handlers();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
timeout_seconds = (apdu_timeout() / 1000) * apdu_retries();
|
||||
/* try to bind with the device */
|
||||
found = address_bind_request(
|
||||
Target_Device_Object_Instance, &max_apdu, &Target_Address);
|
||||
if (!found) {
|
||||
Send_WhoIs(
|
||||
Target_Device_Object_Instance, Target_Device_Object_Instance);
|
||||
}
|
||||
/* start at the beginning of the subscribe list */
|
||||
cov_data = COV_Subscribe_Data;
|
||||
/* loop forever */
|
||||
for (;;) {
|
||||
/* increment timer - exit if timed out */
|
||||
current_seconds = time(NULL);
|
||||
/* at least one second has passed */
|
||||
if (current_seconds != last_seconds) {
|
||||
/* increment timer - exit if timed out */
|
||||
delta_seconds = current_seconds - last_seconds;
|
||||
elapsed_seconds += delta_seconds;
|
||||
tsm_timer_milliseconds((delta_seconds * 1000));
|
||||
/* keep track of time for next check */
|
||||
last_seconds = current_seconds;
|
||||
}
|
||||
if (Error_Detected) {
|
||||
break;
|
||||
}
|
||||
/* wait until the device is bound, or timeout and quit */
|
||||
if (!found) {
|
||||
found = address_bind_request(
|
||||
Target_Device_Object_Instance, &max_apdu, &Target_Address);
|
||||
}
|
||||
if (found) {
|
||||
if (Request_Invoke_ID == 0) {
|
||||
Simple_Ack_Detected = false;
|
||||
if (cov_data->cancellationRequest) {
|
||||
Cancel_Requested = true;
|
||||
} else {
|
||||
Cancel_Requested = false;
|
||||
}
|
||||
Target_Device_Process_Identifier =
|
||||
cov_data->subscriberProcessIdentifier;
|
||||
Request_Invoke_ID =
|
||||
Send_COV_Subscribe(Target_Device_Object_Instance, cov_data);
|
||||
if (!cov_data->cancellationRequest &&
|
||||
(timeout_seconds < cov_data->lifetime)) {
|
||||
/* increase the timeout to the longest lifetime */
|
||||
timeout_seconds = cov_data->lifetime;
|
||||
}
|
||||
printf("Sent SubscribeCOV request. "
|
||||
" Waiting up to %u seconds....\r\n",
|
||||
(unsigned)(timeout_seconds - elapsed_seconds));
|
||||
} else if (tsm_invoke_id_free(Request_Invoke_ID)) {
|
||||
if (cov_data->next) {
|
||||
cov_data = cov_data->next;
|
||||
Request_Invoke_ID = 0;
|
||||
} else {
|
||||
if (Cancel_Requested && Simple_Ack_Detected) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (tsm_invoke_id_failed(Request_Invoke_ID)) {
|
||||
fprintf(stderr, "\rError: TSM Timeout!\r\n");
|
||||
tsm_free_invoke_id(Request_Invoke_ID);
|
||||
Error_Detected = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* exit if timed out */
|
||||
if (elapsed_seconds > timeout_seconds) {
|
||||
Error_Detected = true;
|
||||
printf("\rError: APDU Timeout!\r\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
/* COV - so just wait until lifetime value expires */
|
||||
if (elapsed_seconds > timeout_seconds) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Error_Detected)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
set print pretty on
|
||||
set print union on
|
||||
set print address on
|
||||
list
|
||||
@@ -0,0 +1,71 @@
|
||||
#Makefile to build BACnet Application for the GCC port
|
||||
|
||||
# Executable file name
|
||||
TARGET = bacserv
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/device.c \
|
||||
$(BACNET_OBJECT_DIR)/ai.c \
|
||||
$(BACNET_OBJECT_DIR)/ao.c \
|
||||
$(BACNET_OBJECT_DIR)/av.c \
|
||||
$(BACNET_OBJECT_DIR)/bi.c \
|
||||
$(BACNET_OBJECT_DIR)/bo.c \
|
||||
$(BACNET_OBJECT_DIR)/bv.c \
|
||||
$(BACNET_OBJECT_DIR)/channel.c \
|
||||
$(BACNET_OBJECT_DIR)/command.c \
|
||||
$(BACNET_OBJECT_DIR)/csv.c \
|
||||
$(BACNET_OBJECT_DIR)/iv.c \
|
||||
$(BACNET_OBJECT_DIR)/lc.c \
|
||||
$(BACNET_OBJECT_DIR)/lo.c \
|
||||
$(BACNET_OBJECT_DIR)/lsp.c \
|
||||
$(BACNET_OBJECT_DIR)/ms-input.c \
|
||||
$(BACNET_OBJECT_DIR)/mso.c \
|
||||
$(BACNET_OBJECT_DIR)/msv.c \
|
||||
$(BACNET_OBJECT_DIR)/osv.c \
|
||||
$(BACNET_OBJECT_DIR)/piv.c \
|
||||
$(BACNET_OBJECT_DIR)/nc.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c \
|
||||
$(BACNET_OBJECT_DIR)/trendlog.c \
|
||||
$(BACNET_OBJECT_DIR)/schedule.c \
|
||||
$(BACNET_OBJECT_DIR)/access_credential.c \
|
||||
$(BACNET_OBJECT_DIR)/access_door.c \
|
||||
$(BACNET_OBJECT_DIR)/access_point.c \
|
||||
$(BACNET_OBJECT_DIR)/access_rights.c \
|
||||
$(BACNET_OBJECT_DIR)/access_user.c \
|
||||
$(BACNET_OBJECT_DIR)/access_zone.c \
|
||||
$(BACNET_OBJECT_DIR)/credential_data_input.c \
|
||||
$(BACNET_OBJECT_DIR)/bacfile.c
|
||||
|
||||
BACNET_BASIC_SRC += \
|
||||
$(wildcard $(BACNET_SRC_DIR)/bacnet/basic/service/*.c) \
|
||||
|
||||
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = $(SRC) $(BACNET_SRC) $(BACNET_BASIC_SRC) $(BACNET_PORT_SRC)
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,673 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<CodeBlocks_project_file>
|
||||
<FileVersion major="1" minor="6" />
|
||||
<Project>
|
||||
<Option title="BACnet Server Demo" />
|
||||
<Option pch_mode="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Build>
|
||||
<Target title="Debug">
|
||||
<Option output="bin/Debug/bacserv" prefix_auto="1" extension_auto="1" />
|
||||
<Option object_output="obj/Debug/" />
|
||||
<Option type="1" />
|
||||
<Option compiler="gcc" />
|
||||
<Compiler>
|
||||
<Add option="-Wall" />
|
||||
<Add option="-g" />
|
||||
<Add directory="../../ports/linux" />
|
||||
<Add directory="../../src" />
|
||||
</Compiler>
|
||||
</Target>
|
||||
<Target title="Release">
|
||||
<Option output="bin/Release/bacserv" prefix_auto="1" extension_auto="1" />
|
||||
<Option object_output="obj/Release/" />
|
||||
<Option type="1" />
|
||||
<Option compiler="gcc" />
|
||||
<Compiler>
|
||||
<Add option="-O2" />
|
||||
<Add option="-Wall" />
|
||||
<Add directory="." />
|
||||
<Add directory="../../ports/linux" />
|
||||
<Add directory="../../src" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add option="-s" />
|
||||
</Linker>
|
||||
</Target>
|
||||
</Build>
|
||||
<Compiler>
|
||||
<Add option="-O" />
|
||||
<Add option="-Wall" />
|
||||
<Add option="-fexceptions" />
|
||||
<Add option="-DBACDL_BIP" />
|
||||
<Add option="-DPRINT_ENABLED=1" />
|
||||
<Add option="-DBACAPP_ALL" />
|
||||
<Add directory="../../ports/linux" />
|
||||
<Add directory="../../src" />
|
||||
</Compiler>
|
||||
<Unit filename="../../ports/linux/bacport.h" />
|
||||
<Unit filename="../../ports/linux/bip-init.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../ports/linux/datetime-init.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../ports/linux/mstimer-init.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/abort.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/abort.h" />
|
||||
<Unit filename="../../src/bacnet/access_rule.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/access_rule.h" />
|
||||
<Unit filename="../../src/bacnet/alarm_ack.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/alarm_ack.h" />
|
||||
<Unit filename="../../src/bacnet/apdu.h" />
|
||||
<Unit filename="../../src/bacnet/arf.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/arf.h" />
|
||||
<Unit filename="../../src/bacnet/assigned_access_rights.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/assigned_access_rights.h" />
|
||||
<Unit filename="../../src/bacnet/authentication_factor.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/authentication_factor.h" />
|
||||
<Unit filename="../../src/bacnet/authentication_factor_format.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/authentication_factor_format.h" />
|
||||
<Unit filename="../../src/bacnet/awf.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/awf.h" />
|
||||
<Unit filename="../../src/bacnet/bacaddr.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacaddr.h" />
|
||||
<Unit filename="../../src/bacnet/bacapp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacapp.h" />
|
||||
<Unit filename="../../src/bacnet/bacdcode.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacdcode.h" />
|
||||
<Unit filename="../../src/bacnet/bacdef.h" />
|
||||
<Unit filename="../../src/bacnet/bacdevobjpropref.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacdevobjpropref.h" />
|
||||
<Unit filename="../../src/bacnet/bacenum.h" />
|
||||
<Unit filename="../../src/bacnet/bacerror.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacerror.h" />
|
||||
<Unit filename="../../src/bacnet/bacint.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacint.h" />
|
||||
<Unit filename="../../src/bacnet/bacprop.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacprop.h" />
|
||||
<Unit filename="../../src/bacnet/bacpropstates.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacpropstates.h" />
|
||||
<Unit filename="../../src/bacnet/bacreal.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacreal.h" />
|
||||
<Unit filename="../../src/bacnet/bacstr.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bacstr.h" />
|
||||
<Unit filename="../../src/bacnet/bactext.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bactext.h" />
|
||||
<Unit filename="../../src/bacnet/bactimevalue.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/bactimevalue.h" />
|
||||
<Unit filename="../../src/bacnet/basic/binding/address.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/binding/address.h" />
|
||||
<Unit filename="../../src/bacnet/basic/npdu/h_npdu.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/npdu/h_npdu.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/access_credential.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/access_credential.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/access_door.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/access_door.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/access_point.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/access_point.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/access_rights.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/access_rights.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/access_user.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/access_user.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/access_zone.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/access_zone.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/ai.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/ai.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/ao.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/ao.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/av.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/av.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/bacfile.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/bacfile.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/bi.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/bi.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/bo.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/bo.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/bv.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/bv.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/channel.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/channel.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/command.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/command.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/credential_data_input.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/credential_data_input.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/csv.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/csv.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/device.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/device.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/iv.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/iv.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/lc.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/lc.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/lo.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/lo.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/lsp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/lsp.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/ms-input.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/ms-input.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/mso.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/mso.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/msv.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/msv.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/nc.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/nc.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/netport.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/netport.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/objects.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/osv.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/osv.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/piv.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/piv.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/schedule.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/schedule.h" />
|
||||
<Unit filename="../../src/bacnet/basic/object/trendlog.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/object/trendlog.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_alarm_ack.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_alarm_ack.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_apdu.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_apdu.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_arf.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_arf.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_arf_a.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_arf_a.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_awf.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_awf.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_ccov.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_ccov.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_cov.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_cov.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_dcc.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_dcc.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_gas_a.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_gas_a.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_get_alarm_sum.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_get_alarm_sum.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_getevent.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_getevent.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_getevent_a.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_getevent_a.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_iam.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_iam.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_ihave.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_ihave.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_lso.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_lso.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_noserv.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_noserv.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_rd.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_rd.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_rp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_rp.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_rp_a.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_rp_a.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_rpm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_rpm.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_rpm_a.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_rpm_a.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_rr.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_rr.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_rr_a.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_rr_a.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_ts.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_ts.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_ucov.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_ucov.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_upt.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_upt.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_whohas.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_whohas.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_whois.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_whois.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_wp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_wp.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/h_wpm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/h_wpm.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_abort.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_abort.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_ack_alarm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_ack_alarm.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_arfs.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_arfs.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_awfs.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_awfs.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_cevent.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_cevent.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_cov.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_cov.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_dcc.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_dcc.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_error.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_error.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_get_alarm_sum.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_get_alarm_sum.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_get_event.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_get_event.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_getevent.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_getevent.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_iam.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_iam.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_ihave.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_ihave.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_lso.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_lso.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_rd.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_rd.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_readrange.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_readrange.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_rp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_rp.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_rpm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_rpm.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_ts.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_ts.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_uevent.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_uevent.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_upt.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_upt.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_whohas.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_whohas.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_whois.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_whois.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_wp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_wp.h" />
|
||||
<Unit filename="../../src/bacnet/basic/service/s_wpm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/service/s_wpm.h" />
|
||||
<Unit filename="../../src/bacnet/basic/services.h" />
|
||||
<Unit filename="../../src/bacnet/basic/sys/bigend.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/sys/bigend.h" />
|
||||
<Unit filename="../../src/bacnet/basic/sys/debug.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/sys/debug.h" />
|
||||
<Unit filename="../../src/bacnet/basic/sys/fifo.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/sys/fifo.h" />
|
||||
<Unit filename="../../src/bacnet/basic/sys/filename.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/sys/filename.h" />
|
||||
<Unit filename="../../src/bacnet/basic/sys/key.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/sys/key.h" />
|
||||
<Unit filename="../../src/bacnet/basic/sys/keylist.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/sys/keylist.h" />
|
||||
<Unit filename="../../src/bacnet/basic/sys/mstimer.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/sys/mstimer.h" />
|
||||
<Unit filename="../../src/bacnet/basic/sys/ringbuf.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/sys/ringbuf.h" />
|
||||
<Unit filename="../../src/bacnet/basic/sys/sbuf.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/sys/sbuf.h" />
|
||||
<Unit filename="../../src/bacnet/basic/tsm/tsm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/basic/tsm/tsm.h" />
|
||||
<Unit filename="../../src/bacnet/bits.h" />
|
||||
<Unit filename="../../src/bacnet/bytes.h" />
|
||||
<Unit filename="../../src/bacnet/config.h" />
|
||||
<Unit filename="../../src/bacnet/cov.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/cov.h" />
|
||||
<Unit filename="../../src/bacnet/credential_authentication_factor.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/credential_authentication_factor.h" />
|
||||
<Unit filename="../../src/bacnet/datalink/bip.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/datalink/bip.h" />
|
||||
<Unit filename="../../src/bacnet/datalink/bvlc.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/datalink/bvlc.h" />
|
||||
<Unit filename="../../src/bacnet/datalink/datalink.h" />
|
||||
<Unit filename="../../src/bacnet/datalink/dlenv.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/datalink/dlenv.h" />
|
||||
<Unit filename="../../src/bacnet/datetime.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/datetime.h" />
|
||||
<Unit filename="../../src/bacnet/dcc.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/dcc.h" />
|
||||
<Unit filename="../../src/bacnet/event.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/event.h" />
|
||||
<Unit filename="../../src/bacnet/get_alarm_sum.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/get_alarm_sum.h" />
|
||||
<Unit filename="../../src/bacnet/getevent.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/getevent.h" />
|
||||
<Unit filename="../../src/bacnet/iam.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/iam.h" />
|
||||
<Unit filename="../../src/bacnet/ihave.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/ihave.h" />
|
||||
<Unit filename="../../src/bacnet/indtext.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/indtext.h" />
|
||||
<Unit filename="../../src/bacnet/lighting.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/lighting.h" />
|
||||
<Unit filename="../../src/bacnet/lso.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/lso.h" />
|
||||
<Unit filename="../../src/bacnet/memcopy.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/memcopy.h" />
|
||||
<Unit filename="../../src/bacnet/npdu.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/npdu.h" />
|
||||
<Unit filename="../../src/bacnet/property.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/property.h" />
|
||||
<Unit filename="../../src/bacnet/proplist.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/proplist.h" />
|
||||
<Unit filename="../../src/bacnet/ptransfer.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/ptransfer.h" />
|
||||
<Unit filename="../../src/bacnet/rd.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/rd.h" />
|
||||
<Unit filename="../../src/bacnet/readrange.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/readrange.h" />
|
||||
<Unit filename="../../src/bacnet/reject.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/reject.h" />
|
||||
<Unit filename="../../src/bacnet/rp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/rp.h" />
|
||||
<Unit filename="../../src/bacnet/rpm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/rpm.h" />
|
||||
<Unit filename="../../src/bacnet/timestamp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/timestamp.h" />
|
||||
<Unit filename="../../src/bacnet/timesync.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/timesync.h" />
|
||||
<Unit filename="../../src/bacnet/version.h" />
|
||||
<Unit filename="../../src/bacnet/whohas.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/whohas.h" />
|
||||
<Unit filename="../../src/bacnet/whois.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/whois.h" />
|
||||
<Unit filename="../../src/bacnet/wp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/wp.h" />
|
||||
<Unit filename="../../src/bacnet/wpm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="../../src/bacnet/wpm.h" />
|
||||
<Unit filename="main.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Extensions>
|
||||
<code_completion />
|
||||
<envvars />
|
||||
<debugger />
|
||||
</Extensions>
|
||||
</Project>
|
||||
</CodeBlocks_project_file>
|
||||
@@ -0,0 +1,676 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<CodeBlocks_project_file>
|
||||
<FileVersion major="1" minor="6" />
|
||||
<Project>
|
||||
<Option title="BACnet Server Demo" />
|
||||
<Option pch_mode="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Build>
|
||||
<Target title="Debug">
|
||||
<Option output="bin\Debug\bacserv" prefix_auto="1" extension_auto="1" />
|
||||
<Option object_output="obj\Debug\" />
|
||||
<Option type="1" />
|
||||
<Option compiler="gcc" />
|
||||
<Compiler>
|
||||
<Add option="-Wall" />
|
||||
<Add option="-g" />
|
||||
<Add directory="..\..\ports\win32" />
|
||||
<Add directory="..\..\src" />
|
||||
</Compiler>
|
||||
</Target>
|
||||
<Target title="Release">
|
||||
<Option output="bin\Release\bacserv" prefix_auto="1" extension_auto="1" />
|
||||
<Option object_output="obj\Release\" />
|
||||
<Option type="1" />
|
||||
<Option compiler="gcc" />
|
||||
<Compiler>
|
||||
<Add option="-O2" />
|
||||
<Add option="-Wall" />
|
||||
<Add directory="..\..\ports\win32" />
|
||||
<Add directory="..\..\src" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add option="-s" />
|
||||
</Linker>
|
||||
</Target>
|
||||
</Build>
|
||||
<Compiler>
|
||||
<Add option="-O" />
|
||||
<Add option="-Wall" />
|
||||
<Add option="-fexceptions" />
|
||||
<Add option="-DBACDL_BIP" />
|
||||
<Add option="-DPRINT_ENABLED=1" />
|
||||
<Add option="-DBACAPP_ALL" />
|
||||
<Add directory="..\..\ports\win32" />
|
||||
<Add directory="..\..\src" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add library="ws2_32" />
|
||||
<Add library="iphlpapi" />
|
||||
</Linker>
|
||||
<Unit filename="..\..\ports\win32\bacport.h" />
|
||||
<Unit filename="..\..\ports\win32\bip-init.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\ports\win32\datetime-init.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\ports\win32\mstimer-init.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\abort.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\abort.h" />
|
||||
<Unit filename="..\..\src\bacnet\access_rule.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\access_rule.h" />
|
||||
<Unit filename="..\..\src\bacnet\alarm_ack.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\alarm_ack.h" />
|
||||
<Unit filename="..\..\src\bacnet\apdu.h" />
|
||||
<Unit filename="..\..\src\bacnet\arf.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\arf.h" />
|
||||
<Unit filename="..\..\src\bacnet\assigned_access_rights.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\assigned_access_rights.h" />
|
||||
<Unit filename="..\..\src\bacnet\authentication_factor.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\authentication_factor.h" />
|
||||
<Unit filename="..\..\src\bacnet\authentication_factor_format.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\authentication_factor_format.h" />
|
||||
<Unit filename="..\..\src\bacnet\awf.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\awf.h" />
|
||||
<Unit filename="..\..\src\bacnet\bacaddr.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\bacaddr.h" />
|
||||
<Unit filename="..\..\src\bacnet\bacapp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\bacapp.h" />
|
||||
<Unit filename="..\..\src\bacnet\bacdcode.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\bacdcode.h" />
|
||||
<Unit filename="..\..\src\bacnet\bacdef.h" />
|
||||
<Unit filename="..\..\src\bacnet\bacdevobjpropref.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\bacdevobjpropref.h" />
|
||||
<Unit filename="..\..\src\bacnet\bacenum.h" />
|
||||
<Unit filename="..\..\src\bacnet\bacerror.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\bacerror.h" />
|
||||
<Unit filename="..\..\src\bacnet\bacint.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\bacint.h" />
|
||||
<Unit filename="..\..\src\bacnet\bacprop.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\bacprop.h" />
|
||||
<Unit filename="..\..\src\bacnet\bacpropstates.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\bacpropstates.h" />
|
||||
<Unit filename="..\..\src\bacnet\bacreal.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\bacreal.h" />
|
||||
<Unit filename="..\..\src\bacnet\bacstr.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\bacstr.h" />
|
||||
<Unit filename="..\..\src\bacnet\bactext.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\bactext.h" />
|
||||
<Unit filename="..\..\src\bacnet\bactimevalue.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\bactimevalue.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\binding\address.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\binding\address.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\npdu\h_npdu.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\npdu\h_npdu.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\access_credential.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\access_credential.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\access_door.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\access_door.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\access_point.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\access_point.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\access_rights.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\access_rights.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\access_user.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\access_user.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\access_zone.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\access_zone.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\ai.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\ai.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\ao.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\ao.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\av.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\av.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\bacfile.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\bacfile.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\bi.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\bi.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\bo.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\bo.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\bv.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\bv.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\channel.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\channel.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\command.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\command.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\credential_data_input.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\credential_data_input.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\csv.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\csv.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\device.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\device.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\iv.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\iv.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\lc.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\lc.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\lo.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\lo.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\lsp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\lsp.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\ms-input.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\ms-input.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\mso.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\mso.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\msv.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\msv.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\nc.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\nc.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\netport.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\netport.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\objects.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\osv.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\osv.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\piv.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\piv.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\schedule.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\schedule.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\object\trendlog.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\object\trendlog.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_alarm_ack.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_alarm_ack.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_apdu.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_apdu.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_arf.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_arf.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_arf_a.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_arf_a.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_awf.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_awf.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_ccov.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_ccov.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_cov.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_cov.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_dcc.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_dcc.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_gas_a.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_gas_a.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_get_alarm_sum.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_get_alarm_sum.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_getevent.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_getevent.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_getevent_a.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_getevent_a.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_iam.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_iam.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_ihave.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_ihave.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_lso.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_lso.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_noserv.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_noserv.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_rd.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_rd.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_rp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_rp.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_rp_a.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_rp_a.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_rpm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_rpm.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_rpm_a.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_rpm_a.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_rr.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_rr.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_rr_a.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_rr_a.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_ts.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_ts.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_ucov.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_ucov.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_upt.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_upt.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_whohas.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_whohas.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_whois.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_whois.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_wp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_wp.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_wpm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\h_wpm.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_abort.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_abort.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_ack_alarm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_ack_alarm.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_arfs.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_arfs.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_awfs.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_awfs.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_cevent.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_cevent.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_cov.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_cov.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_dcc.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_dcc.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_error.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_error.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_get_alarm_sum.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_get_alarm_sum.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_get_event.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_get_event.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_getevent.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_getevent.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_iam.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_iam.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_ihave.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_ihave.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_lso.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_lso.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_rd.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_rd.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_readrange.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_readrange.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_rp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_rp.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_rpm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_rpm.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_ts.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_ts.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_uevent.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_uevent.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_upt.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_upt.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_whohas.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_whohas.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_whois.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_whois.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_wp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_wp.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_wpm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\service\s_wpm.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic/services.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\sys\bigend.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\sys\bigend.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\sys\debug.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\sys\debug.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\sys\fifo.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\sys\fifo.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\sys\filename.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\sys\filename.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\sys\key.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\sys\key.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\sys\keylist.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\sys\keylist.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\sys\mstimer.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\sys\mstimer.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\sys\ringbuf.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\sys\ringbuf.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\sys\sbuf.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\sys\sbuf.h" />
|
||||
<Unit filename="..\..\src\bacnet\basic\tsm\tsm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\basic\tsm\tsm.h" />
|
||||
<Unit filename="..\..\src\bacnet\bits.h" />
|
||||
<Unit filename="..\..\src\bacnet\bytes.h" />
|
||||
<Unit filename="..\..\src\bacnet\config.h" />
|
||||
<Unit filename="..\..\src\bacnet\cov.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\cov.h" />
|
||||
<Unit filename="..\..\src\bacnet\credential_authentication_factor.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\credential_authentication_factor.h" />
|
||||
<Unit filename="..\..\src\bacnet\datalink\bip.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\datalink\bip.h" />
|
||||
<Unit filename="..\..\src\bacnet\datalink\bvlc.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\datalink\bvlc.h" />
|
||||
<Unit filename="..\..\src\bacnet\datalink\datalink.h" />
|
||||
<Unit filename="..\..\src\bacnet\datalink\dlenv.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\datalink\dlenv.h" />
|
||||
<Unit filename="..\..\src\bacnet\datetime.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\datetime.h" />
|
||||
<Unit filename="..\..\src\bacnet\dcc.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\dcc.h" />
|
||||
<Unit filename="..\..\src\bacnet\event.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\event.h" />
|
||||
<Unit filename="..\..\src\bacnet\get_alarm_sum.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\get_alarm_sum.h" />
|
||||
<Unit filename="..\..\src\bacnet\getevent.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\getevent.h" />
|
||||
<Unit filename="..\..\src\bacnet\iam.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\iam.h" />
|
||||
<Unit filename="..\..\src\bacnet\ihave.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\ihave.h" />
|
||||
<Unit filename="..\..\src\bacnet\indtext.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\indtext.h" />
|
||||
<Unit filename="..\..\src\bacnet\lighting.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\lighting.h" />
|
||||
<Unit filename="..\..\src\bacnet\lso.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\lso.h" />
|
||||
<Unit filename="..\..\src\bacnet\memcopy.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\memcopy.h" />
|
||||
<Unit filename="..\..\src\bacnet\npdu.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\npdu.h" />
|
||||
<Unit filename="..\..\src\bacnet\property.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\property.h" />
|
||||
<Unit filename="..\..\src\bacnet\proplist.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\proplist.h" />
|
||||
<Unit filename="..\..\src\bacnet\ptransfer.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\ptransfer.h" />
|
||||
<Unit filename="..\..\src\bacnet\rd.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\rd.h" />
|
||||
<Unit filename="..\..\src\bacnet\readrange.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\readrange.h" />
|
||||
<Unit filename="..\..\src\bacnet\reject.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\reject.h" />
|
||||
<Unit filename="..\..\src\bacnet\rp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\rp.h" />
|
||||
<Unit filename="..\..\src\bacnet\rpm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\rpm.h" />
|
||||
<Unit filename="..\..\src\bacnet\timestamp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\timestamp.h" />
|
||||
<Unit filename="..\..\src\bacnet\timesync.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\timesync.h" />
|
||||
<Unit filename="..\..\src\bacnet\version.h" />
|
||||
<Unit filename="..\..\src\bacnet\whohas.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\whohas.h" />
|
||||
<Unit filename="..\..\src\bacnet\whois.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\whois.h" />
|
||||
<Unit filename="..\..\src\bacnet\wp.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\wp.h" />
|
||||
<Unit filename="..\..\src\bacnet\wpm.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="..\..\src\bacnet\wpm.h" />
|
||||
<Unit filename="main.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Extensions>
|
||||
<code_completion />
|
||||
<envvars />
|
||||
<debugger />
|
||||
</Extensions>
|
||||
</Project>
|
||||
</CodeBlocks_project_file>
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,759 @@
|
||||
PICS 0
|
||||
BACnet Protocol Implementation Conformance Statement
|
||||
|
||||
--
|
||||
--
|
||||
-- BACnet Stack Demo
|
||||
-- bacnet.sourceforge.net
|
||||
-- Author: Steve Karg
|
||||
--
|
||||
--
|
||||
|
||||
Vendor Name: "ASHRAE"
|
||||
Product Name: "SimpleServer"
|
||||
Product Model Number: "GNU"
|
||||
Product Description: "server"
|
||||
|
||||
BIBBs Supported:
|
||||
{
|
||||
-- The BIBBs may be any of:
|
||||
-- DS-RP-A
|
||||
DS-RP-B
|
||||
-- DS-RPM-A DS-RPM-B
|
||||
-- DS-RPC-A DS-RPC-B
|
||||
-- DS-WP-A
|
||||
DS-WP-B
|
||||
-- DS-WPM-A DS-WPM-B
|
||||
-- DS-COV-A DS-COV-B
|
||||
-- DS-COVP-A DS-COVP-B
|
||||
-- DS-COVU-A DS-COVU-B
|
||||
-- AE-N-A AE-N-I-B AE-N-E-B
|
||||
-- AE-ACK-A AE-ACK-B
|
||||
-- AE-ASUM-A AE-ASUM-B
|
||||
-- AE-ESUM-A AE-ESUM-B
|
||||
-- AE-INFO-A AE-INFO-B
|
||||
-- AE-LS-A AE-LS-B
|
||||
-- SCHED-A SCHED-I-B SCHED-E-B
|
||||
-- T-VMT-A T-VMT-I-B T-VMT-E-B
|
||||
-- T-ATR-A T-ATR-B
|
||||
-- DM-DDB-A
|
||||
DM-DDB-B
|
||||
-- DM-DOB-A
|
||||
DM-DOB-B
|
||||
-- DM-DCC-A
|
||||
DM-DCC-B
|
||||
-- DM-PT-A DM-PT-B
|
||||
-- DM-TM-A DM-TM-B
|
||||
-- DM-TS-A
|
||||
DM-TS-B
|
||||
-- DM-UTC-A
|
||||
DM-UTC-B
|
||||
-- DM-RD-A
|
||||
DM-RD-B
|
||||
-- DM-BR-A DM-BR-B
|
||||
-- DM-R-A DM-R-B
|
||||
-- DM-LM-A DM-LM-B
|
||||
-- DM-OCD-A DM-OCD-B
|
||||
-- DM-VT-A DM-VT-B
|
||||
-- NM-CE-A NM-CE-B
|
||||
-- NM-RC-A NM-RC-B
|
||||
}
|
||||
|
||||
BACnet Standard Application Services Supported:
|
||||
{
|
||||
-- AcknowledgeAlarm Initiate Execute
|
||||
-- ConfirmedCOVNotification Initiate Execute
|
||||
UnconfirmedCOVNotification Initiate
|
||||
-- ConfirmedEventNotification Initiate Execute
|
||||
-- UnconfirmedEventNotification Initiate Execute
|
||||
-- GetAlarmSummary Initiate Execute
|
||||
-- GetEnrollmentSummary Initiate Execute
|
||||
AtomicReadFile Initiate Execute
|
||||
-- AtomicWriteFile Initiate Execute
|
||||
-- AddListElement Initiate Execute
|
||||
-- RemoveListElement Initiate Execute
|
||||
-- CreateObject Initiate Execute
|
||||
-- DeleteObject Initiate Execute
|
||||
ReadProperty Initiate Execute
|
||||
-- ReadpropertyConditional Initiate Execute
|
||||
-- ReadPropertyMultiple Initiate Execute
|
||||
-- SubscribeCOV Initiate Execute
|
||||
WriteProperty Initiate Execute
|
||||
-- WritePropertyMultiple Initiate Execute
|
||||
DeviceCommunicationControl Initiate Execute
|
||||
-- ConfirmedPrivateTransfer Initiate Execute
|
||||
-- UnconfirmedPrivateTransfer Initiate Execute
|
||||
TimeSynchronization Initiate Execute
|
||||
Who-Has Initiate Execute
|
||||
I-Have Initiate
|
||||
Who-Is Initiate Execute
|
||||
I-Am Initiate
|
||||
-- VT-Open Initiate Execute
|
||||
-- VT-Close Initiate Execute
|
||||
-- VT-Data Initiate Execute
|
||||
-- ConfirmedTextMessage Initiate Execute
|
||||
-- UnconfirmedTextMessage Initiate Execute
|
||||
ReinitializeDevice Initiate Execute
|
||||
-- RequestKey Initiate Execute
|
||||
-- Authenticate Initiate Execute
|
||||
UTCTimeSynchronization Initiate Execute
|
||||
-- ReadRange Initiate Execute
|
||||
-- GetEventInformation Initiate Execute
|
||||
-- LifeSafetyOperation Initiate Execute
|
||||
-- SubscribeCOVProperty Initiate Execute
|
||||
-- RequestKey Initiate Execute
|
||||
-- Authenticate Initiate Execute
|
||||
}
|
||||
|
||||
Standard Object-Types Supported:
|
||||
{
|
||||
Analog Input
|
||||
Analog Output
|
||||
Analog Value
|
||||
-- Averaging Createable Deleteable
|
||||
Binary Input
|
||||
Binary Output
|
||||
Binary Value
|
||||
-- Calendar Createable Deleteable
|
||||
-- Command Createable Deleteable
|
||||
Device
|
||||
-- Event Enrollment Createable Deleteable
|
||||
File
|
||||
-- Group Createable Deleteable
|
||||
-- Loop Createable Deleteable
|
||||
-- Multi-state Input Createable Deleteable
|
||||
Multi-state Output
|
||||
-- Multi-state Value Createable Deleteable
|
||||
-- Notification Class Createable Deleteable
|
||||
-- Program Createable Deleteable
|
||||
-- Schedule Createable Deleteable
|
||||
Life Safety Point
|
||||
-- Life Safety Zone Createable Deleteable
|
||||
-- Trend Log Createable Deleteable
|
||||
Load Control
|
||||
}
|
||||
|
||||
Data Link Layer Option:
|
||||
{
|
||||
-- ISO 8802-3, 10BASE5
|
||||
-- ISO 8802-3, 10BASE2
|
||||
-- ISO 8802-3, 10BASET
|
||||
-- ISO 8802-3, Fiber
|
||||
-- ARCNET, coax star
|
||||
-- ARCNET, coax bus
|
||||
-- ARCNET, twisted pair star
|
||||
-- ARCNET, twisted pair bus
|
||||
-- ARCNET, fiber star
|
||||
-- MS/TP master. Baud rate(s): 9600
|
||||
-- MS/TP slave. Baud rate(s): 9600
|
||||
-- Point-To-Point. Modem, Baud rate(s): 14.4k
|
||||
-- Point-To-Point. Modem, Autobaud range: 9600 to 28.8k
|
||||
BACnet/IP, 'DIX' Ethernet
|
||||
-- BACnet/IP, PPP
|
||||
-- Other
|
||||
}
|
||||
|
||||
Character Sets Supported:
|
||||
{
|
||||
ANSI X3.4
|
||||
-- Other Character Sets not supported
|
||||
-- IBM/Microsoft DBCS
|
||||
-- JIS C 6226
|
||||
-- ISO 10646 (ICS-4)
|
||||
-- ISO 10646 (UCS2)
|
||||
}
|
||||
|
||||
Special Functionality:
|
||||
{
|
||||
Maximum APDU size in octets: 480 -- Arcnet Maximum 501 less NL Header
|
||||
-- Maximum APDU size in octets: 480
|
||||
-- Segmented Requests Supported, window size: 1
|
||||
-- Segmented Responses Supported, window size: 1
|
||||
-- Router
|
||||
}
|
||||
|
||||
List of Objects in test device:
|
||||
{
|
||||
{
|
||||
object-identifier: (device,123)
|
||||
object-name: "SimpleServer"
|
||||
object-type: device
|
||||
system-status: operational
|
||||
vendor-name: "ASHRAE"
|
||||
vendor-identifier: 0
|
||||
model-name: "GNU"
|
||||
firmware-revision: "1.0"
|
||||
application-software-version: "1.0"
|
||||
protocol-version: 1
|
||||
protocol-revision: 5
|
||||
protocol-conformance-class: 1
|
||||
protocol-services-supported: (F,F,F,F,F,F,T,F,F,F,F,F,T,F,F,T,F,T,F,F,T,F,F,F,F,F,F,F,F,F,F,F,T,T,T,F,T,F,F,F)
|
||||
protocol-object-types-supported: (T,T,T,T,T,T,F,F,T,F,T,F,F,F,T,F,F,F,F,F,F,T,F,F,F)
|
||||
max-apdu-length-accepted: 480
|
||||
segmentation-supported: no-segmentation
|
||||
apdu-timeout: 3000
|
||||
number-of-apdu-retries: 3
|
||||
device-address-binding: ?
|
||||
local-time: ?
|
||||
local-date: ?
|
||||
utc-offset: ?
|
||||
daylight-savings-status: ?
|
||||
database-revision: ?
|
||||
object-list: {
|
||||
(device,123),(analog-input,0),(analog-input,1),
|
||||
(analog-input,2),(analog-input,3),(analog-input,4),
|
||||
(analog-input,5),(analog-input,6),(analog-output,0),
|
||||
(analog-output,1),(analog-output,2),(analog-output,3),
|
||||
(analog-value,0),(analog-value,1),(analog-value,2),
|
||||
(analog-value,3),(binary-input,0),(binary-input,1),
|
||||
(binary-input,2),(binary-input,3),(binary-input,4),
|
||||
(binary-output,0),(binary-output,1),(binary-output,2),
|
||||
(binary-output,3),(binary-output,4),(binary-output,5),
|
||||
(binary-value,0),(binary-value,1),(life-safety-point,0),
|
||||
(life-safety-point,1),(life-safety-point,2),(life-safety-point,3),
|
||||
(life-safety-point,4),(life-safety-point,5),(life-safety-point,6),
|
||||
(multi-state-output,0),(multi-state-output,1),(multi-state-output,2),
|
||||
(multi-state-output,3),(file,0),(file,1),
|
||||
(file,2)
|
||||
}
|
||||
},
|
||||
{
|
||||
object-identifier: (analog-input,0)
|
||||
object-name: "ANALOG INPUT 0"
|
||||
object-type: analog-input
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
units: percent
|
||||
description: "ANALOG INPUT 0"
|
||||
},
|
||||
{
|
||||
object-identifier: (analog-input,1)
|
||||
object-name: "ANALOG INPUT 1"
|
||||
object-type: analog-input
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
units: percent
|
||||
description: "ANALOG INPUT 1"
|
||||
},
|
||||
{
|
||||
object-identifier: (analog-input,2)
|
||||
object-name: "ANALOG INPUT 2"
|
||||
object-type: analog-input
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
units: percent
|
||||
description: "ANALOG INPUT 2"
|
||||
},
|
||||
{
|
||||
object-identifier: (analog-input,3)
|
||||
object-name: "ANALOG INPUT 3"
|
||||
object-type: analog-input
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
units: percent
|
||||
description: "ANALOG INPUT 3"
|
||||
},
|
||||
{
|
||||
object-identifier: (analog-input,4)
|
||||
object-name: "ANALOG INPUT 4"
|
||||
object-type: analog-input
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
units: percent
|
||||
description: "ANALOG INPUT 4"
|
||||
},
|
||||
{
|
||||
object-identifier: (analog-input,5)
|
||||
object-name: "ANALOG INPUT 5"
|
||||
object-type: analog-input
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
units: percent
|
||||
description: "ANALOG INPUT 5"
|
||||
},
|
||||
{
|
||||
object-identifier: (analog-input,6)
|
||||
object-name: "ANALOG INPUT 6"
|
||||
object-type: analog-input
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
units: percent
|
||||
description: "ANALOG INPUT 6"
|
||||
},
|
||||
{
|
||||
object-identifier: (analog-output,0)
|
||||
object-name: "ANALOG OUTPUT 0"
|
||||
object-type: analog-output
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
units: percent
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: 0.000000
|
||||
description: "ANALOG OUTPUT 0"
|
||||
},
|
||||
{
|
||||
object-identifier: (analog-output,1)
|
||||
object-name: "ANALOG OUTPUT 1"
|
||||
object-type: analog-output
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
units: percent
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: 0.000000
|
||||
description: "ANALOG OUTPUT 1"
|
||||
},
|
||||
{
|
||||
object-identifier: (analog-output,2)
|
||||
object-name: "ANALOG OUTPUT 2"
|
||||
object-type: analog-output
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
units: percent
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: 0.000000
|
||||
description: "ANALOG OUTPUT 2"
|
||||
},
|
||||
{
|
||||
object-identifier: (analog-output,3)
|
||||
object-name: "ANALOG OUTPUT 3"
|
||||
object-type: analog-output
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
units: percent
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: 0.000000
|
||||
description: "ANALOG OUTPUT 3"
|
||||
},
|
||||
{
|
||||
object-identifier: (analog-value,0)
|
||||
object-name: "ANALOG VALUE 0"
|
||||
object-type: analog-value
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
units: percent
|
||||
relinquish-default: 0.000000
|
||||
description: "ANALOG VALUE 0"
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: 0.000000
|
||||
},
|
||||
{
|
||||
object-identifier: (analog-value,1)
|
||||
object-name: "ANALOG VALUE 1"
|
||||
object-type: analog-value
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
units: percent
|
||||
relinquish-default: 0.000000
|
||||
description: "ANALOG VALUE 1"
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: 0.000000
|
||||
},
|
||||
{
|
||||
object-identifier: (analog-value,2)
|
||||
object-name: "ANALOG VALUE 2"
|
||||
object-type: analog-value
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
units: percent
|
||||
relinquish-default: 0.000000
|
||||
description: "ANALOG VALUE 2"
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: 0.000000
|
||||
},
|
||||
{
|
||||
object-identifier: (analog-value,3)
|
||||
object-name: "ANALOG VALUE 3"
|
||||
object-type: analog-value
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
units: percent
|
||||
relinquish-default: 0.000000
|
||||
description: "ANALOG VALUE 3"
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: 0.000000
|
||||
},
|
||||
{
|
||||
object-identifier: (binary-input,0)
|
||||
object-name: "BINARY INPUT 0"
|
||||
object-type: binary-input
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
polarity: normal
|
||||
description: "BINARY INPUT 0"
|
||||
},
|
||||
{
|
||||
object-identifier: (binary-input,1)
|
||||
object-name: "BINARY INPUT 1"
|
||||
object-type: binary-input
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
polarity: normal
|
||||
description: "BINARY INPUT 1"
|
||||
},
|
||||
{
|
||||
object-identifier: (binary-input,2)
|
||||
object-name: "BINARY INPUT 2"
|
||||
object-type: binary-input
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
polarity: normal
|
||||
description: "BINARY INPUT 2"
|
||||
},
|
||||
{
|
||||
object-identifier: (binary-input,3)
|
||||
object-name: "BINARY INPUT 3"
|
||||
object-type: binary-input
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
polarity: normal
|
||||
description: "BINARY INPUT 3"
|
||||
},
|
||||
{
|
||||
object-identifier: (binary-input,4)
|
||||
object-name: "BINARY INPUT 4"
|
||||
object-type: binary-input
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
polarity: normal
|
||||
description: "BINARY INPUT 4"
|
||||
},
|
||||
{
|
||||
object-identifier: (binary-output,0)
|
||||
object-name: "BINARY OUTPUT 0"
|
||||
object-type: binary-output
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
polarity: normal
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: inactive
|
||||
description: "BINARY OUTPUT 0"
|
||||
},
|
||||
{
|
||||
object-identifier: (binary-output,1)
|
||||
object-name: "BINARY OUTPUT 1"
|
||||
object-type: binary-output
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
polarity: normal
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: inactive
|
||||
description: "BINARY OUTPUT 1"
|
||||
},
|
||||
{
|
||||
object-identifier: (binary-output,2)
|
||||
object-name: "BINARY OUTPUT 2"
|
||||
object-type: binary-output
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
polarity: normal
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: inactive
|
||||
description: "BINARY OUTPUT 2"
|
||||
},
|
||||
{
|
||||
object-identifier: (binary-output,3)
|
||||
object-name: "BINARY OUTPUT 3"
|
||||
object-type: binary-output
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
polarity: normal
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: inactive
|
||||
description: "BINARY OUTPUT 3"
|
||||
},
|
||||
{
|
||||
object-identifier: (binary-output,4)
|
||||
object-name: "BINARY OUTPUT 4"
|
||||
object-type: binary-output
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
polarity: normal
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: inactive
|
||||
description: "BINARY OUTPUT 4"
|
||||
},
|
||||
{
|
||||
object-identifier: (binary-output,5)
|
||||
object-name: "BINARY OUTPUT 5"
|
||||
object-type: binary-output
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
polarity: normal
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: inactive
|
||||
description: "BINARY OUTPUT 5"
|
||||
},
|
||||
{
|
||||
object-identifier: (binary-value,0)
|
||||
object-name: "BINARY VALUE 0"
|
||||
object-type: binary-value
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
description: "BINARY VALUE 0"
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: inactive
|
||||
},
|
||||
{
|
||||
object-identifier: (binary-value,1)
|
||||
object-name: "BINARY VALUE 1"
|
||||
object-type: binary-value
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
description: "BINARY VALUE 1"
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: inactive
|
||||
},
|
||||
{
|
||||
object-identifier: (life-safety-point,0)
|
||||
object-name: "LS POINT 0"
|
||||
object-type: life-safety-point
|
||||
description: "LS POINT 0"
|
||||
present-value: ?
|
||||
device-type: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
reliability: ?
|
||||
out-of-service: F
|
||||
mode: ? W
|
||||
silenced: ?
|
||||
operation-expected: ?
|
||||
},
|
||||
{
|
||||
object-identifier: (life-safety-point,1)
|
||||
object-name: "LS POINT 1"
|
||||
object-type: life-safety-point
|
||||
description: "LS POINT 1"
|
||||
present-value: ?
|
||||
device-type: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
reliability: ?
|
||||
out-of-service: F
|
||||
mode: ? W
|
||||
silenced: ?
|
||||
operation-expected: ?
|
||||
},
|
||||
{
|
||||
object-identifier: (life-safety-point,2)
|
||||
object-name: "LS POINT 2"
|
||||
object-type: life-safety-point
|
||||
description: "LS POINT 2"
|
||||
present-value: ?
|
||||
device-type: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
reliability: ?
|
||||
out-of-service: F
|
||||
mode: ? W
|
||||
silenced: ?
|
||||
operation-expected: ?
|
||||
},
|
||||
{
|
||||
object-identifier: (life-safety-point,3)
|
||||
object-name: "LS POINT 3"
|
||||
object-type: life-safety-point
|
||||
description: "LS POINT 3"
|
||||
present-value: ?
|
||||
device-type: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
reliability: ?
|
||||
out-of-service: F
|
||||
mode: ? W
|
||||
silenced: ?
|
||||
operation-expected: ?
|
||||
},
|
||||
{
|
||||
object-identifier: (life-safety-point,4)
|
||||
object-name: "LS POINT 4"
|
||||
object-type: life-safety-point
|
||||
description: "LS POINT 4"
|
||||
present-value: ?
|
||||
device-type: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
reliability: ?
|
||||
out-of-service: F
|
||||
mode: ? W
|
||||
silenced: ?
|
||||
operation-expected: ?
|
||||
},
|
||||
{
|
||||
object-identifier: (life-safety-point,5)
|
||||
object-name: "LS POINT 5"
|
||||
object-type: life-safety-point
|
||||
description: "LS POINT 5"
|
||||
present-value: ?
|
||||
device-type: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
reliability: ?
|
||||
out-of-service: F
|
||||
mode: ? W
|
||||
silenced: ?
|
||||
operation-expected: ?
|
||||
},
|
||||
{
|
||||
object-identifier: (life-safety-point,6)
|
||||
object-name: "LS POINT 6"
|
||||
object-type: life-safety-point
|
||||
description: "LS POINT 6"
|
||||
present-value: ?
|
||||
device-type: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
reliability: ?
|
||||
out-of-service: F
|
||||
mode: ? W
|
||||
silenced: ?
|
||||
operation-expected: ?
|
||||
},
|
||||
{
|
||||
object-identifier: (multi-state-output,0)
|
||||
object-name: "MULTISTATE OUTPUT 0"
|
||||
object-type: multi-state-output
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: inactive
|
||||
number-of-states: 254
|
||||
description: "MULTISTATE OUTPUT 0"
|
||||
},
|
||||
{
|
||||
object-identifier: (multi-state-output,1)
|
||||
object-name: "MULTISTATE OUTPUT 1"
|
||||
object-type: multi-state-output
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: inactive
|
||||
number-of-states: 254
|
||||
description: "MULTISTATE OUTPUT 1"
|
||||
},
|
||||
{
|
||||
object-identifier: (multi-state-output,2)
|
||||
object-name: "MULTISTATE OUTPUT 2"
|
||||
object-type: multi-state-output
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: inactive
|
||||
number-of-states: 254
|
||||
description: "MULTISTATE OUTPUT 2"
|
||||
},
|
||||
{
|
||||
object-identifier: (multi-state-output,3)
|
||||
object-name: "MULTISTATE OUTPUT 3"
|
||||
object-type: multi-state-output
|
||||
present-value: ?
|
||||
status-flags: (F,F,F,F)
|
||||
event-state: normal
|
||||
out-of-service: F
|
||||
priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?}
|
||||
relinquish-default: inactive
|
||||
number-of-states: 254
|
||||
description: "MULTISTATE OUTPUT 3"
|
||||
},
|
||||
{
|
||||
object-identifier: (file,0)
|
||||
object-name: "FILE 0"
|
||||
object-type: file
|
||||
file-type: "TEXT"
|
||||
file-size: 0
|
||||
modification-date: ?
|
||||
archive: ? W
|
||||
read-only: T
|
||||
file-access-method: stream-access
|
||||
description: "test.log"
|
||||
},
|
||||
{
|
||||
object-identifier: (file,1)
|
||||
object-name: "FILE 1"
|
||||
object-type: file
|
||||
file-type: "TEXT"
|
||||
file-size: 0
|
||||
modification-date: ?
|
||||
archive: ? W
|
||||
read-only: T
|
||||
file-access-method: stream-access
|
||||
description: "script.txt"
|
||||
},
|
||||
{
|
||||
object-identifier: (file,2)
|
||||
object-name: "FILE 2"
|
||||
object-type: file
|
||||
file-type: "TEXT"
|
||||
file-size: 39582
|
||||
modification-date: ?
|
||||
archive: ? W
|
||||
read-only: T
|
||||
file-access-method: stream-access
|
||||
description: "bacenum.h"
|
||||
}
|
||||
}
|
||||
|
||||
End of BACnet Protocol Implementation Conformance Statement
|
||||
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/dcc.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/getevent.h"
|
||||
#include "bacnet/version.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/datalink/dlenv.h"
|
||||
#include "bacnet/basic/sys/filename.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
/* include the device object */
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/object/lc.h"
|
||||
#include "bacnet/basic/object/trendlog.h"
|
||||
#if defined(INTRINSIC_REPORTING)
|
||||
#include "bacnet/basic/object/nc.h"
|
||||
#endif /* defined(INTRINSIC_REPORTING) */
|
||||
#if defined(BACFILE)
|
||||
#include "bacnet/basic/object/bacfile.h"
|
||||
#endif /* defined(BACFILE) */
|
||||
#if defined(BAC_UCI)
|
||||
#include "bacnet/basic/ucix/ucix.h"
|
||||
#endif /* defined(BAC_UCI) */
|
||||
|
||||
/** @file server/main.c Example server application using the BACnet Stack. */
|
||||
|
||||
/* (Doxygen note: The next two lines pull all the following Javadoc
|
||||
* into the ServerDemo module.) */
|
||||
/** @addtogroup ServerDemo */
|
||||
/*@{*/
|
||||
|
||||
/* current version of the BACnet stack */
|
||||
static const char *BACnet_Version = BACNET_VERSION_TEXT;
|
||||
|
||||
/** Buffer used for receiving */
|
||||
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
|
||||
|
||||
/** Initialize the handlers we will utilize.
|
||||
* @see Device_Init, apdu_set_unconfirmed_handler, apdu_set_confirmed_handler
|
||||
*/
|
||||
static void Init_Service_Handlers(void)
|
||||
{
|
||||
Device_Init(NULL);
|
||||
/* we need to handle who-is to support dynamic device binding */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has);
|
||||
|
||||
#if 0
|
||||
/* BACnet Testing Observed Incident oi00107
|
||||
Server only devices should not indicate that they EXECUTE I-Am
|
||||
Revealed by BACnet Test Client v1.8.16 ( www.bac-test.com/bacnet-test-client-download )
|
||||
BITS: BIT00040
|
||||
Any discussions can be directed to edward@bac-test.com
|
||||
Please feel free to remove this comment when my changes accepted after suitable time for
|
||||
review by all interested parties. Say 6 months -> September 2016 */
|
||||
/* In this demo, we are the server only ( BACnet "B" device ) so we do not indicate
|
||||
that we can execute the I-Am message */
|
||||
/* handle i-am to support binding to other devices */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind);
|
||||
#endif
|
||||
|
||||
/* 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);
|
||||
/* Set the handlers for any confirmed services that we support. */
|
||||
/* We must implement read property - it's required! */
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_READ_PROPERTY, handler_read_property);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_READ_PROP_MULTIPLE, handler_read_property_multiple);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_WRITE_PROPERTY, handler_write_property);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE, handler_write_property_multiple);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_READ_RANGE, handler_read_range);
|
||||
#if defined(BACFILE)
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_ATOMIC_READ_FILE, handler_atomic_read_file);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_ATOMIC_WRITE_FILE, handler_atomic_write_file);
|
||||
#endif
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_REINITIALIZE_DEVICE, handler_reinitialize_device);
|
||||
apdu_set_unconfirmed_handler(
|
||||
SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, handler_timesync_utc);
|
||||
apdu_set_unconfirmed_handler(
|
||||
SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, handler_timesync);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_SUBSCRIBE_COV, handler_cov_subscribe);
|
||||
apdu_set_unconfirmed_handler(
|
||||
SERVICE_UNCONFIRMED_COV_NOTIFICATION, handler_ucov_notification);
|
||||
/* handle communication so we can shutup when asked */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
|
||||
handler_device_communication_control);
|
||||
/* handle the data coming back from private requests */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_PRIVATE_TRANSFER,
|
||||
handler_unconfirmed_private_transfer);
|
||||
#if defined(INTRINSIC_REPORTING)
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM, handler_alarm_ack);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_GET_EVENT_INFORMATION, handler_get_event_information);
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_GET_ALARM_SUMMARY, handler_get_alarm_summary);
|
||||
#endif /* defined(INTRINSIC_REPORTING) */
|
||||
#if defined(BACNET_TIME_MASTER)
|
||||
handler_timesync_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void print_usage(const char *filename)
|
||||
{
|
||||
printf("Usage: %s [device-instance [device-name]]\n", filename);
|
||||
printf(" [--version][--help]\n");
|
||||
}
|
||||
|
||||
static void print_help(const char *filename)
|
||||
{
|
||||
printf("Simulate a BACnet server device\n"
|
||||
"device-instance:\n"
|
||||
"BACnet Device Object Instance number that you are\n"
|
||||
"trying simulate.\n"
|
||||
"device-name:\n"
|
||||
"The Device object-name is the text name for the device.\n"
|
||||
"\nExample:\n");
|
||||
printf("To simulate Device 123, use the following command:\n"
|
||||
"%s 123\n",
|
||||
filename);
|
||||
printf("To simulate Device 123 named Fred, use following command:\n"
|
||||
"%s 123 Fred\n",
|
||||
filename);
|
||||
}
|
||||
|
||||
/** Main function of server demo.
|
||||
*
|
||||
* @see Device_Set_Object_Instance_Number, dlenv_init, Send_I_Am,
|
||||
* datalink_receive, npdu_handler,
|
||||
* dcc_timer_seconds, bvlc_maintenance_timer,
|
||||
* Load_Control_State_Machine_Handler, handler_cov_task,
|
||||
* tsm_timer_milliseconds
|
||||
*
|
||||
* @param argc [in] Arg count.
|
||||
* @param argv [in] Takes one argument: the Device Instance #.
|
||||
* @return 0 on success.
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
BACNET_ADDRESS src = { 0 }; /* address where message came from */
|
||||
uint16_t pdu_len = 0;
|
||||
unsigned timeout = 1; /* milliseconds */
|
||||
time_t last_seconds = 0;
|
||||
time_t current_seconds = 0;
|
||||
uint32_t elapsed_seconds = 0;
|
||||
uint32_t elapsed_milliseconds = 0;
|
||||
uint32_t address_binding_tmr = 0;
|
||||
#if defined(INTRINSIC_REPORTING)
|
||||
uint32_t recipient_scan_tmr = 0;
|
||||
#endif
|
||||
#if defined(BACNET_TIME_MASTER)
|
||||
BACNET_DATE_TIME bdatetime;
|
||||
#endif
|
||||
#if defined(BAC_UCI)
|
||||
int uciId = 0;
|
||||
struct uci_context *ctx;
|
||||
#endif
|
||||
int argi = 0;
|
||||
const 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) 2014 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 defined(BAC_UCI)
|
||||
ctx = ucix_init("bacnet_dev");
|
||||
if (!ctx)
|
||||
fprintf(stderr, "Failed to load config file bacnet_dev\n");
|
||||
uciId = ucix_get_option_int(ctx, "bacnet_dev", "0", "Id", 0);
|
||||
printf("ID: %i", uciId);
|
||||
if (uciId != 0) {
|
||||
Device_Set_Object_Instance_Number(uciId);
|
||||
} else {
|
||||
#endif /* defined(BAC_UCI) */
|
||||
/* allow the device ID to be set */
|
||||
if (argc > 1) {
|
||||
Device_Set_Object_Instance_Number(strtol(argv[1], NULL, 0));
|
||||
}
|
||||
if (argc > 2) {
|
||||
Device_Object_Name_ANSI_Init(argv[2]);
|
||||
}
|
||||
#if defined(BAC_UCI)
|
||||
}
|
||||
ucix_cleanup(ctx);
|
||||
#endif /* defined(BAC_UCI) */
|
||||
|
||||
printf("BACnet Server Demo\n"
|
||||
"BACnet Stack Version %s\n"
|
||||
"BACnet Device ID: %u\n"
|
||||
"Max APDU: %d\n",
|
||||
BACnet_Version, Device_Object_Instance_Number(), MAX_APDU);
|
||||
/* load any static address bindings to show up
|
||||
in our device bindings list */
|
||||
address_init();
|
||||
Init_Service_Handlers();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
/* broadcast an I-Am on startup */
|
||||
Send_I_Am(&Handler_Transmit_Buffer[0]);
|
||||
/* loop forever */
|
||||
for (;;) {
|
||||
/* input */
|
||||
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 */
|
||||
elapsed_seconds = (uint32_t)(current_seconds - last_seconds);
|
||||
if (elapsed_seconds) {
|
||||
last_seconds = current_seconds;
|
||||
dcc_timer_seconds(elapsed_seconds);
|
||||
#if defined(BACDL_BIP) && BBMD_ENABLED
|
||||
bvlc_maintenance_timer(elapsed_seconds);
|
||||
#endif
|
||||
dlenv_maintenance_timer(elapsed_seconds);
|
||||
Load_Control_State_Machine_Handler();
|
||||
elapsed_milliseconds = elapsed_seconds * 1000;
|
||||
handler_cov_timer_seconds(elapsed_seconds);
|
||||
tsm_timer_milliseconds(elapsed_milliseconds);
|
||||
trend_log_timer(elapsed_seconds);
|
||||
#if defined(INTRINSIC_REPORTING)
|
||||
Device_local_reporting();
|
||||
#endif
|
||||
#if defined(BACNET_TIME_MASTER)
|
||||
Device_getCurrentDateTime(&bdatetime);
|
||||
handler_timesync_task(&bdatetime);
|
||||
#endif
|
||||
}
|
||||
handler_cov_task();
|
||||
/* scan cache address */
|
||||
address_binding_tmr += elapsed_seconds;
|
||||
if (address_binding_tmr >= 60) {
|
||||
address_cache_timer(address_binding_tmr);
|
||||
address_binding_tmr = 0;
|
||||
}
|
||||
#if defined(INTRINSIC_REPORTING)
|
||||
/* try to find addresses of recipients */
|
||||
recipient_scan_tmr += elapsed_seconds;
|
||||
if (recipient_scan_tmr >= NC_RESCAN_RECIPIENTS_SECS) {
|
||||
Notification_Class_find_recipient();
|
||||
recipient_scan_tmr = 0;
|
||||
}
|
||||
#endif
|
||||
/* output */
|
||||
|
||||
/* blink LEDs, Turn on or off outputs, etc */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* @} */
|
||||
|
||||
/* End group ServerDemo */
|
||||
@@ -0,0 +1,54 @@
|
||||
#Makefile to build BACnet Application for the Linux Port
|
||||
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
|
||||
# Executable file name
|
||||
TARGET = bacts
|
||||
# BACnet objects that are used with this app
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/client/device-client.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c
|
||||
BACNET_BASIC_SRC += \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_apdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_noserv.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_ts.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_whois.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_ts.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_whois.c
|
||||
|
||||
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = $(SRC) $(BACNET_SRC) $(BACNET_BASIC_SRC) $(BACNET_PORT_SRC)
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h> /* for time */
|
||||
#include <errno.h>
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacport.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/timesync.h"
|
||||
#include "bacnet/version.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/sys/filename.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/dlenv.h"
|
||||
|
||||
/* buffer used for receive */
|
||||
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
|
||||
/* error flag */
|
||||
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\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\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 in */
|
||||
apdu_set_unconfirmed_handler(
|
||||
SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, handler_timesync_utc);
|
||||
apdu_set_unconfirmed_handler(
|
||||
SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, handler_timesync);
|
||||
/* handle any errors coming back */
|
||||
apdu_set_abort_handler(MyAbortHandler);
|
||||
apdu_set_reject_handler(MyRejectHandler);
|
||||
}
|
||||
|
||||
static void print_usage(char *filename)
|
||||
{
|
||||
printf("Usage: %s [--dnet][--dadr][--mac]\n", filename);
|
||||
printf(" [--version][--help]\n");
|
||||
}
|
||||
|
||||
static void print_help(char *filename)
|
||||
{
|
||||
printf("Send BACnet TimeSynchronization request.\n"
|
||||
"\n"
|
||||
"--mac A\n"
|
||||
"BACnet mac address."
|
||||
"Valid ranges are from 0 to 255\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"
|
||||
"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"
|
||||
"BACnet mac address on the destination BACnet network number.\n"
|
||||
"Valid ranges are from 0 to 255\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("Examples:\n"
|
||||
"Send a TimeSynchronization request to DNET 123:\n"
|
||||
"%s --dnet 123\n",
|
||||
filename);
|
||||
printf(
|
||||
"Send a TimeSynchronization request to MAC 10.0.0.1 DNET 123 DADR 5:\n"
|
||||
"%s --mac 10.0.0.1 --dnet 123 --dadr 5\n",
|
||||
filename);
|
||||
printf("Send a TimeSynchronization request to MAC 10.1.2.3:47808:\n"
|
||||
"%s --mac 10.1.2.3:47808\n",
|
||||
filename);
|
||||
#if 0
|
||||
/* FIXME: it would be nice to be able to send arbitrary time values */
|
||||
"date format: year/month/day:dayofweek (e.g. 2006/4/1:6)\n"
|
||||
"year: AD, such as 2006\n" "month: 1=January, 12=December\n"
|
||||
"day: 1-31\n" "dayofweek: 1=Monday, 7=Sunday\n" "\n"
|
||||
"time format: hour:minute:second.hundredths (e.g. 23:59:59.12)\n"
|
||||
"hour: 0-23\n" "minute: 0-59\n" "second: 0-59\n"
|
||||
"hundredths: 0-99\n" "\n"
|
||||
"Optional device-instance sends a unicast time sync.\n",
|
||||
filename);
|
||||
#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 elapsed_seconds = 0;
|
||||
time_t last_seconds = 0;
|
||||
time_t current_seconds = 0;
|
||||
time_t timeout_seconds = 0;
|
||||
time_t rawtime;
|
||||
struct tm *my_time;
|
||||
BACNET_DATE bdate;
|
||||
BACNET_TIME btime;
|
||||
long dnet = -1;
|
||||
BACNET_MAC_ADDRESS mac = { 0 };
|
||||
BACNET_MAC_ADDRESS adr = { 0 };
|
||||
BACNET_ADDRESS dest = { 0 };
|
||||
bool global_broadcast = true;
|
||||
int argi = 0;
|
||||
char *filename = NULL;
|
||||
|
||||
/* decode any command line parameters */
|
||||
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) 2014 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])) {
|
||||
global_broadcast = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (strcmp(argv[argi], "--dnet") == 0) {
|
||||
if (++argi < argc) {
|
||||
dnet = strtol(argv[argi], NULL, 0);
|
||||
if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) {
|
||||
global_broadcast = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (strcmp(argv[argi], "--dadr") == 0) {
|
||||
if (++argi < argc) {
|
||||
if (address_mac_from_ascii(&adr, argv[argi])) {
|
||||
global_broadcast = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (global_broadcast) {
|
||||
datalink_get_broadcast_address(&dest);
|
||||
} else {
|
||||
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();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
timeout_seconds = apdu_timeout() / 1000;
|
||||
/* send the request */
|
||||
time(&rawtime);
|
||||
my_time = localtime(&rawtime);
|
||||
bdate.year = my_time->tm_year + 1900;
|
||||
bdate.month = my_time->tm_mon + 1;
|
||||
bdate.day = my_time->tm_mday;
|
||||
bdate.wday = my_time->tm_wday ? my_time->tm_wday : 7;
|
||||
btime.hour = my_time->tm_hour;
|
||||
btime.min = my_time->tm_min;
|
||||
btime.sec = my_time->tm_sec;
|
||||
btime.hundredths = 0;
|
||||
Send_TimeSync_Remote(&dest, &bdate, &btime);
|
||||
/* loop forever - not necessary for time sync, but we can watch */
|
||||
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 > timeout_seconds) {
|
||||
break;
|
||||
}
|
||||
/* keep track of time for next check */
|
||||
last_seconds = current_seconds;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#Makefile to build BACnet Application for the Linux Port
|
||||
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
|
||||
# Executable file name
|
||||
TARGET = bacucov
|
||||
# BACnet objects that are used with this app
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/client/device-client.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c
|
||||
BACNET_BASIC_SRC += \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_apdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_noserv.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_whois.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_cov.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_whois.c
|
||||
|
||||
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = $(SRC) $(BACNET_SRC) $(BACNET_BASIC_SRC) $(BACNET_PORT_SRC)
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h> /* for time */
|
||||
#include <errno.h>
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/cov.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacport.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/whois.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/sys/filename.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/dlenv.h"
|
||||
|
||||
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);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *value_string = NULL;
|
||||
bool status = false;
|
||||
BACNET_COV_DATA cov_data;
|
||||
BACNET_PROPERTY_VALUE value_list;
|
||||
uint8_t tag;
|
||||
|
||||
if (argc < 7) {
|
||||
/* note: priority 16 and 0 should produce the same end results... */
|
||||
printf(
|
||||
"Usage: %s pid device-id object-type object-instance "
|
||||
"time property tag value [priority] [index]\r\n"
|
||||
"\r\n"
|
||||
"pid:\r\n"
|
||||
"Process Identifier for this broadcast.\r\n"
|
||||
"\r\n"
|
||||
"device-id:\r\n"
|
||||
"The Initiating BACnet Device Object Instance number.\r\n"
|
||||
"\r\n"
|
||||
"object-type:\r\n"
|
||||
"The monitored object type is the integer value of the\r\n"
|
||||
"enumeration BACNET_OBJECT_TYPE in bacenum.h. For example,\r\n"
|
||||
"if you were monitoring Analog Output 2, the object-type\r\n"
|
||||
"would be 1.\r\n"
|
||||
"\r\n"
|
||||
"object-instance:\r\n"
|
||||
"The monitored object instance number.\r\n"
|
||||
"\r\n"
|
||||
"time:\r\n"
|
||||
"The subscription time remaining is conveyed in seconds.\r\n"
|
||||
"\r\n"
|
||||
"property:\r\n"
|
||||
"The property is an integer value of the enumeration \r\n"
|
||||
"BACNET_PROPERTY_ID in bacenum.h. For example, if you were\r\n"
|
||||
"monitoring the Present Value property, you would use 85\r\n"
|
||||
"as the property.\r\n"
|
||||
"\r\n"
|
||||
"tag:\r\n"
|
||||
"Tag is the integer value of the enumeration "
|
||||
"BACNET_APPLICATION_TAG \r\n"
|
||||
"in bacenum.h. It is the data type of the value that you are\r\n"
|
||||
"monitoring. For example, if you were monitoring a REAL value, "
|
||||
"you would \r\n"
|
||||
"use a tag of 4."
|
||||
"\r\n"
|
||||
"value:\r\n"
|
||||
"The value is an ASCII representation of some type of data that "
|
||||
"you\r\n"
|
||||
"are monitoring. It is encoded using the tag information "
|
||||
"provided. For\r\n"
|
||||
"example, if you were writing a REAL value of 100.0, you would use "
|
||||
"\r\n"
|
||||
"100.0 as the value.\r\n"
|
||||
"\r\n"
|
||||
"[priority]:\r\n"
|
||||
"This optional parameter is used for reporting the priority of "
|
||||
"the\r\n"
|
||||
"value. If no priority is given, none is sent, and the BACnet \r\n"
|
||||
"standard requires that the value is reported at the lowest \r\n"
|
||||
"priority (16) if the object property supports priorities.\r\n"
|
||||
"\r\n"
|
||||
"[index]\r\n"
|
||||
"This optional integer parameter is the index number of an "
|
||||
"array.\r\n"
|
||||
"If the property is an array, individual elements can be "
|
||||
"reported.\r\n"
|
||||
"\r\n"
|
||||
"Here is a brief overview of BACnet property and tags:\r\n"
|
||||
"Certain properties are expected to be written with certain \r\n"
|
||||
"application tags, so you probably need to know which ones to "
|
||||
"use\r\n"
|
||||
"with each property of each object. It is almost safe to say "
|
||||
"that\r\n"
|
||||
"given a property and an object and a table, the tag could be "
|
||||
"looked\r\n"
|
||||
"up automatically. There may be a few exceptions to this, such "
|
||||
"as\r\n"
|
||||
"the Any property type in the schedule object and the Present "
|
||||
"Value\r\n"
|
||||
"accepting REAL, BOOLEAN, NULL, etc. Perhaps it would be simpler "
|
||||
"for\r\n"
|
||||
"the demo to use this kind of table - but I also wanted to be "
|
||||
"able\r\n"
|
||||
"to do negative testing by passing the wrong tag and have the "
|
||||
"server\r\n"
|
||||
"return a reject message.\r\n"
|
||||
"\r\n"
|
||||
"Example:\r\n"
|
||||
"If you want generate an unconfirmed COV,\r\n"
|
||||
"you could send the following command:\r\n"
|
||||
"%s 1 2 3 4 5 85 4 100.0\r\n"
|
||||
"where 1=pid, 2=device-id, 3=AV, 4=object-id, 5=time,\r\n"
|
||||
"85=Present-Value, 4=REAL, 100.0=value\r\n",
|
||||
filename_remove_path(argv[0]), filename_remove_path(argv[0]));
|
||||
return 0;
|
||||
}
|
||||
/* decode the command line parameters */
|
||||
cov_data.subscriberProcessIdentifier = strtol(argv[1], NULL, 0);
|
||||
cov_data.initiatingDeviceIdentifier = strtol(argv[2], NULL, 0);
|
||||
cov_data.monitoredObjectIdentifier.type = strtol(argv[3], NULL, 0);
|
||||
cov_data.monitoredObjectIdentifier.instance = strtol(argv[4], NULL, 0);
|
||||
cov_data.timeRemaining = strtol(argv[5], NULL, 0);
|
||||
cov_data.listOfValues = &value_list;
|
||||
value_list.next = NULL;
|
||||
value_list.propertyIdentifier = strtol(argv[6], NULL, 0);
|
||||
tag = strtol(argv[7], NULL, 0);
|
||||
value_string = argv[8];
|
||||
/* optional priority */
|
||||
if (argc > 9)
|
||||
value_list.priority = strtol(argv[9], NULL, 0);
|
||||
else
|
||||
value_list.priority = BACNET_NO_PRIORITY;
|
||||
/* optional index */
|
||||
if (argc > 10)
|
||||
value_list.propertyArrayIndex = strtol(argv[10], NULL, 0);
|
||||
else
|
||||
value_list.propertyArrayIndex = BACNET_ARRAY_ALL;
|
||||
|
||||
if (cov_data.initiatingDeviceIdentifier >= BACNET_MAX_INSTANCE) {
|
||||
fprintf(stderr, "device-instance=%u - it must be less than %u\r\n",
|
||||
cov_data.initiatingDeviceIdentifier, BACNET_MAX_INSTANCE);
|
||||
return 1;
|
||||
}
|
||||
if (cov_data.monitoredObjectIdentifier.type >= MAX_BACNET_OBJECT_TYPE) {
|
||||
fprintf(stderr, "object-type=%u - it must be less than %u\r\n",
|
||||
cov_data.monitoredObjectIdentifier.type, MAX_BACNET_OBJECT_TYPE);
|
||||
return 1;
|
||||
}
|
||||
if (cov_data.monitoredObjectIdentifier.instance > BACNET_MAX_INSTANCE) {
|
||||
fprintf(stderr, "object-instance=%u - it must be less than %u\r\n",
|
||||
cov_data.monitoredObjectIdentifier.instance,
|
||||
BACNET_MAX_INSTANCE + 1);
|
||||
return 1;
|
||||
}
|
||||
if (cov_data.listOfValues->propertyIdentifier > MAX_BACNET_PROPERTY_ID) {
|
||||
fprintf(stderr, "property-identifier=%u - it must be less than %u\r\n",
|
||||
cov_data.listOfValues->propertyIdentifier,
|
||||
MAX_BACNET_PROPERTY_ID + 1);
|
||||
return 1;
|
||||
}
|
||||
if (tag >= MAX_BACNET_APPLICATION_TAG) {
|
||||
fprintf(stderr, "tag=%u - it must be less than %u\r\n", tag,
|
||||
MAX_BACNET_APPLICATION_TAG);
|
||||
return 1;
|
||||
}
|
||||
status = bacapp_parse_application_data(
|
||||
tag, value_string, &cov_data.listOfValues->value);
|
||||
if (!status) {
|
||||
/* FIXME: show the expected entry format for the tag */
|
||||
fprintf(stderr, "unable to parse the tag value\r\n");
|
||||
return 1;
|
||||
}
|
||||
/* setup my info */
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
Init_Service_Handlers();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
Send_UCOV_Notify(&Handler_Transmit_Buffer[0],
|
||||
sizeof(Handler_Transmit_Buffer), &cov_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#Makefile to build BACnet Application with GNU Make
|
||||
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
|
||||
# Executable file name
|
||||
TARGET = bacuevent
|
||||
# BACnet objects that are used with this app
|
||||
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
|
||||
SRC = main.c \
|
||||
$(BACNET_OBJECT_DIR)/client/device-client.c \
|
||||
$(BACNET_OBJECT_DIR)/netport.c
|
||||
BACNET_BASIC_SRC += \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_apdu.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_noserv.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rp.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_rr_a.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/h_whois.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_iam.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_uevent.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/basic/service/s_whois.c
|
||||
|
||||
# TARGET_EXT is defined in apps/Makefile as .exe or nothing
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = $(SRC) $(BACNET_SRC) $(BACNET_BASIC_SRC) $(BACNET_PORT_SRC)
|
||||
|
||||
OBJS += ${SRCS:.c=.o}
|
||||
|
||||
.PHONY: all
|
||||
all: Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
.PHONY: depend
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
|
||||
|
||||
.PHONY: include
|
||||
include: .depend
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user