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:
Steve Karg
2019-12-13 15:19:10 -06:00
committed by GitHub
parent 8a38dbe2cf
commit d50c190957
912 changed files with 36206 additions and 52502 deletions
+275
View File
@@ -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 $@
+50
View File
@@ -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
+238
View File
@@ -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;
}
+50
View File
@@ -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
View File
@@ -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;
}
+50
View File
@@ -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
+305
View File
@@ -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>
+104
View 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
View File
File diff suppressed because it is too large Load Diff
+47
View File
@@ -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
+246
View File
@@ -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;
}
+72
View File
@@ -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
+48
View File
@@ -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_ */
+338
View File
@@ -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 */
+50
View File
@@ -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
+289
View File
@@ -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;
}
+47
View File
@@ -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
View File
@@ -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;
}
+47
View File
@@ -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
+173
View File
@@ -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;
}
+47
View File
@@ -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
+384
View File
@@ -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;
}
+70
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+211
View File
@@ -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
+51
View File
@@ -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
+9
View File
@@ -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
+335
View File
@@ -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;
}
+20
View File
@@ -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
+348
View File
@@ -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 &quot;hello world&quot;</p>
<pre>
Log(&quot;Hello World&quot;);</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 &quot;hello&quot;, but not &quot;world&quot;</p>
<pre>
Log(&quot;Hello&quot;);
SilenceLog(1);
Log(&quot;World&quot;);</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(
\&amp;ReadProperty, [$deviceInstance, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE'],
42, $maxRetries, $retryDelay
);
if ($isFailure)
{
die &quot;Value was not 42. Last response was '$resp'&quot;;
}</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(
\&amp;WriteProperty, [$deviceInstance, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE', 'BACNET_APPLICATION_TAG_REAL', 42.0],
&quot;Acknowledged&quot;, $maxRetries, $retryDelay
);
if ($isFailure)
{
die &quot;Could not write 42. Last response was '$resp'&quot;;
}</pre>
</body>
</html>
File diff suppressed because one or more lines are too long
+75
View File
@@ -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;
}
+137
View File
@@ -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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\n/g, "<BR>").replace(/\t/g, " "); //tachyon
while (/\s\s/.test(s))
s = s.replace(/\s\s/, "&nbsp; ");
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);
});
});
+869
View File
@@ -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;
}
+53
View File
@@ -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;
+933
View File
@@ -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;
}
+20
View File
@@ -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
+223
View File
@@ -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/*)
+13
View File
@@ -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
+1679
View File
File diff suppressed because it is too large Load Diff
+281
View File
@@ -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 */
+10
View File
@@ -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
+45
View File
@@ -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
+283
View File
@@ -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;
}
+52
View File
@@ -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
+221
View File
@@ -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
+52
View File
@@ -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
+401
View File
@@ -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;
}
+20
View File
@@ -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
+123
View File
@@ -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;
}
+51
View File
@@ -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
+47
View File
@@ -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
+194
View File
@@ -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;
}
+52
View File
@@ -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
+392
View File
@@ -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.
+50
View File
@@ -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
+58
View File
@@ -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>
+418
View 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;
}
+56
View File
@@ -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
+55
View File
@@ -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>
+479
View 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;
}
+50
View File
@@ -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
+381
View File
@@ -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;
}
+20
View File
@@ -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
+53
View File
@@ -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
+246
View File
@@ -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;
}
+78
View File
@@ -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
+22
View File
@@ -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
+63
View File
@@ -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
+68
View File
@@ -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;
}
);
+392
View File
@@ -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;
}
+78
View File
@@ -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 */
+857
View File
@@ -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 *)&param);
if (result) {
current->params.bip_params.port = param;
} else {
current->params.bip_params.port = 0xBAC0;
}
result =
config_setting_lookup_int(port, "network", (int *)&param);
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 *)&param);
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 *)&param);
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 *)&param);
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 *)&param);
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 *)&param);
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 *)&param);
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 *)&param);
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;
}
+103
View File
@@ -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);
}
+98
View File
@@ -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 */
+174
View File
@@ -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;
}
+37
View File
@@ -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 */
+359
View File
@@ -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;
}
}
+65
View File
@@ -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 */
+121
View File
@@ -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;
}
}
+138
View File
@@ -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 */
+149
View File
@@ -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
+51
View File
@@ -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
+417
View File
@@ -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;
}
+4
View File
@@ -0,0 +1,4 @@
set print pretty on
set print union on
set print address on
list
+71
View File
@@ -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.
+673
View File
@@ -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>
+676
View 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>
BIN
View File
Binary file not shown.
+759
View File
@@ -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
+320
View File
@@ -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 */
+54
View File
@@ -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
+286
View File
@@ -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;
}
+53
View File
@@ -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
+231
View File
@@ -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;
}
+54
View File
@@ -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