adjust root folder
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<CodeBlocks_workspace_file>
|
||||
<Workspace title="Workspace">
|
||||
<Project filename="..\lib\bacnet.cbp" />
|
||||
<Project filename="server\bacserv.cbp" />
|
||||
<Project filename="epics\bacepics.cbp" />
|
||||
<Project filename="readprop\bacrp.cbp" />
|
||||
<Project filename="..\ports\win32\bacnet.cbp" />
|
||||
<Project filename="..\ports\win32\rs485.cbp" active="1" />
|
||||
</Workspace>
|
||||
</CodeBlocks_workspace_file>
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
# 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
|
||||
|
||||
# Directories
|
||||
BACNET_PORT ?= linux
|
||||
BACNET_PORT_DIR = ../../ports/${BACNET_PORT}
|
||||
BACNET_INCLUDE = ../../include
|
||||
BACNET_OBJECT = ../../demo/object
|
||||
BACNET_HANDLER = ../../demo/handler
|
||||
# BACnet Library
|
||||
BACNET_LIB_DIR = ../../lib
|
||||
BACNET_LIB_NAME = bacnet
|
||||
BACNET_LIB_TARGET = $(BACNET_LIB_DIR)/lib$(BACNET_LIB_NAME).a
|
||||
# Compiler Setup
|
||||
INCLUDE1 = -I$(BACNET_PORT_DIR) -I$(BACNET_OBJECT) -I$(BACNET_HANDLER)
|
||||
INCLUDE2 = -I$(BACNET_INCLUDE)
|
||||
INCLUDES = $(INCLUDE1) $(INCLUDE2)
|
||||
BACNET_LIB := -L$(BACNET_LIB_DIR),-l$(BACNET_LIB_NAME)
|
||||
# build for UCI
|
||||
ifneq (,$(findstring -DBAC_UCI,$(BACNET_DEFINES)))
|
||||
BACNET_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
|
||||
DEFINES += -D_NO_OLDNAMES
|
||||
endif
|
||||
#build for release (default) or debug
|
||||
DEBUGGING =
|
||||
OPTIMIZATION = -Os
|
||||
ifeq (${BUILD},debug)
|
||||
OPTIMIZATION = -O0
|
||||
DEBUGGING = -g -DDEBUG_ENABLED=1
|
||||
endif
|
||||
# put all the flags together
|
||||
CFLAGS := -Wall $(DEBUGGING) $(OPTIMIZATION) $(INCLUDES) $(DEFINES)
|
||||
LFLAGS := -Wl,$(BACNET_LIB),$(SYSTEM_LIB)
|
||||
|
||||
.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 += ptransfer mstpcap mstpcrc
|
||||
endif
|
||||
|
||||
.PHONY : all gateway router clean
|
||||
|
||||
TARGETS = all clean
|
||||
|
||||
$(TARGETS): %: $(patsubst %, %.%, $(SUBDIRS))
|
||||
|
||||
$(foreach TGT, $(TARGETS), $(patsubst %, %.$(TGT), $(SUBDIRS))):
|
||||
$(MAKE) -s -b -C $(subst ., , $@)
|
||||
|
||||
gateway:
|
||||
$(MAKE) -s -b -C gateway
|
||||
|
||||
server:
|
||||
$(MAKE) -b -C server
|
||||
|
||||
mstpcap:
|
||||
$(MAKE) -b -C mstpcap
|
||||
|
||||
mstpcrc:
|
||||
$(MAKE) -b -C mstpcrc
|
||||
|
||||
iam:
|
||||
$(MAKE) -b -C iam
|
||||
|
||||
uevent:
|
||||
$(MAKE) -b -C uevent
|
||||
|
||||
abort:
|
||||
$(MAKE) -b -C abort
|
||||
|
||||
error:
|
||||
$(MAKE) -b -C error
|
||||
|
||||
router:
|
||||
$(MAKE) -s -b -C router
|
||||
|
||||
router-ipv6:
|
||||
$(MAKE) -b -C router-ipv6
|
||||
|
||||
writepropm:
|
||||
$(MAKE) -b -C writepropm
|
||||
@@ -0,0 +1,39 @@
|
||||
#Makefile to build BACnet Application for the Linux Port
|
||||
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
|
||||
TARGET = bacabort
|
||||
|
||||
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 -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map
|
||||
|
||||
include: .depend
|
||||
@@ -0,0 +1,249 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "bactext.h"
|
||||
#include "iam.h"
|
||||
#include "address.h"
|
||||
#include "config.h"
|
||||
#include "bacdef.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "version.h"
|
||||
/* some demo stuff needed */
|
||||
#include "filename.h"
|
||||
#include "handlers.h"
|
||||
#include "client.h"
|
||||
#include "txbuf.h"
|
||||
#include "dlenv.h"
|
||||
|
||||
/* parsed command line parameters */
|
||||
static 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;
|
||||
|
||||
void MyAbortHandler(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id,
|
||||
uint8_t abort_reason,
|
||||
bool server)
|
||||
{
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
(void) server;
|
||||
printf("BACnet Abort: %s\n", bactext_abort_reason_name(abort_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
void MyRejectHandler(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id,
|
||||
uint8_t reject_reason)
|
||||
{
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
printf("BACnet Reject: %s\n", bactext_reject_reason_name(reject_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
static void Init_Service_Handlers(
|
||||
void)
|
||||
{
|
||||
Device_Init(NULL);
|
||||
/* we need to handle who-is
|
||||
to support dynamic device binding to us */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
|
||||
/* set the handler for all the services we don't implement
|
||||
It is required to send the proper reject message... */
|
||||
apdu_set_unrecognized_service_handler_handler
|
||||
(handler_unrecognized_service);
|
||||
/* we must implement read property - it's required! */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
|
||||
handler_read_property);
|
||||
/* handle the reply (request) coming back */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_add);
|
||||
/* handle any errors coming back */
|
||||
apdu_set_abort_handler(MyAbortHandler);
|
||||
apdu_set_reject_handler(MyRejectHandler);
|
||||
}
|
||||
|
||||
static void print_usage(char *filename)
|
||||
{
|
||||
printf("Usage: %s [abort-reason [invoke-id [server]]]\n",
|
||||
filename);
|
||||
printf(" [--dnet][--dadr][--mac]\n");
|
||||
printf(" [--version][--help]\n");
|
||||
}
|
||||
|
||||
static void print_help(char *filename)
|
||||
{
|
||||
printf("Send BACnet Abort message to the network.\n");
|
||||
printf("--mac A\n"
|
||||
"Optional destination BACnet mac address."
|
||||
"Valid ranges are from 00 to FF (hex) for MS/TP or ARCNET,\n"
|
||||
"or an IP string with optional port number like 10.1.2.3:47808\n"
|
||||
"or an Ethernet MAC in hex like 00:21:70:7e:32:bb\n"
|
||||
"\n"
|
||||
"--dnet N\n"
|
||||
"Optional destination BACnet network number N for directed requests.\n"
|
||||
"Valid range is from 0 to 65535 where 0 is the local connection\n"
|
||||
"and 65535 is network broadcast.\n"
|
||||
"\n"
|
||||
"--dadr A\n"
|
||||
"Optional BACnet mac address on the destination BACnet network number.\n"
|
||||
"Valid ranges are from 00 to FF (hex) for MS/TP or ARCNET,\n"
|
||||
"or an IP string with optional port number like 10.1.2.3:47808\n"
|
||||
"or an Ethernet MAC in hex like 00:21:70:7e:32:bb\n"
|
||||
"\n");
|
||||
printf("abort-reason:\n"
|
||||
" number from 0 to 65535\n"
|
||||
"invoke-id:\n"
|
||||
" number from 1 to 255\n"
|
||||
"server:\n"
|
||||
" 0=false, 1=true\n"
|
||||
"Example:\n"
|
||||
"%s 3 2 1\n",
|
||||
filename);
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char *argv[])
|
||||
{
|
||||
long dnet = -1;
|
||||
BACNET_MAC_ADDRESS mac = { 0 };
|
||||
BACNET_MAC_ADDRESS adr = { 0 };
|
||||
BACNET_ADDRESS dest = { 0 };
|
||||
bool specific_address = false;
|
||||
int argi = 0;
|
||||
unsigned int target_args = 0;
|
||||
char *filename = NULL;
|
||||
|
||||
filename = filename_remove_path(argv[0]);
|
||||
for (argi = 1; argi < argc; argi++) {
|
||||
if (strcmp(argv[argi], "--help") == 0) {
|
||||
print_usage(filename);
|
||||
print_help(filename);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(argv[argi], "--version") == 0) {
|
||||
printf("%s %s\n", filename, BACNET_VERSION_TEXT);
|
||||
printf("Copyright (C) 2016 by Steve Karg and others.\n"
|
||||
"This is free software; see the source for copying conditions.\n"
|
||||
"There is NO warranty; not even for MERCHANTABILITY or\n"
|
||||
"FITNESS FOR A PARTICULAR PURPOSE.\n");
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(argv[argi], "--mac") == 0) {
|
||||
if (++argi < argc) {
|
||||
if (address_mac_from_ascii(&mac, argv[argi])) {
|
||||
specific_address = true;
|
||||
}
|
||||
}
|
||||
} else if (strcmp(argv[argi], "--dnet") == 0) {
|
||||
if (++argi < argc) {
|
||||
dnet = strtol(argv[argi], NULL, 0);
|
||||
if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) {
|
||||
specific_address = true;
|
||||
}
|
||||
}
|
||||
} else if (strcmp(argv[argi], "--dadr") == 0) {
|
||||
if (++argi < argc) {
|
||||
if (address_mac_from_ascii(&adr, argv[argi])) {
|
||||
specific_address = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (target_args == 0) {
|
||||
Target_Abort_Reason = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else if (target_args == 1) {
|
||||
Target_Invoke_ID = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else if (target_args == 2) {
|
||||
Target_Server = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else {
|
||||
print_usage(filename);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
address_init();
|
||||
if (specific_address) {
|
||||
if (adr.len && mac.len) {
|
||||
memcpy(&dest.mac[0], &mac.adr[0], mac.len);
|
||||
dest.mac_len = mac.len;
|
||||
memcpy(&dest.adr[0], &adr.adr[0], adr.len);
|
||||
dest.len = adr.len;
|
||||
if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) {
|
||||
dest.net = dnet;
|
||||
} else {
|
||||
dest.net = BACNET_BROADCAST_NETWORK;
|
||||
}
|
||||
} else if (mac.len) {
|
||||
memcpy(&dest.mac[0], &mac.adr[0], mac.len);
|
||||
dest.mac_len = mac.len;
|
||||
dest.len = 0;
|
||||
if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) {
|
||||
dest.net = dnet;
|
||||
} else {
|
||||
dest.net = 0;
|
||||
}
|
||||
} else {
|
||||
if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) {
|
||||
dest.net = dnet;
|
||||
} else {
|
||||
dest.net = BACNET_BROADCAST_NETWORK;
|
||||
}
|
||||
dest.mac_len = 0;
|
||||
dest.len = 0;
|
||||
}
|
||||
}
|
||||
/* setup my info */
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
Init_Service_Handlers();
|
||||
address_init();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
/* send the request */
|
||||
Send_Abort_To_Network(&Handler_Transmit_Buffer[0],
|
||||
&dest,
|
||||
Target_Invoke_ID,
|
||||
Target_Abort_Reason,
|
||||
Target_Server);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#Makefile to build BACnet Application for GCC compiler
|
||||
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
|
||||
TARGET = bacdcc
|
||||
|
||||
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 -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map
|
||||
|
||||
include: .depend
|
||||
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "bactext.h"
|
||||
#include "iam.h"
|
||||
#include "arf.h"
|
||||
#include "tsm.h"
|
||||
#include "address.h"
|
||||
#include "config.h"
|
||||
#include "bacdef.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "net.h"
|
||||
#include "datalink.h"
|
||||
#include "whois.h"
|
||||
#include "dcc.h"
|
||||
#include "version.h"
|
||||
/* some demo stuff needed */
|
||||
#include "filename.h"
|
||||
#include "handlers.h"
|
||||
#include "client.h"
|
||||
#include "txbuf.h"
|
||||
#include "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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void MyDeviceCommunicationControlSimpleAckHandler(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id)
|
||||
{
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
printf("DeviceCommunicationControl Acknowledged!\n");
|
||||
}
|
||||
|
||||
static void Init_Service_Handlers(
|
||||
void)
|
||||
{
|
||||
Device_Init(NULL);
|
||||
/* we need to handle who-is
|
||||
to support dynamic device binding to us */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
|
||||
/* handle i-am to support binding to other devices */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind);
|
||||
/* set the handler for all the services we don't implement
|
||||
It is required to send the proper reject message... */
|
||||
apdu_set_unrecognized_service_handler_handler
|
||||
(handler_unrecognized_service);
|
||||
/* we must implement read property - it's required! */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
|
||||
handler_read_property);
|
||||
/* handle communication so we can shutup when asked */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
|
||||
handler_device_communication_control);
|
||||
/* handle the ack coming back */
|
||||
apdu_set_confirmed_simple_ack_handler
|
||||
(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
|
||||
MyDeviceCommunicationControlSimpleAckHandler);
|
||||
/* handle any errors coming back */
|
||||
apdu_set_error_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
|
||||
MyErrorHandler);
|
||||
apdu_set_abort_handler(MyAbortHandler);
|
||||
apdu_set_reject_handler(MyRejectHandler);
|
||||
}
|
||||
|
||||
static void print_usage(char *filename)
|
||||
{
|
||||
printf("Usage: %s device-instance state [timeout [password]]\n", filename);
|
||||
printf(" [--version][--help]\n");
|
||||
}
|
||||
|
||||
static void print_help(char *filename)
|
||||
{
|
||||
printf("Send BACnet DeviceCommunicationControl service to device.\n"
|
||||
"\n" "The device-instance can be 0 to %lu.\n"
|
||||
"Possible state values:\n" " 0=enable\n" " 1=disable\n"
|
||||
" 2=disable-initiation\n"
|
||||
"The timeout can be 0 for infinite, or a value in minutes for disable.\n"
|
||||
"The optional password is a character string of 1 to 20 characters.\n"
|
||||
"\nExample:\n"
|
||||
"If you want disable Device Communications in Device 123\n"
|
||||
"for 60 minutes with password 'filister', use the following command:\n"
|
||||
"%s 123 1 60 filister\n",
|
||||
(unsigned long)(BACNET_MAX_INSTANCE - 1), filename);
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char *argv[])
|
||||
{
|
||||
BACNET_ADDRESS src = {
|
||||
0
|
||||
}; /* address where message came from */
|
||||
uint16_t pdu_len = 0;
|
||||
unsigned timeout = 100; /* milliseconds */
|
||||
unsigned max_apdu = 0;
|
||||
time_t elapsed_seconds = 0;
|
||||
time_t last_seconds = 0;
|
||||
time_t current_seconds = 0;
|
||||
time_t timeout_seconds = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
bool found = false;
|
||||
int argi = 0;
|
||||
char *filename = NULL;
|
||||
|
||||
filename = filename_remove_path(argv[0]);
|
||||
for (argi = 1; argi < argc; argi++) {
|
||||
if (strcmp(argv[argi], "--help") == 0) {
|
||||
print_usage(filename);
|
||||
print_help(filename);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(argv[argi], "--version") == 0) {
|
||||
printf("%s %s\n", filename, BACNET_VERSION_TEXT);
|
||||
printf("Copyright (C) 2014 by Steve Karg and others.\n"
|
||||
"This is free software; see the source for copying conditions.\n"
|
||||
"There is NO warranty; not even for MERCHANTABILITY or\n"
|
||||
"FITNESS FOR A PARTICULAR PURPOSE.\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (argc < 3) {
|
||||
print_usage(filename);
|
||||
return 0;
|
||||
}
|
||||
/* decode the command line parameters */
|
||||
Target_Device_Object_Instance = strtol(argv[1], NULL, 0);
|
||||
Communication_State = (uint16_t) strtol(argv[2], NULL, 0);
|
||||
/* optional timeout, required if password is included */
|
||||
if (argc > 3) {
|
||||
Communication_Timeout_Minutes = (uint16_t) strtol(argv[3], NULL, 0);
|
||||
}
|
||||
/* optional password */
|
||||
if (argc > 4) {
|
||||
Communication_Password = argv[4];
|
||||
}
|
||||
if (Target_Device_Object_Instance >= BACNET_MAX_INSTANCE) {
|
||||
fprintf(stderr, "device-instance=%u - it must be less than %u\n",
|
||||
Target_Device_Object_Instance, BACNET_MAX_INSTANCE);
|
||||
return 1;
|
||||
}
|
||||
/* setup my info */
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
address_init();
|
||||
Init_Service_Handlers();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
timeout_seconds = (apdu_timeout() / 1000) * apdu_retries();
|
||||
/* try to bind with the device */
|
||||
found =
|
||||
address_bind_request(Target_Device_Object_Instance, &max_apdu,
|
||||
&Target_Address);
|
||||
if (!found) {
|
||||
Send_WhoIs(Target_Device_Object_Instance,
|
||||
Target_Device_Object_Instance);
|
||||
}
|
||||
/* loop forever */
|
||||
for (;;) {
|
||||
/* increment timer - exit if timed out */
|
||||
current_seconds = time(NULL);
|
||||
|
||||
/* returns 0 bytes on timeout */
|
||||
pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
|
||||
|
||||
/* process */
|
||||
if (pdu_len) {
|
||||
npdu_handler(&src, &Rx_Buf[0], pdu_len);
|
||||
}
|
||||
/* at least one second has passed */
|
||||
if (current_seconds != last_seconds)
|
||||
tsm_timer_milliseconds((uint16_t) ((current_seconds -
|
||||
last_seconds) * 1000));
|
||||
if (Error_Detected)
|
||||
break;
|
||||
/* wait until the device is bound, or timeout and quit */
|
||||
if (!found) {
|
||||
found =
|
||||
address_bind_request(Target_Device_Object_Instance, &max_apdu,
|
||||
&Target_Address);
|
||||
}
|
||||
if (found) {
|
||||
if (invoke_id == 0) {
|
||||
invoke_id =
|
||||
Send_Device_Communication_Control_Request
|
||||
(Target_Device_Object_Instance,
|
||||
Communication_Timeout_Minutes, Communication_State,
|
||||
Communication_Password);
|
||||
} else if (tsm_invoke_id_free(invoke_id))
|
||||
break;
|
||||
else if (tsm_invoke_id_failed(invoke_id)) {
|
||||
fprintf(stderr, "\rError: TSM Timeout!\n");
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
/* try again or abort? */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* increment timer - exit if timed out */
|
||||
elapsed_seconds += (current_seconds - last_seconds);
|
||||
if (elapsed_seconds > timeout_seconds) {
|
||||
printf("\rError: APDU Timeout!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* keep track of time for next check */
|
||||
last_seconds = current_seconds;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
#
|
||||
# Simple makefile to build an executable for Win32
|
||||
#
|
||||
# This makefile assumes Borland bcc32 development environment
|
||||
# on Windows NT/9x/2000/XP
|
||||
#
|
||||
|
||||
!ifndef BORLAND_DIR
|
||||
BORLAND_DIR_Not_Defined:
|
||||
@echo .
|
||||
@echo You must define environment variable BORLAND_DIR to compile.
|
||||
!endif
|
||||
|
||||
# target
|
||||
PRODUCT = bacdcc
|
||||
PRODUCT_EXE = $(PRODUCT).exe
|
||||
|
||||
# tools
|
||||
CC = $(BORLAND_DIR)\bin\bcc32
|
||||
MAKE=$(BORLAND_DIR)\bin\make.exe
|
||||
#LINK = $(BORLAND_DIR)\bin\tlink32
|
||||
LINK = $(BORLAND_DIR)\bin\ilink32
|
||||
|
||||
BACNET_LIB_DIR = ..\..\lib
|
||||
BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib
|
||||
|
||||
# directories
|
||||
BACNET_PORT = ..\..\ports\win32
|
||||
BACNET_INCLUDE = ..\..\include
|
||||
BACNET_OBJECT = ..\object
|
||||
BACNET_HANDLER = ..\handler
|
||||
INCLUDES = \
|
||||
-I$(BACNET_INCLUDE) \
|
||||
-I$(BACNET_PORT) \
|
||||
-I$(BACNET_OBJECT) \
|
||||
-I$(BACNET_HANDLER) \
|
||||
-I$(BORLAND_DIR)\include
|
||||
|
||||
#
|
||||
BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL
|
||||
#BACDL_DEFINE=-DBACDL_MSTP=1
|
||||
BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1
|
||||
DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE)
|
||||
|
||||
SRCS = main.c \
|
||||
$(BACNET_OBJECT)\device-client.c
|
||||
|
||||
OBJS = $(SRCS:.c=.obj)
|
||||
|
||||
#
|
||||
# Compiler definitions
|
||||
#
|
||||
BCC_CFG = bcc32.cfg
|
||||
|
||||
#
|
||||
# Include directories
|
||||
#
|
||||
CFLAGS = $(INCLUDES) $(DEFINES)
|
||||
|
||||
#
|
||||
# Libraries
|
||||
#
|
||||
C_LIB_DIR = $(BORLAND_DIR)\lib
|
||||
|
||||
LIBS = $(BACNET_LIB) \
|
||||
$(C_LIB_DIR)\IMPORT32.lib \
|
||||
$(C_LIB_DIR)\CW32MT.lib \
|
||||
|
||||
#
|
||||
# Main target
|
||||
#
|
||||
# This should be the first one in the makefile
|
||||
|
||||
all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE)
|
||||
del $(BCC_CFG)
|
||||
|
||||
install: $(PRODUCT_EXE)
|
||||
copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE)
|
||||
|
||||
# Linker specific: the link below is for BCC linker/compiler. If you link
|
||||
# with a different linker - please change accordingly.
|
||||
#
|
||||
|
||||
# need a temp response file (@&&| ... |) because command line is too long
|
||||
# $** lists each dependency
|
||||
# $< target name
|
||||
# $* target name without extension
|
||||
$(PRODUCT_EXE) : $(OBJS)
|
||||
@echo Running Linker for $(PRODUCT_EXE)
|
||||
$(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&|
|
||||
$(BORLAND_DIR)\lib\c0x32.obj $**
|
||||
$<
|
||||
$*.map
|
||||
$(LIBS)
|
||||
|
|
||||
|
||||
#
|
||||
# Utilities
|
||||
|
||||
clean :
|
||||
del $(OBJS)
|
||||
del $(PRODUCT_EXE)
|
||||
del $(PRODUCT).map
|
||||
del $(PRODUCT).ilc
|
||||
del $(PRODUCT).ild
|
||||
del $(PRODUCT).ilf
|
||||
del $(PRODUCT).ils
|
||||
del $(PRODUCT).tds
|
||||
del $(BCC_CFG)
|
||||
|
||||
#
|
||||
# Generic rules
|
||||
#
|
||||
.SUFFIXES: .cpp .c .sbr .obj
|
||||
|
||||
#
|
||||
# cc generic rule
|
||||
#
|
||||
.c.obj:
|
||||
$(CC) +$(BCC_CFG) -o$@ $<
|
||||
|
||||
# Compiler configuration file
|
||||
$(BCC_CFG) :
|
||||
Copy &&|
|
||||
$(CFLAGS)
|
||||
-c
|
||||
-y #include line numbers in OBJ's
|
||||
-v #include debug info
|
||||
-w+ #turn on all warnings
|
||||
-Od #disable all optimizations
|
||||
#-a4 #32 bit data alignment
|
||||
#-M # generate link map
|
||||
#-ls # linker options
|
||||
#-WM- #not multithread
|
||||
-WM #multithread
|
||||
-w-aus # ignore warning assigned a value that is never used
|
||||
-w-sig # ignore warning conversion may lose sig digits
|
||||
| $@
|
||||
|
||||
# EOF: makefile
|
||||
@@ -0,0 +1,506 @@
|
||||
#############################################################################
|
||||
|
||||
# Makefile for building bacdcc
|
||||
# Generated by tmake at 11:27, 2006/05/10
|
||||
# Project: tmake
|
||||
# Template: app
|
||||
#############################################################################
|
||||
|
||||
####### Compiler, tools and options
|
||||
|
||||
QTDIR = /usr
|
||||
CC = gcc
|
||||
CXX = g++
|
||||
CFLAGS = -pipe -Wall -W -g -DBACDL_BIP=1 -DTSM_ENABLED=1 -DUSE_INADDR=1 -DBIP_DEBUG
|
||||
CXXFLAGS= -pipe -Wall -W -g -DBACDL_BIP=1 -DTSM_ENABLED=1 -DUSE_INADDR=1 -DBIP_DEBUG
|
||||
INCPATH = -I. -I../.. -I../../demo/object -I../../demo/handler -I../../ports/linux
|
||||
LINK = g++
|
||||
LFLAGS =
|
||||
LIBS = $(SUBLIBS)
|
||||
MOC = $(QTDIR)/bin/moc
|
||||
UIC = $(QTDIR)/bin/uic
|
||||
|
||||
TAR = tar -cf
|
||||
GZIP = gzip -9f
|
||||
|
||||
####### Files
|
||||
|
||||
HEADERS =
|
||||
SOURCES = main.c \
|
||||
../../filename.c \
|
||||
../../bip.c \
|
||||
../../demo/handler/txbuf.c \
|
||||
../../demo/handler/noserv.c \
|
||||
../../demo/handler/h_whois.c \
|
||||
../../demo/handler/h_iam.c \
|
||||
../../demo/handler/h_rp.c \
|
||||
../../demo/handler/h_dcc.c \
|
||||
../../demo/handler/s_whois.c \
|
||||
../../demo/handler/s_dcc.c \
|
||||
../../bacdcode.c \
|
||||
../../bacapp.c \
|
||||
../../bacstr.c \
|
||||
../../bactext.c \
|
||||
../../indtext.c \
|
||||
../../bigend.c \
|
||||
../../whois.c \
|
||||
../../iam.c \
|
||||
../../rp.c \
|
||||
../../wp.c \
|
||||
../../arf.c \
|
||||
../../awf.c \
|
||||
../../dcc.c \
|
||||
../../demo/object/bacfile.c \
|
||||
../../demo/object/device.c \
|
||||
../../demo/object/ai.c \
|
||||
../../demo/object/ao.c \
|
||||
../../demo/object/bi.c \
|
||||
../../demo/object/bo.c \
|
||||
../../demo/object/lsp.c \
|
||||
../../datalink.c \
|
||||
../../tsm.c \
|
||||
../../address.c \
|
||||
../../abort.c \
|
||||
../../reject.c \
|
||||
../../bacerror.c \
|
||||
../../apdu.c \
|
||||
../../npdu.c \
|
||||
../../ports/linux/bip-init.c
|
||||
OBJECTS = main.o \
|
||||
../../filename.o \
|
||||
../../bip.o \
|
||||
../../demo/handler/txbuf.o \
|
||||
../../demo/handler/noserv.o \
|
||||
../../demo/handler/h_whois.o \
|
||||
../../demo/handler/h_iam.o \
|
||||
../../demo/handler/h_rp.o \
|
||||
../../demo/handler/h_dcc.o \
|
||||
../../demo/handler/s_whois.o \
|
||||
../../demo/handler/s_dcc.o \
|
||||
../../bacdcode.o \
|
||||
../../bacapp.o \
|
||||
../../bacstr.o \
|
||||
../../bactext.o \
|
||||
../../indtext.o \
|
||||
../../bigend.o \
|
||||
../../whois.o \
|
||||
../../iam.o \
|
||||
../../rp.o \
|
||||
../../wp.o \
|
||||
../../arf.o \
|
||||
../../awf.o \
|
||||
../../dcc.o \
|
||||
../../demo/object/bacfile.o \
|
||||
../../demo/object/device.o \
|
||||
../../demo/object/ai.o \
|
||||
../../demo/object/ao.o \
|
||||
../../demo/object/bi.o \
|
||||
../../demo/object/bo.o \
|
||||
../../demo/object/lsp.o \
|
||||
../../datalink.o \
|
||||
../../tsm.o \
|
||||
../../address.o \
|
||||
../../abort.o \
|
||||
../../reject.o \
|
||||
../../bacerror.o \
|
||||
../../apdu.o \
|
||||
../../npdu.o \
|
||||
../../ports/linux/bip-init.o
|
||||
INTERFACES =
|
||||
UICDECLS =
|
||||
UICIMPLS =
|
||||
SRCMOC =
|
||||
OBJMOC =
|
||||
DIST =
|
||||
TARGET = bacdcc
|
||||
INTERFACE_DECL_PATH = .
|
||||
|
||||
####### Implicit rules
|
||||
|
||||
.SUFFIXES: .cpp .cxx .cc .C .c
|
||||
|
||||
.cpp.o:
|
||||
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
|
||||
|
||||
.cxx.o:
|
||||
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
|
||||
|
||||
.cc.o:
|
||||
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
|
||||
|
||||
.C.o:
|
||||
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
|
||||
|
||||
.c.o:
|
||||
$(CC) -c $(CFLAGS) $(INCPATH) -o $@ $<
|
||||
|
||||
####### Build rules
|
||||
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC)
|
||||
$(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJMOC) $(LIBS)
|
||||
|
||||
moc: $(SRCMOC)
|
||||
|
||||
tmake: makefile.g++
|
||||
|
||||
makefile.g++: tmake.pro
|
||||
tmake tmake.pro -o makefile.g++
|
||||
|
||||
dist:
|
||||
$(TAR) tmake.tar tmake.pro $(SOURCES) $(HEADERS) $(INTERFACES) $(DIST)
|
||||
$(GZIP) tmake.tar
|
||||
|
||||
clean:
|
||||
-rm -f $(OBJECTS) $(OBJMOC) $(SRCMOC) $(UICIMPLS) $(UICDECLS) $(TARGET)
|
||||
-rm -f *~ core
|
||||
-rm -f core *~
|
||||
|
||||
####### Sub-libraries
|
||||
|
||||
|
||||
###### Combined headers
|
||||
|
||||
|
||||
####### Compile
|
||||
|
||||
main.o: main.c
|
||||
|
||||
../../filename.o: ../../filename.c
|
||||
|
||||
../../bip.o: ../../bip.c \
|
||||
../../bacdcode.h \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../config.h \
|
||||
../../bacstr.h \
|
||||
../../bip.h
|
||||
|
||||
../../demo/handler/txbuf.o: ../../demo/handler/txbuf.c \
|
||||
../../config.h
|
||||
|
||||
../../demo/handler/noserv.o: ../../demo/handler/noserv.c \
|
||||
../../demo/handler/txbuf.h \
|
||||
../../config.h \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h
|
||||
|
||||
../../demo/handler/h_whois.o: ../../demo/handler/h_whois.c \
|
||||
../../config.h \
|
||||
../../demo/handler/txbuf.h \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h \
|
||||
../../demo/handler/client.h
|
||||
|
||||
../../demo/handler/h_iam.o: ../../demo/handler/h_iam.c \
|
||||
../../config.h \
|
||||
../../demo/handler/txbuf.h \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h
|
||||
|
||||
../../demo/handler/h_rp.o: ../../demo/handler/h_rp.c \
|
||||
../../config.h \
|
||||
../../demo/handler/txbuf.h \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h
|
||||
|
||||
../../demo/handler/h_dcc.o: ../../demo/handler/h_dcc.c \
|
||||
../../config.h \
|
||||
../../demo/handler/txbuf.h \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h
|
||||
|
||||
../../demo/handler/s_whois.o: ../../demo/handler/s_whois.c \
|
||||
../../config.h \
|
||||
../../demo/handler/txbuf.h \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h \
|
||||
../../demo/handler/handlers.h
|
||||
|
||||
../../demo/handler/s_dcc.o: ../../demo/handler/s_dcc.c \
|
||||
../../config.h \
|
||||
../../demo/handler/txbuf.h \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h \
|
||||
../../demo/handler/handlers.h
|
||||
|
||||
../../bacdcode.o: ../../bacdcode.c \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../config.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h \
|
||||
../../bits.h \
|
||||
../../bigend.h
|
||||
|
||||
../../bacapp.o: ../../bacapp.c \
|
||||
../../bacenum.h \
|
||||
../../bacdcode.h \
|
||||
../../bacdef.h \
|
||||
../../config.h \
|
||||
../../bacstr.h \
|
||||
../../bacapp.h \
|
||||
../../bactext.h \
|
||||
../../indtext.h
|
||||
|
||||
../../bacstr.o: ../../bacstr.c \
|
||||
../../bacstr.h \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../config.h \
|
||||
../../bits.h
|
||||
|
||||
../../bactext.o: ../../bactext.c \
|
||||
../../indtext.h \
|
||||
../../bacenum.h
|
||||
|
||||
../../indtext.o: ../../indtext.c \
|
||||
../../indtext.h
|
||||
|
||||
../../bigend.o: ../../bigend.c
|
||||
|
||||
../../whois.o: ../../whois.c \
|
||||
../../bacenum.h \
|
||||
../../bacdcode.h \
|
||||
../../bacdef.h \
|
||||
../../config.h \
|
||||
../../bacstr.h
|
||||
|
||||
../../iam.o: ../../iam.c \
|
||||
../../bacenum.h \
|
||||
../../bacdef.h \
|
||||
../../config.h \
|
||||
../../npdu.h \
|
||||
../../datalink.h \
|
||||
../../ethernet.h \
|
||||
../../arcnet.h \
|
||||
../../dlmstp.h \
|
||||
../../bip.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h \
|
||||
../../address.h
|
||||
|
||||
../../rp.o: ../../rp.c \
|
||||
../../bacenum.h \
|
||||
../../bacdcode.h \
|
||||
../../bacdef.h \
|
||||
../../config.h \
|
||||
../../bacstr.h \
|
||||
../../rp.h
|
||||
|
||||
../../wp.o: ../../wp.c \
|
||||
../../bacenum.h \
|
||||
../../bacdcode.h \
|
||||
../../bacdef.h \
|
||||
../../config.h \
|
||||
../../bacstr.h \
|
||||
../../wp.h \
|
||||
../../bacapp.h
|
||||
|
||||
../../arf.o: ../../arf.c \
|
||||
../../bacenum.h \
|
||||
../../bacdcode.h \
|
||||
../../bacdef.h \
|
||||
../../config.h \
|
||||
../../bacstr.h \
|
||||
../../arf.h
|
||||
|
||||
../../awf.o: ../../awf.c \
|
||||
../../bacenum.h \
|
||||
../../bacdcode.h \
|
||||
../../bacdef.h \
|
||||
../../config.h \
|
||||
../../bacstr.h \
|
||||
../../awf.h
|
||||
|
||||
../../dcc.o: ../../dcc.c \
|
||||
../../bacenum.h \
|
||||
../../bacdcode.h \
|
||||
../../bacdef.h \
|
||||
../../config.h \
|
||||
../../bacstr.h \
|
||||
../../dcc.h
|
||||
|
||||
../../demo/object/bacfile.o: ../../demo/object/bacfile.c \
|
||||
../../config.h \
|
||||
../../address.h \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../datalink.h \
|
||||
../../ethernet.h \
|
||||
../../arcnet.h \
|
||||
../../dlmstp.h \
|
||||
../../bip.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h \
|
||||
../../npdu.h \
|
||||
../../demo/object/device.h \
|
||||
../../wp.h \
|
||||
../../bacapp.h \
|
||||
../../arf.h \
|
||||
../../awf.h
|
||||
|
||||
../../demo/object/device.o: ../../demo/object/device.c \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../config.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h \
|
||||
../../demo/object/ai.h \
|
||||
../../demo/object/bi.h \
|
||||
../../demo/object/bo.h \
|
||||
../../wp.h \
|
||||
../../bacapp.h \
|
||||
../../demo/object/ao.h \
|
||||
../../demo/object/lsp.h \
|
||||
../../demo/object/device.h \
|
||||
../../demo/object/bacfile.h \
|
||||
../../arf.h
|
||||
|
||||
../../demo/object/ai.o: ../../demo/object/ai.c \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../config.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h
|
||||
|
||||
../../demo/object/ao.o: ../../demo/object/ao.c \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../config.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h \
|
||||
../../wp.h \
|
||||
../../bacapp.h
|
||||
|
||||
../../demo/object/bi.o: ../../demo/object/bi.c \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../config.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h
|
||||
|
||||
../../demo/object/bo.o: ../../demo/object/bo.c \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../config.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h \
|
||||
../../wp.h \
|
||||
../../bacapp.h
|
||||
|
||||
../../demo/object/lsp.o: ../../demo/object/lsp.c \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../config.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h \
|
||||
../../wp.h \
|
||||
../../bacapp.h
|
||||
|
||||
../../datalink.o: ../../datalink.c \
|
||||
../../datalink.h \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../config.h \
|
||||
../../ethernet.h \
|
||||
../../arcnet.h \
|
||||
../../dlmstp.h \
|
||||
../../bip.h
|
||||
|
||||
../../tsm.o: ../../tsm.c \
|
||||
../../bits.h \
|
||||
../../apdu.h \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../config.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h \
|
||||
../../tsm.h \
|
||||
../../demo/object/device.h \
|
||||
../../wp.h \
|
||||
../../bacapp.h \
|
||||
../../datalink.h \
|
||||
../../ethernet.h \
|
||||
../../arcnet.h \
|
||||
../../dlmstp.h \
|
||||
../../bip.h \
|
||||
../../demo/handler/handlers.h \
|
||||
../../address.h
|
||||
|
||||
../../address.o: ../../address.c \
|
||||
../../config.h \
|
||||
../../address.h \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h
|
||||
|
||||
../../abort.o: ../../abort.c \
|
||||
../../bacenum.h \
|
||||
../../bacdcode.h \
|
||||
../../bacdef.h \
|
||||
../../config.h \
|
||||
../../bacstr.h
|
||||
|
||||
../../reject.o: ../../reject.c \
|
||||
../../bacenum.h \
|
||||
../../bacdcode.h \
|
||||
../../bacdef.h \
|
||||
../../config.h \
|
||||
../../bacstr.h
|
||||
|
||||
../../bacerror.o: ../../bacerror.c \
|
||||
../../bacenum.h \
|
||||
../../bacdcode.h \
|
||||
../../bacdef.h \
|
||||
../../config.h \
|
||||
../../bacstr.h
|
||||
|
||||
../../apdu.o: ../../apdu.c \
|
||||
../../bits.h \
|
||||
../../apdu.h \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../config.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h \
|
||||
../../tsm.h \
|
||||
../../dcc.h \
|
||||
../../iam.h
|
||||
|
||||
../../npdu.o: ../../npdu.c \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../config.h \
|
||||
../../bacdcode.h \
|
||||
../../bacstr.h \
|
||||
../../bits.h \
|
||||
../../npdu.h \
|
||||
../../apdu.h
|
||||
|
||||
../../ports/linux/bip-init.o: ../../ports/linux/bip-init.c \
|
||||
../../bacdcode.h \
|
||||
../../bacdef.h \
|
||||
../../bacenum.h \
|
||||
../../config.h \
|
||||
../../bacstr.h \
|
||||
../../bip.h \
|
||||
../../ports/linux/net.h
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
TEMPLATE = app
|
||||
CONFIG = warn_on debug console
|
||||
CLEAN_FILES = core *~
|
||||
TARGET = bacdcc
|
||||
DEFINES = BACDL_BIP=1 TSM_ENABLED=1 USE_INADDR=1 BIP_DEBUG
|
||||
SOURCES = main.c \
|
||||
../../filename.c \
|
||||
../../bip.c \
|
||||
../../demo/handler/txbuf.c \
|
||||
../../demo/handler/noserv.c \
|
||||
../../demo/handler/h_whois.c \
|
||||
../../demo/handler/h_iam.c \
|
||||
../../demo/handler/h_rp.c \
|
||||
../../demo/handler/h_dcc.c \
|
||||
../../demo/handler/s_whois.c \
|
||||
../../demo/handler/s_dcc.c \
|
||||
../../bacdcode.c \
|
||||
../../bacapp.c \
|
||||
../../bacstr.c \
|
||||
../../bactext.c \
|
||||
../../indtext.c \
|
||||
../../bigend.c \
|
||||
../../whois.c \
|
||||
../../iam.c \
|
||||
../../rp.c \
|
||||
../../wp.c \
|
||||
../../arf.c \
|
||||
../../awf.c \
|
||||
../../dcc.c \
|
||||
../../demo/object/bacfile.c \
|
||||
../../demo/object/device.c \
|
||||
../../demo/object/ai.c \
|
||||
../../demo/object/ao.c \
|
||||
../../demo/object/bi.c \
|
||||
../../demo/object/bo.c \
|
||||
../../demo/object/lsp.c \
|
||||
../../datalink.c \
|
||||
../../tsm.c \
|
||||
../../address.c \
|
||||
../../abort.c \
|
||||
../../reject.c \
|
||||
../../bacerror.c \
|
||||
../../apdu.c \
|
||||
../../npdu.c
|
||||
unix:SOURCES += ../../ports/linux/bip-init.c
|
||||
win32:SOURCES += ../../ports/win32/bip-init.c
|
||||
|
||||
INCLUDEPATH = . \
|
||||
../../ \
|
||||
../../demo/object \
|
||||
../../demo/handler
|
||||
|
||||
unix:INCLUDEPATH += ../../ports/linux
|
||||
win32:INCLUDEPATH += ../../ports/win32
|
||||
|
||||
#unix:HEADERS += ../../ports/linux/net.h
|
||||
#win32:HEADERS += ../../ports/win32/stdint.h
|
||||
#win32:HEADERS += ../../ports/win32/net.h
|
||||
#win32:HEADERS += ../../ports/win32/stdbool.h
|
||||
@@ -0,0 +1,39 @@
|
||||
#Makefile to build BACnet Application for the Linux Port
|
||||
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
|
||||
TARGET = bacepics
|
||||
|
||||
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 -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map
|
||||
|
||||
include: .depend
|
||||
@@ -0,0 +1,60 @@
|
||||
<?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="-Wall" />
|
||||
<Add option="-fexceptions" />
|
||||
<Add option="-DBACDL_BIP" />
|
||||
<Add option="-DPRINT_ENABLED=1" />
|
||||
<Add option="-DBACAPP_ALL" />
|
||||
<Add directory="." />
|
||||
<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>
|
||||
Executable
+104
@@ -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_ */
|
||||
+1854
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,139 @@
|
||||
#
|
||||
# Simple makefile to build an executable for Win32
|
||||
#
|
||||
# This makefile assumes Borland bcc32 development environment
|
||||
# on Windows NT/9x/2000/XP
|
||||
#
|
||||
|
||||
!ifndef BORLAND_DIR
|
||||
BORLAND_DIR_Not_Defined:
|
||||
@echo .
|
||||
@echo You must define environment variable BORLAND_DIR to compile.
|
||||
!endif
|
||||
|
||||
PRODUCT = bacepics
|
||||
PRODUCT_EXE = $(PRODUCT).exe
|
||||
|
||||
# tools
|
||||
CC = $(BORLAND_DIR)\bin\bcc32
|
||||
MAKE=$(BORLAND_DIR)\bin\make.exe
|
||||
#LINK = $(BORLAND_DIR)\bin\tlink32
|
||||
LINK = $(BORLAND_DIR)\bin\ilink32
|
||||
|
||||
BACNET_LIB_DIR = ..\..\lib
|
||||
BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib
|
||||
|
||||
# directories
|
||||
BACNET_SOURCE = ..\..\src
|
||||
BACNET_PORT = ..\..\ports\win32
|
||||
BACNET_INCLUDE = ..\..\include
|
||||
BACNET_OBJECT = ..\object
|
||||
BACNET_HANDLER = ..\handler
|
||||
INCLUDES = \
|
||||
-I$(BACNET_INCLUDE) \
|
||||
-I$(BACNET_PORT) \
|
||||
-I$(BACNET_OBJECT) \
|
||||
-I$(BACNET_HANDLER) \
|
||||
-I$(BORLAND_DIR)\include
|
||||
|
||||
#
|
||||
BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL
|
||||
#BACDL_DEFINE=-DBACDL_MSTP=1
|
||||
BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1
|
||||
DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE)
|
||||
|
||||
SRCS = main.c
|
||||
|
||||
OBJS = $(SRCS:.c=.obj)
|
||||
|
||||
#
|
||||
# Compiler definitions
|
||||
#
|
||||
BCC_CFG = bcc32.cfg
|
||||
|
||||
#
|
||||
# Include directories
|
||||
#
|
||||
CFLAGS = $(INCLUDES) $(DEFINES)
|
||||
|
||||
#
|
||||
# Libraries
|
||||
#
|
||||
C_LIB_DIR = $(BORLAND_DIR)\lib
|
||||
|
||||
LIBS = $(BACNET_LIB) \
|
||||
$(C_LIB_DIR)\IMPORT32.lib \
|
||||
$(C_LIB_DIR)\CW32MT.lib \
|
||||
|
||||
#
|
||||
# Main target
|
||||
#
|
||||
# This should be the first one in the makefile
|
||||
|
||||
all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE)
|
||||
del $(BCC_CFG)
|
||||
|
||||
install: $(PRODUCT_EXE)
|
||||
copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE)
|
||||
|
||||
# Linker specific: the link below is for BCC linker/compiler. If you link
|
||||
# with a different linker - please change accordingly.
|
||||
#
|
||||
|
||||
# need a temp response file (@&&| ... |) because command line is too long
|
||||
# $** lists each dependency
|
||||
# $< target name
|
||||
# $* target name without extension
|
||||
$(PRODUCT_EXE) : $(OBJS)
|
||||
@echo Running Linker for $(PRODUCT_EXE)
|
||||
$(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&|
|
||||
$(BORLAND_DIR)\lib\c0x32.obj $**
|
||||
$<
|
||||
$*.map
|
||||
$(LIBS)
|
||||
|
|
||||
|
||||
#
|
||||
# Utilities
|
||||
|
||||
clean :
|
||||
del $(OBJS)
|
||||
del $(PRODUCT_EXE)
|
||||
del $(PRODUCT).map
|
||||
del $(PRODUCT).ilc
|
||||
del $(PRODUCT).ild
|
||||
del $(PRODUCT).ilf
|
||||
del $(PRODUCT).ils
|
||||
del $(PRODUCT).tds
|
||||
del $(BCC_CFG)
|
||||
|
||||
#
|
||||
# Generic rules
|
||||
#
|
||||
.SUFFIXES: .cpp .c .sbr .obj
|
||||
|
||||
#
|
||||
# cc generic rule
|
||||
#
|
||||
.c.obj:
|
||||
$(CC) +$(BCC_CFG) -o$@ $<
|
||||
|
||||
# Compiler configuration file
|
||||
$(BCC_CFG) :
|
||||
Copy &&|
|
||||
$(CFLAGS)
|
||||
-c
|
||||
-y #include line numbers in OBJ's
|
||||
-v #include debug info
|
||||
-w+ #turn on all warnings
|
||||
-Od #disable all optimizations
|
||||
#-a4 #32 bit data alignment
|
||||
#-M # generate link map
|
||||
#-ls # linker options
|
||||
#-WM- #not multithread
|
||||
-WM #multithread
|
||||
-w-aus # ignore warning assigned a value that is never used
|
||||
-w-sig # ignore warning conversion may lose sig digits
|
||||
| $@
|
||||
|
||||
# EOF: makefile
|
||||
@@ -0,0 +1,39 @@
|
||||
#Makefile to build BACnet Application for the Linux Port
|
||||
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
|
||||
TARGET = bacerror
|
||||
|
||||
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 -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map
|
||||
|
||||
include: .depend
|
||||
@@ -0,0 +1,256 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "bactext.h"
|
||||
#include "iam.h"
|
||||
#include "address.h"
|
||||
#include "config.h"
|
||||
#include "bacdef.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "version.h"
|
||||
/* some demo stuff needed */
|
||||
#include "filename.h"
|
||||
#include "handlers.h"
|
||||
#include "client.h"
|
||||
#include "txbuf.h"
|
||||
#include "dlenv.h"
|
||||
|
||||
/* parsed command line parameters */
|
||||
static 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;
|
||||
|
||||
void MyAbortHandler(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id,
|
||||
uint8_t abort_reason,
|
||||
bool server)
|
||||
{
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
(void) server;
|
||||
printf("BACnet Abort: %s\n", bactext_abort_reason_name(abort_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
void MyRejectHandler(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id,
|
||||
uint8_t reject_reason)
|
||||
{
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
printf("BACnet Reject: %s\n", bactext_reject_reason_name(reject_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
static void Init_Service_Handlers(
|
||||
void)
|
||||
{
|
||||
Device_Init(NULL);
|
||||
/* we need to handle who-is
|
||||
to support dynamic device binding to us */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
|
||||
/* set the handler for all the services we don't implement
|
||||
It is required to send the proper reject message... */
|
||||
apdu_set_unrecognized_service_handler_handler
|
||||
(handler_unrecognized_service);
|
||||
/* we must implement read property - it's required! */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
|
||||
handler_read_property);
|
||||
/* handle the reply (request) coming back */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_add);
|
||||
/* handle any errors coming back */
|
||||
apdu_set_abort_handler(MyAbortHandler);
|
||||
apdu_set_reject_handler(MyRejectHandler);
|
||||
}
|
||||
|
||||
static void print_usage(char *filename)
|
||||
{
|
||||
printf("Usage: %s [error-class error-code service-number invoke-id]\n",
|
||||
filename);
|
||||
printf(" [--dnet][--dadr][--mac]\n");
|
||||
printf(" [--version][--help]\n");
|
||||
}
|
||||
|
||||
static void print_help(char *filename)
|
||||
{
|
||||
printf("Send BACnet Error message to the network.\n");
|
||||
printf("--mac A\n"
|
||||
"Optional destination BACnet mac address."
|
||||
"Valid ranges are from 00 to FF (hex) for MS/TP or ARCNET,\n"
|
||||
"or an IP string with optional port number like 10.1.2.3:47808\n"
|
||||
"or an Ethernet MAC in hex like 00:21:70:7e:32:bb\n"
|
||||
"\n"
|
||||
"--dnet N\n"
|
||||
"Optional destination BACnet network number N for directed requests.\n"
|
||||
"Valid range is from 0 to 65535 where 0 is the local connection\n"
|
||||
"and 65535 is network broadcast.\n"
|
||||
"\n"
|
||||
"--dadr A\n"
|
||||
"Optional BACnet mac address on the destination BACnet network number.\n"
|
||||
"Valid ranges are from 00 to FF (hex) for MS/TP or ARCNET,\n"
|
||||
"or an IP string with optional port number like 10.1.2.3:47808\n"
|
||||
"or an Ethernet MAC in hex like 00:21:70:7e:32:bb\n"
|
||||
"\n");
|
||||
printf("error-class:\n"
|
||||
" number from 0 to 65535\n"
|
||||
"error-code:\n"
|
||||
" number from 0 to 65535\n"
|
||||
"service-number:\n"
|
||||
" number from 0 to 65535 for BACnet Services\n"
|
||||
"invoke-id:\n"
|
||||
" number from 1 to 255\n"
|
||||
"Example:\n"
|
||||
"%s 3 2 1\n",
|
||||
filename);
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char *argv[])
|
||||
{
|
||||
long dnet = -1;
|
||||
BACNET_MAC_ADDRESS mac = { 0 };
|
||||
BACNET_MAC_ADDRESS adr = { 0 };
|
||||
BACNET_ADDRESS dest = { 0 };
|
||||
bool specific_address = false;
|
||||
int argi = 0;
|
||||
unsigned int target_args = 0;
|
||||
char *filename = NULL;
|
||||
|
||||
filename = filename_remove_path(argv[0]);
|
||||
for (argi = 1; argi < argc; argi++) {
|
||||
if (strcmp(argv[argi], "--help") == 0) {
|
||||
print_usage(filename);
|
||||
print_help(filename);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(argv[argi], "--version") == 0) {
|
||||
printf("%s %s\n", filename, BACNET_VERSION_TEXT);
|
||||
printf("Copyright (C) 2016 by Steve Karg and others.\n"
|
||||
"This is free software; see the source for copying conditions.\n"
|
||||
"There is NO warranty; not even for MERCHANTABILITY or\n"
|
||||
"FITNESS FOR A PARTICULAR PURPOSE.\n");
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(argv[argi], "--mac") == 0) {
|
||||
if (++argi < argc) {
|
||||
if (address_mac_from_ascii(&mac, argv[argi])) {
|
||||
specific_address = true;
|
||||
}
|
||||
}
|
||||
} else if (strcmp(argv[argi], "--dnet") == 0) {
|
||||
if (++argi < argc) {
|
||||
dnet = strtol(argv[argi], NULL, 0);
|
||||
if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) {
|
||||
specific_address = true;
|
||||
}
|
||||
}
|
||||
} else if (strcmp(argv[argi], "--dadr") == 0) {
|
||||
if (++argi < argc) {
|
||||
if (address_mac_from_ascii(&adr, argv[argi])) {
|
||||
specific_address = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (target_args == 0) {
|
||||
Target_Error_Class = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else if (target_args == 1) {
|
||||
Target_Error_Code = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else if (target_args == 2) {
|
||||
Target_Service = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else if (target_args == 2) {
|
||||
Target_Invoke_ID = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else {
|
||||
print_usage(filename);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
address_init();
|
||||
if (specific_address) {
|
||||
if (adr.len && mac.len) {
|
||||
memcpy(&dest.mac[0], &mac.adr[0], mac.len);
|
||||
dest.mac_len = mac.len;
|
||||
memcpy(&dest.adr[0], &adr.adr[0], adr.len);
|
||||
dest.len = adr.len;
|
||||
if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) {
|
||||
dest.net = dnet;
|
||||
} else {
|
||||
dest.net = BACNET_BROADCAST_NETWORK;
|
||||
}
|
||||
} else if (mac.len) {
|
||||
memcpy(&dest.mac[0], &mac.adr[0], mac.len);
|
||||
dest.mac_len = mac.len;
|
||||
dest.len = 0;
|
||||
if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) {
|
||||
dest.net = dnet;
|
||||
} else {
|
||||
dest.net = 0;
|
||||
}
|
||||
} else {
|
||||
if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) {
|
||||
dest.net = dnet;
|
||||
} else {
|
||||
dest.net = BACNET_BROADCAST_NETWORK;
|
||||
}
|
||||
dest.mac_len = 0;
|
||||
dest.len = 0;
|
||||
}
|
||||
}
|
||||
/* setup my info */
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
Init_Service_Handlers();
|
||||
address_init();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
/* send the request */
|
||||
Send_Error_To_Network(&Handler_Transmit_Buffer[0],
|
||||
&dest,
|
||||
Target_Invoke_ID,
|
||||
Target_Service,
|
||||
Target_Error_Class,
|
||||
Target_Error_Code);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
#Makefile to build BACnet Gateway Demonstration Application for the Linux Port
|
||||
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
|
||||
# Executable file name
|
||||
TARGET = bacgateway
|
||||
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
# put any overloaded or special built src files here,
|
||||
# so the linker uses these instead of the functions in the library
|
||||
SRCS = main.c \
|
||||
$(BACNET_OBJECT)/gw_device.c \
|
||||
$(BACNET_HANDLER)/h_routed_npdu.c \
|
||||
$(BACNET_HANDLER)/s_router.c \
|
||||
$(BACNET_OBJECT)/device.c \
|
||||
$(BACNET_OBJECT)/ai.c \
|
||||
$(BACNET_OBJECT)/ao.c \
|
||||
$(BACNET_OBJECT)/av.c \
|
||||
$(BACNET_OBJECT)/bi.c \
|
||||
$(BACNET_OBJECT)/bo.c \
|
||||
$(BACNET_OBJECT)/bv.c \
|
||||
$(BACNET_OBJECT)/channel.c \
|
||||
$(BACNET_OBJECT)/command.c \
|
||||
$(BACNET_OBJECT)/csv.c \
|
||||
$(BACNET_OBJECT)/iv.c \
|
||||
$(BACNET_OBJECT)/lc.c \
|
||||
$(BACNET_OBJECT)/lo.c \
|
||||
$(BACNET_OBJECT)/lsp.c \
|
||||
$(BACNET_OBJECT)/ms-input.c \
|
||||
$(BACNET_OBJECT)/mso.c \
|
||||
$(BACNET_OBJECT)/msv.c \
|
||||
$(BACNET_OBJECT)/nc.c \
|
||||
$(BACNET_OBJECT)/netport.c \
|
||||
$(BACNET_OBJECT)/osv.c \
|
||||
$(BACNET_OBJECT)/piv.c \
|
||||
$(BACNET_OBJECT)/schedule.c \
|
||||
$(BACNET_OBJECT)/trendlog.c \
|
||||
$(BACNET_OBJECT)/bacfile.c
|
||||
|
||||
OBJS = ${SRCS:.c=.o}
|
||||
|
||||
DEFINES += -DBAC_ROUTING
|
||||
|
||||
CFLAGS = $(WARNINGS) $(DEBUGGING) $(OPTIMIZATION) $(STANDARDS) $(INCLUDES) $(DEFINES)
|
||||
|
||||
all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN}
|
||||
|
||||
${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET}
|
||||
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
|
||||
size $@
|
||||
cp $@ ../../bin
|
||||
|
||||
lib: ${BACNET_LIB_TARGET}
|
||||
|
||||
${BACNET_LIB_TARGET}:
|
||||
( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) )
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
clean:
|
||||
rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map
|
||||
|
||||
include: .depend
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef GATEWAY_H_
|
||||
#define GATEWAY_H_
|
||||
|
||||
/** @file gateway/gateway.h Header for example gateway (ie, BACnet Router
|
||||
* and Devices) using the BACnet Stack. */
|
||||
|
||||
/** @defgroup GatewayDemo Demo of a BACnet virtual gateway (multiple Device).
|
||||
* @ingroup Demos
|
||||
* This is a basic demonstration of a BACnet Router with child devices (ie,
|
||||
* gateway) appearing on a virtual BACnet network behind the Router.
|
||||
* This is an extension of the ServerDemo project.
|
||||
*/
|
||||
|
||||
/* Device configuration definitions. */
|
||||
#define FIRST_DEVICE_NUMBER 260001
|
||||
#define VIRTUAL_DNET 2709 /* your choice of number here */
|
||||
#define DEV_NAME_BASE "Gateway Demo Device"
|
||||
#define DEV_DESCR_GATEWAY "Gateway Device and Router"
|
||||
#define DEV_DESCR_REMOTE "Routed Remote Device"
|
||||
|
||||
|
||||
|
||||
#endif /* GATEWAY_H_ */
|
||||
@@ -0,0 +1,346 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "config.h"
|
||||
#include "gateway.h"
|
||||
#include "address.h"
|
||||
#include "bacdef.h"
|
||||
#include "handlers.h"
|
||||
#include "client.h"
|
||||
#include "dlenv.h"
|
||||
#include "bacdcode.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "iam.h"
|
||||
#include "tsm.h"
|
||||
#include "device.h"
|
||||
#include "bacfile.h"
|
||||
#include "datalink.h"
|
||||
#include "dcc.h"
|
||||
#include "net.h"
|
||||
#include "txbuf.h"
|
||||
#include "lc.h"
|
||||
#include "debug.h"
|
||||
#include "version.h"
|
||||
/* include the device object */
|
||||
#include "device.h"
|
||||
#ifdef BACNET_TEST_VMAC
|
||||
#include "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 */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Initialize the Device Objects and each of the child Object instances.
|
||||
* @param first_object_instance Set the first (gateway) Device to this
|
||||
instance number, and subsequent devices to incremented values.
|
||||
*/
|
||||
static void Devices_Init(
|
||||
uint32_t first_object_instance)
|
||||
{
|
||||
int i;
|
||||
char nameText[MAX_DEV_NAME_LEN];
|
||||
char descText[MAX_DEV_DESC_LEN];
|
||||
BACNET_CHARACTER_STRING name_string;
|
||||
|
||||
/* Gateway Device has already been initialized.
|
||||
* But give it a better Description. */
|
||||
Routed_Device_Set_Description(DEV_DESCR_GATEWAY,
|
||||
strlen(DEV_DESCR_GATEWAY));
|
||||
|
||||
/* Now initialize the remote Device objects. */
|
||||
for (i = 1; i < MAX_NUM_DEVICES; i++) {
|
||||
#ifdef _MSC_VER
|
||||
_snprintf(nameText, MAX_DEV_NAME_LEN, "%s %d", DEV_NAME_BASE, i + 1);
|
||||
_snprintf(descText, MAX_DEV_DESC_LEN, "%s %d", DEV_DESCR_REMOTE, i);
|
||||
#else
|
||||
snprintf(nameText, MAX_DEV_NAME_LEN, "%s %d", DEV_NAME_BASE, i + 1);
|
||||
snprintf(descText, MAX_DEV_DESC_LEN, "%s %d", DEV_DESCR_REMOTE, i);
|
||||
#endif
|
||||
characterstring_init_ansi(&name_string, nameText);
|
||||
|
||||
Add_Routed_Device((first_object_instance + i), &name_string, descText);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** Initialize the handlers we will utilize.
|
||||
* @see Device_Init, apdu_set_unconfirmed_handler, apdu_set_confirmed_handler
|
||||
*/
|
||||
static void Init_Service_Handlers(
|
||||
uint32_t first_object_instance)
|
||||
{
|
||||
Device_Init(NULL);
|
||||
Routing_Device_Init(first_object_instance);
|
||||
|
||||
/* we need to handle who-is to support dynamic device binding
|
||||
* For the gateway, we will use the unicast variety so we can
|
||||
* get back through switches to different subnets.
|
||||
* Don't need the routed versions, since the npdu handler calls
|
||||
* each device in turn.
|
||||
*/
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS,
|
||||
handler_who_is_unicast);
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has);
|
||||
/* set the handler for all the services we don't implement */
|
||||
/* It is required to send the proper reject message... */
|
||||
apdu_set_unrecognized_service_handler_handler
|
||||
(handler_unrecognized_service);
|
||||
/* Set the handlers for any confirmed services that we support. */
|
||||
/* We must implement read property - it's required! */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
|
||||
handler_read_property);
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE,
|
||||
handler_read_property_multiple);
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY,
|
||||
handler_write_property);
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_RANGE,
|
||||
handler_read_range);
|
||||
#if defined(BACFILE)
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE,
|
||||
handler_atomic_read_file);
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_ATOMIC_WRITE_FILE,
|
||||
handler_atomic_write_file);
|
||||
#endif
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE,
|
||||
handler_reinitialize_device);
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION,
|
||||
handler_timesync_utc);
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION,
|
||||
handler_timesync);
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_SUBSCRIBE_COV,
|
||||
handler_cov_subscribe);
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_COV_NOTIFICATION,
|
||||
handler_ucov_notification);
|
||||
/* handle communication so we can shutup when asked */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
|
||||
handler_device_communication_control);
|
||||
}
|
||||
|
||||
/** Initialize the BACnet Device Addresses for each Device object.
|
||||
* The gateway has already gotten the normal address (eg, PC's IP for BIP) and
|
||||
* the remote devices get
|
||||
* - For BIP, the IP address reversed, and 4th byte equal to index.
|
||||
* (Eg, 11.22.33.44 for the gateway becomes 44.33.22.01 for the first remote
|
||||
* device.) This is sure to be unique! The port number stays the same.
|
||||
* - For MS/TP, [Steve inserts a good idea here]
|
||||
*/
|
||||
static void Initialize_Device_Addresses(
|
||||
)
|
||||
{
|
||||
int i = 0; /* First entry is Gateway Device */
|
||||
uint32_t virtual_mac = 0;
|
||||
DEVICE_OBJECT_DATA *pDev = NULL;
|
||||
/* Setup info for the main gateway device first */
|
||||
pDev = Get_Routed_Device_Object(i);
|
||||
#if defined(BACDL_BIP)
|
||||
uint16_t myPort;
|
||||
struct in_addr *netPtr; /* Lets us cast to this type */
|
||||
uint8_t *gatewayMac = NULL;
|
||||
uint32_t myAddr = bip_get_addr();
|
||||
gatewayMac = pDev->bacDevAddr.mac; /* Keep pointer to the main MAC */
|
||||
memcpy(pDev->bacDevAddr.mac, &myAddr, 4);
|
||||
myPort = bip_get_port();
|
||||
memcpy(&pDev->bacDevAddr.mac[4], &myPort, 2);
|
||||
pDev->bacDevAddr.mac_len = 6;
|
||||
#elif defined(BACDL_MSTP)
|
||||
pDev->bacDevAddr.mac_len = 1;
|
||||
pDev->bacDevAddr.mac[0] = dlmstp_mac_address();
|
||||
#else
|
||||
#error "No support for this Data Link Layer type "
|
||||
#endif
|
||||
/* broadcast an I-Am on startup */
|
||||
Send_I_Am(&Handler_Transmit_Buffer[0]);
|
||||
|
||||
for (i = 1; i < MAX_NUM_DEVICES; i++) {
|
||||
pDev = Get_Routed_Device_Object(i);
|
||||
if (pDev == NULL)
|
||||
continue;
|
||||
#if defined(BACDL_BIP)
|
||||
virtual_mac = i;
|
||||
netPtr = (struct in_addr *) pDev->bacDevAddr.mac;
|
||||
#if (MAX_NUM_DEVICES > 0xFFFFFF)
|
||||
pDev->bacDevAddr.mac[0] = ((virtual_mac & 0xff000000) >> 24);
|
||||
#else
|
||||
pDev->bacDevAddr.mac[0] = gatewayMac[3];
|
||||
#endif
|
||||
#if (MAX_NUM_DEVICES > 0xFFFF)
|
||||
pDev->bacDevAddr.mac[1] = ((virtual_mac & 0xff0000) >> 16);
|
||||
#else
|
||||
pDev->bacDevAddr.mac[1] = gatewayMac[2];
|
||||
#endif
|
||||
#if (MAX_NUM_DEVICES > 0xFF)
|
||||
pDev->bacDevAddr.mac[2] = ((virtual_mac & 0xff00) >> 8);
|
||||
#else
|
||||
pDev->bacDevAddr.mac[2] = gatewayMac[1];
|
||||
#endif
|
||||
pDev->bacDevAddr.mac[3] = (virtual_mac & 0xff);
|
||||
memcpy(&pDev->bacDevAddr.mac[4], &myPort, 2);
|
||||
pDev->bacDevAddr.mac_len = 6;
|
||||
pDev->bacDevAddr.net = VIRTUAL_DNET;
|
||||
memcpy(&pDev->bacDevAddr.adr[0], &pDev->bacDevAddr.mac[0], 6);
|
||||
pDev->bacDevAddr.len = 6;
|
||||
printf(" - Routed device [%d] ID %u at %s \n", i,
|
||||
pDev->bacObj.Object_Instance_Number, inet_ntoa(*netPtr));
|
||||
#elif defined(BACDL_MSTP)
|
||||
/* Todo: set MS/TP net and port #s */
|
||||
pDev->bacDevAddr.mac_len = 2;
|
||||
#endif
|
||||
/* broadcast an I-Am for each routed Device now */
|
||||
Send_I_Am(&Handler_Transmit_Buffer[0]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** Main function of server demo.
|
||||
*
|
||||
* @see Device_Set_Object_Instance_Number, dlenv_init, Send_I_Am,
|
||||
* datalink_receive, npdu_handler,
|
||||
* dcc_timer_seconds, bvlc_maintenance_timer,
|
||||
* Load_Control_State_Machine_Handler, handler_cov_task,
|
||||
* tsm_timer_milliseconds
|
||||
*
|
||||
* @param argc [in] Arg count.
|
||||
* @param argv [in] Takes one argument: the Device Instance #.
|
||||
* @return 0 on success.
|
||||
*/
|
||||
int main(
|
||||
int argc,
|
||||
char *argv[])
|
||||
{
|
||||
BACNET_ADDRESS src = {
|
||||
0
|
||||
}; /* address where message came from */
|
||||
uint16_t pdu_len = 0;
|
||||
unsigned timeout = 1000; /* milliseconds */
|
||||
time_t last_seconds = 0;
|
||||
time_t current_seconds = 0;
|
||||
uint32_t elapsed_seconds = 0;
|
||||
uint32_t elapsed_milliseconds = 0;
|
||||
uint32_t first_object_instance = FIRST_DEVICE_NUMBER;
|
||||
#ifdef BACNET_TEST_VMAC
|
||||
/* Router data */
|
||||
BACNET_DEVICE_PROFILE *device;
|
||||
BACNET_VMAC_ADDRESS adr;
|
||||
#endif
|
||||
|
||||
/* allow the device ID to be set */
|
||||
if (argc > 1) {
|
||||
first_object_instance = strtol(argv[1], NULL, 0);
|
||||
if ((first_object_instance == 0) ||
|
||||
(first_object_instance >= BACNET_MAX_INSTANCE)) {
|
||||
printf("Error: Invalid Object Instance %s \n", argv[1]);
|
||||
printf("Provide a number from 1 to %ul \n",
|
||||
BACNET_MAX_INSTANCE - 1);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
printf("BACnet Router Demo\n" "BACnet Stack Version %s\n"
|
||||
"BACnet Device ID: %u\n" "Max APDU: %d\n", BACnet_Version,
|
||||
first_object_instance, MAX_APDU);
|
||||
Init_Service_Handlers(first_object_instance);
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
Devices_Init(first_object_instance);
|
||||
Initialize_Device_Addresses();
|
||||
|
||||
#ifdef BACNET_TEST_VMAC
|
||||
/* initialize vmac table and router device */
|
||||
device = vmac_initialize(99, 2001);
|
||||
debug_printf(device->name, "ROUTER:%u", vmac_get_subnet());
|
||||
#endif
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
|
||||
/* broadcast an I-am-router-to-network on startup */
|
||||
printf("Remote Network DNET Number %d \n", DNET_list[0]);
|
||||
Send_I_Am_Router_To_Network(DNET_list);
|
||||
|
||||
/* loop forever */
|
||||
for (;;) {
|
||||
/* input */
|
||||
current_seconds = time(NULL);
|
||||
|
||||
/* returns 0 bytes on timeout */
|
||||
pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
|
||||
|
||||
/* process */
|
||||
if (pdu_len) {
|
||||
routing_npdu_handler(&src, DNET_list, &Rx_Buf[0], pdu_len);
|
||||
}
|
||||
/* at least one second has passed */
|
||||
elapsed_seconds = current_seconds - last_seconds;
|
||||
if (elapsed_seconds) {
|
||||
last_seconds = current_seconds;
|
||||
dcc_timer_seconds(elapsed_seconds);
|
||||
#if defined(BACDL_BIP) && BBMD_ENABLED
|
||||
bvlc_maintenance_timer(elapsed_seconds);
|
||||
#endif
|
||||
dlenv_maintenance_timer(elapsed_seconds);
|
||||
Load_Control_State_Machine_Handler();
|
||||
elapsed_milliseconds = elapsed_seconds * 1000;
|
||||
tsm_timer_milliseconds(elapsed_milliseconds);
|
||||
}
|
||||
handler_cov_task();
|
||||
/* output */
|
||||
|
||||
/* blink LEDs, Turn on or off outputs, etc */
|
||||
}
|
||||
/* Dummy return */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* @} */
|
||||
|
||||
/* End group GatewayDemo */
|
||||
@@ -0,0 +1,148 @@
|
||||
#
|
||||
# Simple makefile to build an executable for Win32
|
||||
#
|
||||
# This makefile assumes Borland bcc32 development environment
|
||||
# on Windows NT/9x/2000/XP
|
||||
#
|
||||
|
||||
!ifndef BORLAND_DIR
|
||||
BORLAND_DIR_Not_Defined:
|
||||
@echo .
|
||||
@echo You must define environment variable BORLAND_DIR to compile.
|
||||
!endif
|
||||
|
||||
# target
|
||||
PRODUCT = bacgateway
|
||||
PRODUCT_EXE = $(PRODUCT).exe
|
||||
|
||||
# tools
|
||||
CC = $(BORLAND_DIR)\bin\bcc32
|
||||
MAKE=$(BORLAND_DIR)\bin\make.exe
|
||||
#LINK = $(BORLAND_DIR)\bin\tlink32
|
||||
LINK = $(BORLAND_DIR)\bin\ilink32
|
||||
|
||||
BACNET_LIB_DIR = ..\..\lib
|
||||
BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib
|
||||
# getting back from the library
|
||||
BACNET_DEMO_DIR = ..\demo\gateway
|
||||
|
||||
# directories
|
||||
BACNET_PORT = ..\..\ports\win32
|
||||
BACNET_INCLUDE = ..\..\include
|
||||
BACNET_OBJECT = ..\object
|
||||
BACNET_HANDLER = ..\handler
|
||||
INCLUDES = \
|
||||
-I$(BACNET_INCLUDE) \
|
||||
-I$(BACNET_PORT) \
|
||||
-I$(BACNET_OBJECT) \
|
||||
-I$(BACNET_HANDLER) \
|
||||
-I$(BORLAND_DIR)\include
|
||||
|
||||
#
|
||||
BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACFILE -DBACAPP_ALL
|
||||
#BACDL_DEFINE=-DBACDL_MSTP -DCRC_USE_TABLE
|
||||
BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1
|
||||
DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE)
|
||||
|
||||
SRCS = main.c device.c
|
||||
|
||||
OBJS = $(SRCS:.c=.obj)
|
||||
|
||||
#
|
||||
# Compiler definitions
|
||||
#
|
||||
BCC_CFG = bcc32.cfg
|
||||
|
||||
#
|
||||
# Include directories
|
||||
#
|
||||
CFLAGS = $(INCLUDES) $(DEFINES)
|
||||
|
||||
#
|
||||
# Libraries
|
||||
#
|
||||
C_LIB_DIR = $(BORLAND_DIR)\lib
|
||||
|
||||
LIBS = $(BACNET_LIB) \
|
||||
$(C_LIB_DIR)\IMPORT32.lib \
|
||||
$(C_LIB_DIR)\CW32MT.lib \
|
||||
|
||||
#
|
||||
# Main target
|
||||
#
|
||||
# This should be the first one in the makefile
|
||||
|
||||
all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE)
|
||||
del $(BCC_CFG)
|
||||
|
||||
install: $(PRODUCT_EXE)
|
||||
copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE)
|
||||
|
||||
$(BACNET_LIB):
|
||||
cd $(BACNET_LIB_DIR)
|
||||
$(MAKE) -i -f makefile.b32 clean
|
||||
$(MAKE) -f makefile.b32 all
|
||||
cd $(BACNET_DEMO_DIR)
|
||||
|
||||
# Linker specific: the link below is for BCC linker/compiler. If you link
|
||||
# with a different linker - please change accordingly.
|
||||
#
|
||||
|
||||
# need a temp response file (@&&| ... |) because command line is too long
|
||||
# $** lists each dependency
|
||||
# $< target name
|
||||
# $* target name without extension
|
||||
$(PRODUCT_EXE) : $(OBJS)
|
||||
@echo Running Linker for $(PRODUCT_EXE)
|
||||
$(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&|
|
||||
$(BORLAND_DIR)\lib\c0x32.obj $**
|
||||
$<
|
||||
$*.map
|
||||
$(LIBS)
|
||||
|
|
||||
|
||||
#
|
||||
# Utilities
|
||||
|
||||
clean :
|
||||
del $(OBJS)
|
||||
del $(PRODUCT_EXE)
|
||||
del $(PRODUCT).map
|
||||
del $(PRODUCT).ilc
|
||||
del $(PRODUCT).ild
|
||||
del $(PRODUCT).ilf
|
||||
del $(PRODUCT).ils
|
||||
del $(PRODUCT).tds
|
||||
del $(BCC_CFG)
|
||||
|
||||
#
|
||||
# Generic rules
|
||||
#
|
||||
.SUFFIXES: .cpp .c .sbr .obj
|
||||
|
||||
#
|
||||
# cc generic rule
|
||||
#
|
||||
.c.obj:
|
||||
$(CC) +$(BCC_CFG) -o$@ $<
|
||||
|
||||
# Compiler configuration file
|
||||
$(BCC_CFG) :
|
||||
Copy &&|
|
||||
$(CFLAGS)
|
||||
-c
|
||||
-y #include line numbers in OBJ's
|
||||
-v #include debug info
|
||||
-w+ #turn on all warnings
|
||||
#-Od #disable all optimizations
|
||||
-O2 #disable all optimizations
|
||||
-WM #multithread
|
||||
#-WM- #not multithread
|
||||
-w-aus # ignore warning assigned a value that is never used
|
||||
-w-sig # ignore warning conversion may lose sig digits
|
||||
#-a4 #32 bit data alignment
|
||||
#-M # generate link map
|
||||
#-ls # linker options
|
||||
| $@
|
||||
|
||||
# EOF: makefile
|
||||
@@ -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 = bacge
|
||||
|
||||
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
|
||||
Executable
+289
@@ -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 "bacdef.h"
|
||||
#include "config.h"
|
||||
#include "bactext.h"
|
||||
#include "bacerror.h"
|
||||
#include "iam.h"
|
||||
#include "arf.h"
|
||||
#include "tsm.h"
|
||||
#include "address.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "net.h"
|
||||
#include "datalink.h"
|
||||
#include "whois.h"
|
||||
#include "getevent.h"
|
||||
|
||||
/* some demo stuff needed */
|
||||
#include "filename.h"
|
||||
#include "handlers.h"
|
||||
#include "client.h"
|
||||
#include "txbuf.h"
|
||||
#include "dlenv.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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
void My_Get_Event_Ack_Handler(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data)
|
||||
{
|
||||
int len = 0;
|
||||
int i;
|
||||
BACNET_GET_EVENT_INFORMATION_DATA data[MAX_OBJ_IDS_IN_GE_ACK];
|
||||
for (i = 0; i < MAX_OBJ_IDS_IN_GE_ACK - 1; i++)
|
||||
data[i].next = &data[i+1];
|
||||
|
||||
printf("Recieved Ack. Saved invoke ID was %i, service returned %i\n", Request_Invoke_ID, service_data->invoke_id);
|
||||
|
||||
if (service_data->invoke_id == Request_Invoke_ID) {
|
||||
len = getevent_ack_decode_service_request(service_request,
|
||||
service_len,
|
||||
&data[0],
|
||||
&More_Events);
|
||||
printf("Decode of Ack returned length %i. MoreEvents flag was %i \n", len, More_Events);
|
||||
if (len > 0) {
|
||||
ge_ack_print_data(&(data[0]), Target_Device_Object_Instance);
|
||||
if (More_Events) {
|
||||
BACNET_GET_EVENT_INFORMATION_DATA * lastData = &(data[0]);
|
||||
while (lastData->next) lastData = lastData->next;
|
||||
LastReceivedObjectIdentifier = lastData->objectIdentifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
Recieved_Ack = true;
|
||||
}
|
||||
|
||||
static void Init_Service_Handlers(void)
|
||||
{
|
||||
Device_Init(NULL);
|
||||
/* we need to handle who-is
|
||||
to support dynamic device binding to us */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
|
||||
/* handle i-am to support binding to other devices */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind);
|
||||
/* set the handler for all the services we don't implement
|
||||
It is required to send the proper reject message... */
|
||||
apdu_set_unrecognized_service_handler_handler(handler_unrecognized_service);
|
||||
/* we must implement getevent - it's required! */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_GET_EVENT_INFORMATION,
|
||||
handler_get_event_information);
|
||||
/* handle the data coming back from confirmed requests */
|
||||
apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_GET_EVENT_INFORMATION,
|
||||
My_Get_Event_Ack_Handler);
|
||||
/* handle any errors coming back */
|
||||
apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler);
|
||||
apdu_set_abort_handler(MyAbortHandler);
|
||||
apdu_set_reject_handler(MyRejectHandler);
|
||||
}
|
||||
|
||||
static int print_help(char *exe_name){
|
||||
printf("Usage:\n" "\n" "%s device-instance [--help]\n" "\n"
|
||||
" Send BACnet GetEventInformation service retruequest to given device, and wait\n"
|
||||
" for responses.\n\n", exe_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
BACNET_ADDRESS src = {0}; /* address where message came from */
|
||||
uint16_t pdu_len = 0;
|
||||
unsigned timeout = 100; /* milliseconds */
|
||||
unsigned max_apdu = 0;
|
||||
time_t elapsed_seconds = 0;
|
||||
time_t last_seconds = 0;
|
||||
time_t current_seconds = 0;
|
||||
time_t timeout_seconds = 0;
|
||||
LastReceivedObjectIdentifier.instance = 0;
|
||||
LastReceivedObjectIdentifier.type = 0;
|
||||
|
||||
bool found = false;
|
||||
if (argc <= 1) {
|
||||
printf("Usage: %s device-instance\r\n", filename_remove_path(argv[0]));
|
||||
return 0;
|
||||
} else if(strcmp(argv[1], "--help") == 0) {
|
||||
print_help(filename_remove_path(argv[0]));
|
||||
return 0;
|
||||
}
|
||||
/* decode the command line parameter */
|
||||
Target_Device_Object_Instance = strtol(argv[1], NULL, 0);
|
||||
/* setup my info */
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
address_init();
|
||||
Init_Service_Handlers();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
timeout_seconds = (apdu_timeout() / 1000) * apdu_retries();
|
||||
/* try to bind with the device */
|
||||
found = address_bind_request(Target_Device_Object_Instance,
|
||||
&max_apdu,
|
||||
&Target_Address);
|
||||
if (!found) {
|
||||
Send_WhoIs(Target_Device_Object_Instance, Target_Device_Object_Instance);
|
||||
}
|
||||
/* loop forever */
|
||||
for (;;) {
|
||||
/* increment timer - exit if timed out */
|
||||
current_seconds = time(NULL);
|
||||
|
||||
/* at least one second has passed */
|
||||
if (current_seconds != last_seconds)
|
||||
tsm_timer_milliseconds((uint16_t) ((current_seconds - last_seconds) * 1000));
|
||||
|
||||
if (Error_Detected){break;}
|
||||
/* wait until the device is bound, or timeout and quit */
|
||||
if (!found) {
|
||||
found = address_bind_request(Target_Device_Object_Instance,
|
||||
&max_apdu,
|
||||
&Target_Address);
|
||||
}
|
||||
if (found) {
|
||||
if (Request_Invoke_ID == 0) {
|
||||
printf("\nSending first GetEventInformation request ...\n");
|
||||
Request_Invoke_ID = Send_GetEvent(&Target_Address, NULL);
|
||||
} else if (More_Events) {
|
||||
printf("\nSending another GetEventInformation request ...\n");
|
||||
Request_Invoke_ID = Send_GetEvent(&Target_Address,
|
||||
&LastReceivedObjectIdentifier);
|
||||
More_Events = false;
|
||||
} else if (tsm_invoke_id_free(Request_Invoke_ID)) {
|
||||
if (Recieved_Ack) {
|
||||
break;
|
||||
}
|
||||
} else if (tsm_invoke_id_failed(Request_Invoke_ID)) {
|
||||
fprintf(stderr, "\rError: TSM Timeout!\r\n");
|
||||
tsm_free_invoke_id(Request_Invoke_ID);
|
||||
Error_Detected = true;
|
||||
/* try again or abort? */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* increment timer - exit if timed out */
|
||||
elapsed_seconds += (current_seconds - last_seconds);
|
||||
if (elapsed_seconds > timeout_seconds) {
|
||||
printf("\rError: APDU Timeout!\r\n");
|
||||
Error_Detected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* returns 0 bytes on timeout */
|
||||
pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
|
||||
|
||||
/* process */
|
||||
if (pdu_len) {
|
||||
npdu_handler(&src, &Rx_Buf[0], pdu_len);
|
||||
}
|
||||
|
||||
/* keep track of time for next check */
|
||||
last_seconds = current_seconds;
|
||||
}
|
||||
if (Error_Detected)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,514 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2009 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* environment variables used for the command line tools */
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "config.h"
|
||||
#include "bacdef.h"
|
||||
#include "apdu.h"
|
||||
#include "datalink.h"
|
||||
#include "handlers.h"
|
||||
#include "dlenv.h"
|
||||
#include "tsm.h"
|
||||
#if (BACNET_PROTOCOL_REVISION >= 17)
|
||||
#include "netport.h"
|
||||
#endif
|
||||
|
||||
/** @file dlenv.c Initialize the DataLink configuration. */
|
||||
|
||||
#if defined(BACDL_BIP)
|
||||
/* timer used to renew Foreign Device Registration */
|
||||
static uint16_t BBMD_Timer_Seconds;
|
||||
/* BBMD variables */
|
||||
static long bbmd_timetolive_seconds = 60000;
|
||||
static long bbmd_port = 0xBAC0;
|
||||
static long bbmd_address = 0;
|
||||
static long bbmd_mask = 0xFFFFFFFF;
|
||||
static int bbmd_result = 0;
|
||||
static BBMD_TABLE_ENTRY BBMD_Table_Entry;
|
||||
|
||||
|
||||
/* Simple setters for BBMD registration variables. */
|
||||
|
||||
/** Sets the IPv4 address for BBMD registration.
|
||||
* If not set here or provided by Environment variables,
|
||||
* no BBMD registration will occur.
|
||||
* @param address - IPv4 address (long) of BBMD to register with,
|
||||
* in network byte order.
|
||||
*/
|
||||
void dlenv_bbmd_address_set(
|
||||
long address)
|
||||
{
|
||||
bbmd_address = address;
|
||||
}
|
||||
|
||||
/** Set the port for BBMD registration.
|
||||
* Default if not set is 0xBAC0.
|
||||
* @param port - The port number (provided in network byte order).
|
||||
*/
|
||||
void dlenv_bbmd_port_set(
|
||||
int port)
|
||||
{
|
||||
bbmd_port = port;
|
||||
}
|
||||
|
||||
/** Set the Lease Time (Time-to-Live) for BBMD registration.
|
||||
* Default if not set is 60000 (1000 minutes).
|
||||
* @param ttl_secs - The Lease Time, in seconds.
|
||||
*/
|
||||
void dlenv_bbmd_ttl_set(
|
||||
int ttl_secs)
|
||||
{
|
||||
bbmd_timetolive_seconds = ttl_secs;
|
||||
}
|
||||
|
||||
/** Get the result of the last attempt to register with the indicated BBMD.
|
||||
* If we sent a foreign registration request, then see if we've received
|
||||
* a NAK in our BVLC handler.
|
||||
*
|
||||
* @return Positive number (of bytes sent) if registration was successful,
|
||||
* 0 if no registration request was made, or
|
||||
* -1 if registration attempt failed.
|
||||
*/
|
||||
int dlenv_bbmd_result(
|
||||
void)
|
||||
{
|
||||
if ((bbmd_result > 0) &&
|
||||
(bvlc_get_last_result() == BVLC_RESULT_REGISTER_FOREIGN_DEVICE_NAK))
|
||||
return -1;
|
||||
/* Else, show our send: */
|
||||
return bbmd_result;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Register as a Foreign Device with the designated BBMD.
|
||||
* @ingroup DataLink
|
||||
* The BBMD's address, port, and lease time must be provided by
|
||||
* internal variables or Environment variables.
|
||||
* If no address for the BBMD is provided, no BBMD registration will occur.
|
||||
*
|
||||
* The Environment Variables depend on define of BACDL_BIP:
|
||||
* - BACNET_BBMD_PORT - 0..65534, defaults to 47808
|
||||
* - BACNET_BBMD_TIMETOLIVE - 0..65535 seconds, defaults to 60000
|
||||
* - BACNET_BBMD_ADDRESS - dotted IPv4 address
|
||||
* @return Positive number (of bytes sent) on success,
|
||||
* 0 if no registration request is sent, or
|
||||
* -1 if registration fails.
|
||||
*/
|
||||
int dlenv_register_as_foreign_device(
|
||||
void)
|
||||
{
|
||||
int retval = 0;
|
||||
#if defined(BACDL_BIP)
|
||||
char *pEnv = NULL;
|
||||
unsigned a[4] = {0};
|
||||
char bbmd_env[32] = "";
|
||||
unsigned entry_number = 0;
|
||||
int c;
|
||||
|
||||
pEnv = getenv("BACNET_BBMD_PORT");
|
||||
if (pEnv) {
|
||||
bbmd_port = strtol(pEnv, NULL, 0);
|
||||
if (bbmd_port > 0xFFFF) {
|
||||
bbmd_port = 0xBAC0;
|
||||
}
|
||||
}
|
||||
pEnv = getenv("BACNET_BBMD_TIMETOLIVE");
|
||||
if (pEnv) {
|
||||
bbmd_timetolive_seconds = strtol(pEnv, NULL, 0);
|
||||
if (bbmd_timetolive_seconds > 0xFFFF) {
|
||||
bbmd_timetolive_seconds = 0xFFFF;
|
||||
}
|
||||
}
|
||||
pEnv = getenv("BACNET_BBMD_ADDRESS");
|
||||
if (pEnv) {
|
||||
bbmd_address = bip_getaddrbyname(pEnv);
|
||||
}
|
||||
if (bbmd_address) {
|
||||
struct in_addr addr;
|
||||
addr.s_addr = bbmd_address;
|
||||
fprintf(stderr, "Registering with BBMD at %s:%ld for %ld seconds\n",
|
||||
inet_ntoa(addr), bbmd_port, bbmd_timetolive_seconds);
|
||||
retval =
|
||||
bvlc_register_with_bbmd(bbmd_address, htons((uint16_t) bbmd_port),
|
||||
(uint16_t) bbmd_timetolive_seconds);
|
||||
if (retval < 0)
|
||||
fprintf(stderr, "FAILED to Register with BBMD at %s \n",
|
||||
inet_ntoa(addr));
|
||||
BBMD_Timer_Seconds = (uint16_t) bbmd_timetolive_seconds;
|
||||
} else {
|
||||
for (entry_number = 1; entry_number <= 128; entry_number++) {
|
||||
sprintf(bbmd_env,"BACNET_BDT_ADDR_%u", entry_number);
|
||||
pEnv = getenv(bbmd_env);
|
||||
if (pEnv) {
|
||||
bbmd_address = bip_getaddrbyname(pEnv);
|
||||
} else if (entry_number == 1) {
|
||||
/* BDT 1 is self (note: can be overridden) */
|
||||
bbmd_address = bip_get_addr();
|
||||
}
|
||||
if (bbmd_address) {
|
||||
bbmd_port = 0xBAC0;
|
||||
sprintf(bbmd_env,"BACNET_BDT_PORT_%u", entry_number);
|
||||
pEnv = getenv(bbmd_env);
|
||||
if (pEnv) {
|
||||
bbmd_port = strtol(pEnv, NULL, 0);
|
||||
if (bbmd_port > 0xFFFF) {
|
||||
bbmd_port = 0xBAC0;
|
||||
}
|
||||
} else if (entry_number == 1) {
|
||||
/* BDT 1 is self (note: can be overridden) */
|
||||
bbmd_port = bip_get_port();
|
||||
}
|
||||
bbmd_mask = 0xFFFFFFFF;
|
||||
sprintf(bbmd_env,"BACNET_BDT_MASK_%u", entry_number);
|
||||
pEnv = getenv(bbmd_env);
|
||||
if (pEnv) {
|
||||
c = sscanf(pEnv, "%3u.%3u.%3u.%3u",
|
||||
&a[0],&a[1],&a[2],&a[3]);
|
||||
if (c == 4) {
|
||||
bbmd_mask =
|
||||
((a[0]&0xFF)<<24)|((a[1]&0xFF)<<16)|
|
||||
((a[2]&0xFF)<<8)|(a[3]&0xFF);
|
||||
}
|
||||
}
|
||||
BBMD_Table_Entry.valid = true;
|
||||
BBMD_Table_Entry.dest_address.s_addr = bbmd_address;
|
||||
BBMD_Table_Entry.dest_port = bbmd_port;
|
||||
BBMD_Table_Entry.broadcast_mask.s_addr = bbmd_mask;
|
||||
bvlc_add_bdt_entry_local(&BBMD_Table_Entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
bbmd_result = retval;
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
|
||||
#if (BACNET_PROTOCOL_REVISION >= 17)
|
||||
#if defined(BACDL_BIP)
|
||||
/**
|
||||
* Datalink network port object settings
|
||||
*/
|
||||
static void dlenv_network_port_init(void)
|
||||
{
|
||||
uint32_t instance = 1;
|
||||
uint32_t address = 0;
|
||||
uint32_t broadcast = 0;
|
||||
uint32_t test_broadcast = 0;
|
||||
uint32_t mask = 0;
|
||||
uint16_t port = 0;
|
||||
uint8_t mac[4+2] = {0};
|
||||
uint8_t prefix = 0;
|
||||
|
||||
Network_Port_Object_Instance_Number_Set(0, instance);
|
||||
Network_Port_Name_Set(instance, "BACnet/IP Port");
|
||||
Network_Port_Type_Set(instance, PORT_TYPE_BIP);
|
||||
port = bip_get_port();
|
||||
Network_Port_BIP_Port_Set(instance, port);
|
||||
address = bip_get_addr();
|
||||
memcpy(&mac[0], &address, 4);
|
||||
memcpy(&mac[4], &port, 2);
|
||||
Network_Port_MAC_Address_Set(instance, &mac[0], 6);
|
||||
broadcast = bip_get_broadcast_addr();
|
||||
for (prefix = 0; prefix < 32; prefix++) {
|
||||
mask = htonl((0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF);
|
||||
test_broadcast = (address & mask) | (~mask);
|
||||
if (test_broadcast == broadcast) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Network_Port_IP_Subnet_Prefix_Set(instance, prefix);
|
||||
Network_Port_Link_Speed_Set(instance, 0.0);
|
||||
/* common NP data */
|
||||
Network_Port_Reliability_Set(instance, RELIABILITY_NO_FAULT_DETECTED);
|
||||
Network_Port_Out_Of_Service_Set(instance, false);
|
||||
Network_Port_Quality_Set(instance, PORT_QUALITY_UNKNOWN);
|
||||
Network_Port_APDU_Length_Set(instance, MAX_APDU);
|
||||
Network_Port_Network_Number_Set(instance, 0);
|
||||
/* last thing - clear pending changes - we don't want to set these
|
||||
since they are already set */
|
||||
Network_Port_Changes_Pending_Set(instance, false);
|
||||
}
|
||||
#elif defined(BACDL_MSTP)
|
||||
/**
|
||||
* Datalink network port object settings
|
||||
*/
|
||||
static void dlenv_network_port_init(void)
|
||||
{
|
||||
uint32_t instance = 1;
|
||||
uint8_t mac[1] = {0};
|
||||
|
||||
Network_Port_Object_Instance_Number_Set(0, instance);
|
||||
Network_Port_Name_Set(instance, "MS/TP Port");
|
||||
Network_Port_Type_Set(instance, PORT_TYPE_MSTP);
|
||||
Network_Port_MSTP_Max_Master_Set(instance, dlmstp_max_master());
|
||||
Network_Port_MSTP_Max_Info_Frames_Set(instance, dlmstp_max_info_frames());
|
||||
Network_Port_Link_Speed_Set(instance, dlmstp_baud_rate());
|
||||
mac[0] = dlmstp_mac_address();
|
||||
Network_Port_MAC_Address_Set(instance, &mac[0], 1);
|
||||
/* common NP data */
|
||||
Network_Port_Reliability_Set(instance, RELIABILITY_NO_FAULT_DETECTED);
|
||||
Network_Port_Out_Of_Service_Set(instance, false);
|
||||
Network_Port_Quality_Set(instance, PORT_QUALITY_UNKNOWN);
|
||||
Network_Port_APDU_Length_Set(instance, MAX_APDU);
|
||||
Network_Port_Network_Number_Set(instance, 0);
|
||||
/* last thing - clear pending changes - we don't want to set these
|
||||
since they are already set */
|
||||
Network_Port_Changes_Pending_Set(instance, false);
|
||||
}
|
||||
#elif defined(BACDL_BIP6)
|
||||
/**
|
||||
* Datalink network port object settings
|
||||
*/
|
||||
static void dlenv_network_port_init(void)
|
||||
{
|
||||
uint32_t instance = 1;
|
||||
uint8_t prefix = 0;
|
||||
BACNET_ADDRESS addr = {0};
|
||||
BACNET_IP6_ADDRESS addr6 = {0};
|
||||
|
||||
Network_Port_Object_Instance_Number_Set(0, instance);
|
||||
Network_Port_Name_Set(instance, "BACnet/IPv6 Port");
|
||||
Network_Port_Type_Set(instance, PORT_TYPE_BIP6);
|
||||
Network_Port_BIP6_Port_Set(instance, bip6_get_port());
|
||||
bip6_get_my_address(&addr);
|
||||
Network_Port_MAC_Address_Set(instance, &addr.mac[0], addr.mac_len);
|
||||
bip6_get_addr(&addr6);
|
||||
Network_Port_IPv6_Address_Set(instance, &addr6.address[0]);
|
||||
bip6_get_broadcast_addr(&addr6);
|
||||
Network_Port_IPv6_Multicast_Address_Set(instance, &addr6.address[0]);
|
||||
Network_Port_IPv6_Subnet_Prefix_Set(instance, prefix);
|
||||
|
||||
Network_Port_Reliability_Set(instance, RELIABILITY_NO_FAULT_DETECTED);
|
||||
Network_Port_Link_Speed_Set(instance, 0.0);
|
||||
Network_Port_Out_Of_Service_Set(instance, false);
|
||||
Network_Port_Quality_Set(instance, PORT_QUALITY_UNKNOWN);
|
||||
Network_Port_APDU_Length_Set(instance, MAX_APDU);
|
||||
Network_Port_Network_Number_Set(instance, 0);
|
||||
/* last thing - clear pending changes - we don't want to set these
|
||||
since they are already set */
|
||||
Network_Port_Changes_Pending_Set(instance, false);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
/**
|
||||
* Datalink network port object settings
|
||||
*/
|
||||
static void dlenv_network_port_init(void)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Datalink maintenance timer
|
||||
* @ingroup DataLink
|
||||
*
|
||||
* Call this function to renew our Foreign Device Registration
|
||||
* @param elapsed_seconds Number of seconds that have elapsed since last called.
|
||||
*/
|
||||
void dlenv_maintenance_timer(
|
||||
uint16_t elapsed_seconds)
|
||||
{
|
||||
#if defined(BACDL_BIP)
|
||||
if (BBMD_Timer_Seconds) {
|
||||
if (BBMD_Timer_Seconds <= elapsed_seconds) {
|
||||
BBMD_Timer_Seconds = 0;
|
||||
} else {
|
||||
BBMD_Timer_Seconds -= elapsed_seconds;
|
||||
}
|
||||
if (BBMD_Timer_Seconds == 0) {
|
||||
(void) dlenv_register_as_foreign_device();
|
||||
/* If that failed (negative), maybe just a network issue.
|
||||
* If nothing happened (0), may be un/misconfigured.
|
||||
* Set up to try again later in all cases. */
|
||||
BBMD_Timer_Seconds = (uint16_t) bbmd_timetolive_seconds;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Initialize the DataLink configuration from Environment variables,
|
||||
* or else to defaults.
|
||||
* @ingroup DataLink
|
||||
* The items configured depend on which BACDL_ the code is built for,
|
||||
* eg, BACDL_BIP.
|
||||
*
|
||||
* For most items, checks first for an environment variable, and, if
|
||||
* found, uses that to set the item's value. Otherwise, will set
|
||||
* to a default value.
|
||||
*
|
||||
* The Environment Variables, by BACDL_ type, are:
|
||||
* - BACDL_ALL: (the general-purpose solution)
|
||||
* - BACNET_DATALINK to set which BACDL_ type we are using.
|
||||
* - (Any):
|
||||
* - 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_APDU_RETRIES - indicate the maximum number of times that
|
||||
* an APDU shall be retransmitted.
|
||||
* - BACNET_IFACE - set this value to dotted IP address (Windows) of
|
||||
* the interface (see ipconfig command on Windows) for which you
|
||||
* want to bind. On Linux, set this to the /dev interface
|
||||
* (i.e. eth0, arc0). Default is eth0 on Linux, and the default
|
||||
* interface on Windows. Hence, if there is only a single network
|
||||
* interface on Windows, the applications will choose it, and this
|
||||
* setting will not be needed.
|
||||
* - BACDL_BIP: (BACnet/IP)
|
||||
* - BACNET_IP_PORT - UDP/IP port number (0..65534) used for BACnet/IP
|
||||
* communications. Default is 47808 (0xBAC0).
|
||||
* - 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.
|
||||
* - BACNET_BDT_ADDR_1 - dotted IPv4 address of the BBMD table entry 1..128
|
||||
* - BACNET_BDT_PORT_1 - UDP port of the BBMD table entry 1..128 (optional)
|
||||
* - BACNET_BDT_MASK_1 - dotted IPv4 mask of the BBMD table
|
||||
* entry 1..128 (optional)
|
||||
* - BACNET_IP_NAT_ADDR - dotted IPv4 address of the public facing router
|
||||
* - BACDL_MSTP: (BACnet MS/TP)
|
||||
* - BACNET_MAX_INFO_FRAMES
|
||||
* - BACNET_MAX_MASTER
|
||||
* - BACNET_MSTP_BAUD
|
||||
* - BACNET_MSTP_MAC
|
||||
* - BACDL_BIP6: (BACnet/IPv6)
|
||||
* - BACNET_BIP6_PORT - UDP/IP port number (0..65534) used for BACnet/IPv6
|
||||
* communications. Default is 47808 (0xBAC0).
|
||||
* - BACNET_BIP6_BROADCAST - FF05::BAC0 or FF02::BAC0 or ...
|
||||
*/
|
||||
void dlenv_init(
|
||||
void)
|
||||
{
|
||||
char *pEnv = NULL;
|
||||
|
||||
#if defined(BACDL_ALL)
|
||||
pEnv = getenv("BACNET_DATALINK");
|
||||
if (pEnv) {
|
||||
datalink_set(pEnv);
|
||||
} else {
|
||||
datalink_set(NULL);
|
||||
}
|
||||
#endif
|
||||
#if defined(BACDL_BIP6)
|
||||
BACNET_IP6_ADDRESS addr;
|
||||
pEnv = getenv("BACNET_BIP6_BROADCAST");
|
||||
if (pEnv) {
|
||||
bvlc6_address_set(&addr,
|
||||
(uint16_t) strtol(pEnv, NULL, 0), 0, 0, 0, 0, 0, 0,
|
||||
BIP6_MULTICAST_GROUP_ID);
|
||||
bip6_set_broadcast_addr(&addr);
|
||||
} else {
|
||||
bvlc6_address_set(&addr,
|
||||
BIP6_MULTICAST_SITE_LOCAL, 0, 0, 0, 0, 0, 0,
|
||||
BIP6_MULTICAST_GROUP_ID);
|
||||
bip6_set_broadcast_addr(&addr);
|
||||
}
|
||||
pEnv = getenv("BACNET_BIP6_PORT");
|
||||
if (pEnv) {
|
||||
bip6_set_port((uint16_t) strtol(pEnv, NULL, 0));
|
||||
} else {
|
||||
bip6_set_port(0xBAC0);
|
||||
}
|
||||
#endif
|
||||
#if defined(BACDL_BIP)
|
||||
#if defined(BIP_DEBUG)
|
||||
BIP_Debug = true;
|
||||
#endif
|
||||
pEnv = getenv("BACNET_IP_PORT");
|
||||
if (pEnv) {
|
||||
bip_set_port(htons((uint16_t) strtol(pEnv, NULL, 0)));
|
||||
} else {
|
||||
/* BIP_Port is statically initialized to 0xBAC0,
|
||||
* so if it is different, then it was programmatically altered,
|
||||
* and we shouldn't just stomp on it here.
|
||||
* Unless it is set below 1024, since:
|
||||
* "The range for well-known ports managed by the IANA is 0-1023."
|
||||
*/
|
||||
if (ntohs(bip_get_port()) < 1024)
|
||||
bip_set_port(htons(0xBAC0));
|
||||
}
|
||||
pEnv = getenv("BACNET_IP_NAT_ADDR");
|
||||
if (pEnv) {
|
||||
struct in_addr nat_addr;
|
||||
nat_addr.s_addr = bip_getaddrbyname(pEnv);
|
||||
if (nat_addr.s_addr) {
|
||||
bvlc_set_global_address_for_nat(&nat_addr);
|
||||
}
|
||||
}
|
||||
#elif defined(BACDL_MSTP)
|
||||
pEnv = getenv("BACNET_MAX_INFO_FRAMES");
|
||||
if (pEnv) {
|
||||
dlmstp_set_max_info_frames(strtol(pEnv, NULL, 0));
|
||||
} else {
|
||||
dlmstp_set_max_info_frames(1);
|
||||
}
|
||||
pEnv = getenv("BACNET_MAX_MASTER");
|
||||
if (pEnv) {
|
||||
dlmstp_set_max_master(strtol(pEnv, NULL, 0));
|
||||
} else {
|
||||
dlmstp_set_max_master(127);
|
||||
}
|
||||
pEnv = getenv("BACNET_MSTP_BAUD");
|
||||
if (pEnv) {
|
||||
dlmstp_set_baud_rate(strtol(pEnv, NULL, 0));
|
||||
} else {
|
||||
dlmstp_set_baud_rate(38400);
|
||||
}
|
||||
pEnv = getenv("BACNET_MSTP_MAC");
|
||||
if (pEnv) {
|
||||
dlmstp_set_mac_address(strtol(pEnv, NULL, 0));
|
||||
} else {
|
||||
dlmstp_set_mac_address(127);
|
||||
}
|
||||
#endif
|
||||
pEnv = getenv("BACNET_APDU_TIMEOUT");
|
||||
if (pEnv) {
|
||||
apdu_timeout_set((uint16_t) strtol(pEnv, NULL, 0));
|
||||
} else {
|
||||
#if defined(BACDL_MSTP)
|
||||
apdu_timeout_set(60000);
|
||||
#endif
|
||||
}
|
||||
pEnv = getenv("BACNET_APDU_RETRIES");
|
||||
if (pEnv) {
|
||||
apdu_retries_set((uint8_t) strtol(pEnv, NULL, 0));
|
||||
}
|
||||
/* === Initialize the Datalink Here === */
|
||||
if (!datalink_init(getenv("BACNET_IFACE"))) {
|
||||
exit(1);
|
||||
}
|
||||
#if (MAX_TSM_TRANSACTIONS)
|
||||
pEnv = getenv("BACNET_INVOKE_ID");
|
||||
if (pEnv) {
|
||||
tsm_invokeID_set((uint8_t) strtol(pEnv, NULL, 0));
|
||||
}
|
||||
#endif
|
||||
dlenv_network_port_init();
|
||||
dlenv_register_as_foreign_device();
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
|
||||
*
|
||||
* 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacerror.h"
|
||||
#include "bactext.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "alarm_ack.h"
|
||||
#include "handlers.h"
|
||||
#include "device.h"
|
||||
|
||||
|
||||
/** @file h_alarm_ack.c Handles Alarm Acknowledgment. */
|
||||
|
||||
static alarm_ack_function Alarm_Ack[MAX_BACNET_OBJECT_TYPE];
|
||||
|
||||
void handler_alarm_ack_set(
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
alarm_ack_function pFunction)
|
||||
{
|
||||
if (object_type < MAX_BACNET_OBJECT_TYPE) {
|
||||
Alarm_Ack[object_type] = pFunction;
|
||||
}
|
||||
}
|
||||
|
||||
/** Handler for an Alarm/Event Acknowledgement.
|
||||
* @ingroup ALMACK
|
||||
* This handler will be invoked by apdu_handler() if it has been enabled
|
||||
* by a call to apdu_set_confirmed_handler().
|
||||
* This handler builds a response packet, which is
|
||||
* - an Abort if
|
||||
* - the message is segmented
|
||||
* - if decoding fails
|
||||
* - Otherwise, sends a simple ACK
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
void handler_alarm_ack(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
int ack_result = 0;
|
||||
BACNET_ADDRESS my_address;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ALARM_ACK_DATA data;
|
||||
BACNET_ERROR_CODE error_code;
|
||||
|
||||
/* encode the NPDU portion of the packet */
|
||||
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) {
|
||||
/* we don't support segmentation - send an abort */
|
||||
len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED,
|
||||
true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Alarm Ack: Segmented message. Sending Abort!\n");
|
||||
#endif
|
||||
goto AA_ABORT;
|
||||
}
|
||||
|
||||
len =
|
||||
alarm_ack_decode_service_request(service_request, service_len, &data);
|
||||
#if PRINT_ENABLED
|
||||
if (len <= 0)
|
||||
fprintf(stderr, "Alarm Ack: Unable to decode Request!\n");
|
||||
#endif
|
||||
if (len < 0) {
|
||||
/* bad decoding - send an abort */
|
||||
len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_OTHER, true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Alarm Ack: Bad Encoding. Sending Abort!\n");
|
||||
#endif
|
||||
goto AA_ABORT;
|
||||
}
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Alarm Ack Operation: Received acknowledge for object id (%d, %lu) from %s for process id %lu \n",
|
||||
data.eventObjectIdentifier.type,
|
||||
(unsigned long) data.eventObjectIdentifier.instance,
|
||||
data.ackSource.value, (unsigned long) data.ackProcessIdentifier);
|
||||
#endif
|
||||
|
||||
/* BACnet Testing Observed Incident oi00105
|
||||
ACK of a non-existent object returned the incorrect error code
|
||||
Revealed by BACnet Test Client v1.8.16 ( www.bac-test.com/bacnet-test-client-download )
|
||||
BC 135.1: 9.1.3.3-A
|
||||
Any discussions can be directed to edward@bac-test.com */
|
||||
if (!Device_Valid_Object_Id(data.eventObjectIdentifier.type, data.eventObjectIdentifier.instance))
|
||||
{
|
||||
len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM, ERROR_CLASS_OBJECT, ERROR_CODE_UNKNOWN_OBJECT);
|
||||
}
|
||||
else if (Alarm_Ack[data.eventObjectIdentifier.type]) {
|
||||
|
||||
ack_result =
|
||||
Alarm_Ack[data.eventObjectIdentifier.type] (&data, &error_code);
|
||||
|
||||
switch (ack_result) {
|
||||
case 1:
|
||||
len =
|
||||
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Alarm Acknowledge: " "Sending Simple Ack!\n");
|
||||
#endif
|
||||
break;
|
||||
|
||||
case -1:
|
||||
len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM, ERROR_CLASS_OBJECT,
|
||||
error_code);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Alarm Acknowledge: error %s!\n",
|
||||
bactext_error_code_name(error_code));
|
||||
#endif
|
||||
break;
|
||||
|
||||
default:
|
||||
len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_OTHER, true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Alarm Acknowledge: abort other!\n");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM,
|
||||
ERROR_CLASS_OBJECT, ERROR_CODE_NO_ALARM_CONFIGURED);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Alarm Acknowledge: error %s!\n",
|
||||
bactext_error_code_name(ERROR_CODE_NO_ALARM_CONFIGURED));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
AA_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, "Alarm Acknowledge: " "Failed to send PDU (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacerror.h"
|
||||
#include "bacdcode.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "arf.h"
|
||||
/* demo objects */
|
||||
#if defined(BACFILE)
|
||||
#include "bacfile.h"
|
||||
#endif
|
||||
#include "handlers.h"
|
||||
|
||||
/** @file h_arf.c Handles Atomic Read File request. */
|
||||
|
||||
/*
|
||||
from BACnet SSPC-135-2004
|
||||
|
||||
14. FILE ACCESS SERVICES
|
||||
|
||||
This clause defines the set of services used to access and
|
||||
manipulate files contained in BACnet devices. The concept of files
|
||||
is used here as a network-visible representation for a collection
|
||||
of octets of arbitrary length and meaning. This is an abstract
|
||||
concept only and does not imply the use of disk, tape or other
|
||||
mass storage devices in the server devices. These services may
|
||||
be used to access vendor-defined files as well as specific
|
||||
files defined in the BACnet protocol standard.
|
||||
Every file that is accessible by File Access Services shall
|
||||
have a corresponding File object in the BACnet device. This File
|
||||
object is used to identify the particular file by name. In addition,
|
||||
the File object provides access to "header information," such
|
||||
as the file's total size, creation date, and type. File Access
|
||||
Services may model files in two ways: as a continuous stream of
|
||||
octets or as a contiguous sequence of numbered records.
|
||||
The File Access Services provide atomic read and write operations.
|
||||
In this context "atomic" means that during the execution
|
||||
of a read or write operation, no other AtomicReadFile or
|
||||
AtomicWriteFile operations are allowed for the same file.
|
||||
Synchronization of these services with internal operations
|
||||
of the BACnet device is a local matter and is not defined by this
|
||||
standard.
|
||||
|
||||
14.1 AtomicReadFile Service
|
||||
|
||||
14.1.5 Service Procedure
|
||||
|
||||
The responding BACnet-user shall first verify the validity
|
||||
of the 'File Identifier' parameter and return a 'Result(-)' response
|
||||
with the appropriate error class and code if the File object
|
||||
is unknown, if there is currently another AtomicReadFile or
|
||||
AtomicWriteFile service in progress, or if the File object is
|
||||
currently inaccessible for another reason. If the 'File Start
|
||||
Position' parameter or the 'File Start Record' parameter is
|
||||
either less than 0 or exceeds the actual file size, then the appropriate
|
||||
error is returned in a 'Result(-)' response. If not, then the
|
||||
responding BACnet-user shall read the number of octets specified by
|
||||
'Requested Octet Count' or the number of records specified by
|
||||
'Requested Record Count'. If the number of remaining octets or
|
||||
records is less than the requested amount, then the length of
|
||||
the 'File Data' returned or 'Returned Record Count' shall indicate
|
||||
the actual number read. If the returned response contains the
|
||||
last octet or record of the file, then the 'End Of File' parameter
|
||||
shall be TRUE, otherwise FALSE.
|
||||
*/
|
||||
|
||||
#if defined(BACFILE)
|
||||
void handler_atomic_read_file(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
BACNET_ATOMIC_READ_FILE_DATA data;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
bool error = false;
|
||||
int bytes_sent = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT;
|
||||
BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Received Atomic-Read-File Request!\n");
|
||||
#endif
|
||||
/* encode the NPDU portion of the packet */
|
||||
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, "ARF: Segmented Message. Sending Abort!\n");
|
||||
#endif
|
||||
goto ARF_ABORT;
|
||||
}
|
||||
len = arf_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, "Bad Encoding. Sending Abort!\n");
|
||||
#endif
|
||||
goto ARF_ABORT;
|
||||
}
|
||||
if (data.object_type == OBJECT_FILE) {
|
||||
if (!bacfile_valid_instance(data.object_instance)) {
|
||||
error = true;
|
||||
} else if (data.access == FILE_STREAM_ACCESS) {
|
||||
if (data.type.stream.requestedOctetCount <
|
||||
octetstring_capacity(&data.fileData[0])) {
|
||||
bacfile_read_stream_data(&data);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "ARF: Stream offset %d, %d octets.\n",
|
||||
data.type.stream.fileStartPosition,
|
||||
data.type.stream.requestedOctetCount);
|
||||
#endif
|
||||
len =
|
||||
arf_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, &data);
|
||||
} else {
|
||||
len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Too Big To Send (%d >= %d). Sending Abort!\n",
|
||||
data.type.stream.requestedOctetCount,
|
||||
(int)octetstring_capacity(&data.fileData[0]));
|
||||
#endif
|
||||
}
|
||||
} else if (data.access == FILE_RECORD_ACCESS) {
|
||||
if (data.type.record.fileStartRecord >=
|
||||
BACNET_READ_FILE_RECORD_COUNT) {
|
||||
error_class = ERROR_CLASS_SERVICES;
|
||||
error_code = ERROR_CODE_INVALID_FILE_START_POSITION;
|
||||
error = true;
|
||||
} else if (bacfile_read_stream_data(&data)) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "ARF: fileStartRecord %d, %u RecordCount.\n",
|
||||
data.type.record.fileStartRecord,
|
||||
data.type.record.RecordCount);
|
||||
#endif
|
||||
len =
|
||||
arf_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, &data);
|
||||
} else {
|
||||
error = true;
|
||||
error_class = ERROR_CLASS_OBJECT;
|
||||
error_code = ERROR_CODE_FILE_ACCESS_DENIED;
|
||||
}
|
||||
} else {
|
||||
error = true;
|
||||
error_class = ERROR_CLASS_SERVICES;
|
||||
error_code = ERROR_CODE_INVALID_FILE_ACCESS_METHOD;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Record Access Requested. Sending Error!\n");
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
error = true;
|
||||
error_class = ERROR_CLASS_SERVICES;
|
||||
error_code = ERROR_CODE_INCONSISTENT_OBJECT_TYPE;
|
||||
}
|
||||
if (error) {
|
||||
len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_ATOMIC_READ_FILE,
|
||||
error_class, error_code);
|
||||
}
|
||||
ARF_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;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,79 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "arf.h"
|
||||
#if defined(BACFILE)
|
||||
#include "bacfile.h"
|
||||
#endif
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
|
||||
/** @file h_arf_a.c Handles Acknowledgment of Atomic Read File response. */
|
||||
|
||||
/* We performed an AtomicReadFile Request, */
|
||||
/* and here is the data from the server */
|
||||
/* Note: it does not have to be the same file=instance */
|
||||
/* that someone can read from us. It is common to */
|
||||
/* use the description as the file name. */
|
||||
#if defined(BACFILE)
|
||||
void handler_atomic_read_file_ack(
|
||||
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;
|
||||
uint32_t instance = 0;
|
||||
|
||||
(void) src;
|
||||
/* get the file instance from the tsm data before freeing it */
|
||||
instance = bacfile_instance_from_tsm(service_data->invoke_id);
|
||||
len = arf_ack_decode_service_request(service_request, service_len, &data);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Received Read-File Ack!\n");
|
||||
#endif
|
||||
if ((len > 0) && (instance <= BACNET_MAX_INSTANCE)) {
|
||||
/* write the data received to the file specified */
|
||||
if (data.access == FILE_STREAM_ACCESS) {
|
||||
bacfile_read_ack_stream_data(instance, &data);
|
||||
} else if (data.access == FILE_RECORD_ACCESS) {
|
||||
bacfile_read_ack_record_data(instance, &data);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,190 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacstr.h"
|
||||
#include "bacerror.h"
|
||||
#include "bacdcode.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "awf.h"
|
||||
/* demo objects */
|
||||
#include "device.h"
|
||||
#if defined(BACFILE)
|
||||
#include "bacfile.h"
|
||||
#endif
|
||||
#include "handlers.h"
|
||||
|
||||
/** @file h_awf.c Handles Atomic Write File request. */
|
||||
|
||||
/*
|
||||
from BACnet SSPC-135-2004
|
||||
|
||||
14. FILE ACCESS SERVICES
|
||||
|
||||
This clause defines the set of services used to access and
|
||||
manipulate files contained in BACnet devices. The concept of files
|
||||
is used here as a network-visible representation for a collection
|
||||
of octets of arbitrary length and meaning. This is an abstract
|
||||
concept only and does not imply the use of disk, tape or other
|
||||
mass storage devices in the server devices. These services may
|
||||
be used to access vendor-defined files as well as specific
|
||||
files defined in the BACnet protocol standard.
|
||||
Every file that is accessible by File Access Services shall
|
||||
have a corresponding File object in the BACnet device. This File
|
||||
object is used to identify the particular file by name. In addition,
|
||||
the File object provides access to "header information," such
|
||||
as the file's total size, creation date, and type. File Access
|
||||
Services may model files in two ways: as a continuous stream of
|
||||
octets or as a contiguous sequence of numbered records.
|
||||
The File Access Services provide atomic read and write operations.
|
||||
In this context "atomic" means that during the execution
|
||||
of a read or write operation, no other AtomicReadFile or
|
||||
AtomicWriteFile operations are allowed for the same file.
|
||||
Synchronization of these services with internal operations
|
||||
of the BACnet device is a local matter and is not defined by this
|
||||
standard.
|
||||
*/
|
||||
|
||||
#if defined(BACFILE)
|
||||
void handler_atomic_write_file(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA data;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
bool error = false;
|
||||
int bytes_sent = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT;
|
||||
BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Received AtomicWriteFile Request!\n");
|
||||
#endif
|
||||
/* encode the NPDU portion of the packet */
|
||||
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, "Segmented Message. Sending Abort!\n");
|
||||
#endif
|
||||
goto AWF_ABORT;
|
||||
}
|
||||
len = awf_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, "Bad Encoding. Sending Abort!\n");
|
||||
#endif
|
||||
goto AWF_ABORT;
|
||||
}
|
||||
if (data.object_type == OBJECT_FILE) {
|
||||
if (!bacfile_valid_instance(data.object_instance)) {
|
||||
error = true;
|
||||
} else if (data.access == FILE_STREAM_ACCESS) {
|
||||
if (bacfile_write_stream_data(&data)) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "AWF: Stream offset %d, %d bytes\n",
|
||||
data.type.stream.fileStartPosition,
|
||||
(int)octetstring_length(&data.fileData[0]));
|
||||
#endif
|
||||
len =
|
||||
awf_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, &data);
|
||||
} else {
|
||||
error = true;
|
||||
error_class = ERROR_CLASS_OBJECT;
|
||||
error_code = ERROR_CODE_FILE_ACCESS_DENIED;
|
||||
}
|
||||
} else if (data.access == FILE_RECORD_ACCESS) {
|
||||
if (bacfile_write_record_data(&data)) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "AWF: StartRecord %d, RecordCount %u\n",
|
||||
data.type.record.fileStartRecord,
|
||||
data.type.record.returnedRecordCount);
|
||||
#endif
|
||||
len =
|
||||
awf_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, &data);
|
||||
} else {
|
||||
error = true;
|
||||
error_class = ERROR_CLASS_OBJECT;
|
||||
error_code = ERROR_CODE_FILE_ACCESS_DENIED;
|
||||
}
|
||||
} else {
|
||||
error = true;
|
||||
error_class = ERROR_CLASS_SERVICES;
|
||||
error_code = ERROR_CODE_INVALID_FILE_ACCESS_METHOD;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Record Access Requested. Sending Error!\n");
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
error = true;
|
||||
error_class = ERROR_CLASS_SERVICES;
|
||||
error_code = ERROR_CODE_INCONSISTENT_OBJECT_TYPE;
|
||||
}
|
||||
if (error) {
|
||||
len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_ATOMIC_WRITE_FILE,
|
||||
error_class, error_code);
|
||||
}
|
||||
AWF_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;
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,163 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2011 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
/* special for this module */
|
||||
#include "cov.h"
|
||||
#include "bactext.h"
|
||||
#include "handlers.h"
|
||||
|
||||
#ifndef MAX_COV_PROPERTIES
|
||||
#define MAX_COV_PROPERTIES 2
|
||||
#endif
|
||||
|
||||
/** @file h_ccov.c Handles Confirmed COV Notifications. */
|
||||
|
||||
/* */
|
||||
/** Handler for an Confirmed COV Notification.
|
||||
* @ingroup DSCOV
|
||||
* Decodes the received list of Properties to update,
|
||||
* and print them out with the subscription information.
|
||||
* @note Nothing is specified in BACnet about what to do with the
|
||||
* information received from Confirmed COV Notifications.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
void handler_ccov_notification(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_COV_DATA cov_data;
|
||||
BACNET_PROPERTY_VALUE property_value[MAX_COV_PROPERTIES];
|
||||
BACNET_PROPERTY_VALUE *pProperty_value = NULL;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
/* create linked list to store data if more
|
||||
than one property value is expected */
|
||||
bacapp_property_value_list_init(&property_value[0], MAX_COV_PROPERTIES);
|
||||
cov_data.listOfValues = &property_value[0];
|
||||
/* encode the NPDU portion of the packet */
|
||||
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 PRINT_ENABLED
|
||||
fprintf(stderr, "CCOV: Received Notification!\n");
|
||||
#endif
|
||||
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, "CCOV: Segmented message. Sending Abort!\n");
|
||||
#endif
|
||||
goto CCOV_ABORT;
|
||||
}
|
||||
/* decode the service request only */
|
||||
len =
|
||||
cov_notify_decode_service_request(service_request, service_len,
|
||||
&cov_data);
|
||||
#if PRINT_ENABLED
|
||||
if (len > 0) {
|
||||
fprintf(stderr, "CCOV: PID=%u ", cov_data.subscriberProcessIdentifier);
|
||||
fprintf(stderr, "instance=%u ", cov_data.initiatingDeviceIdentifier);
|
||||
fprintf(stderr, "%s %u ",
|
||||
bactext_object_type_name(cov_data.monitoredObjectIdentifier.type),
|
||||
cov_data.monitoredObjectIdentifier.instance);
|
||||
fprintf(stderr, "time remaining=%u seconds ", cov_data.timeRemaining);
|
||||
fprintf(stderr, "\n");
|
||||
pProperty_value = &property_value[0];
|
||||
while (pProperty_value) {
|
||||
fprintf(stderr, "CCOV: ");
|
||||
if (pProperty_value->propertyIdentifier < 512) {
|
||||
fprintf(stderr, "%s ",
|
||||
bactext_property_name
|
||||
(pProperty_value->propertyIdentifier));
|
||||
} else {
|
||||
fprintf(stderr, "proprietary %u ",
|
||||
pProperty_value->propertyIdentifier);
|
||||
}
|
||||
if (pProperty_value->propertyArrayIndex != BACNET_ARRAY_ALL) {
|
||||
fprintf(stderr, "%u ", pProperty_value->propertyArrayIndex);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
pProperty_value = pProperty_value->next;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* bad decoding or something we didn't understand - 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, "CCOV: Bad Encoding. Sending Abort!\n");
|
||||
#endif
|
||||
goto CCOV_ABORT;
|
||||
} else {
|
||||
len =
|
||||
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_COV_NOTIFICATION);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "CCOV: Sending Simple Ack!\n");
|
||||
#endif
|
||||
}
|
||||
CCOV_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, "CCOV: Failed to send PDU (%s)!\n", strerror(errno));
|
||||
}
|
||||
#else
|
||||
bytes_sent = bytes_sent;
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,910 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007-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.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacerror.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacaddr.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "reject.h"
|
||||
#include "cov.h"
|
||||
#include "tsm.h"
|
||||
#include "dcc.h"
|
||||
#if PRINT_ENABLED
|
||||
#include "bactext.h"
|
||||
#endif
|
||||
/* demo objects */
|
||||
#include "device.h"
|
||||
#include "handlers.h"
|
||||
|
||||
#ifndef MAX_COV_PROPERTIES
|
||||
#define MAX_COV_PROPERTIES 2
|
||||
#endif
|
||||
|
||||
/** @file h_cov.c Handles Change of Value (COV) services. */
|
||||
|
||||
typedef struct BACnet_COV_Address {
|
||||
bool valid:1;
|
||||
BACNET_ADDRESS dest;
|
||||
} BACNET_COV_ADDRESS;
|
||||
|
||||
/* note: This COV service only monitors the properties
|
||||
of an object that have been specified in the standard. */
|
||||
typedef struct BACnet_COV_Subscription_Flags {
|
||||
bool valid:1;
|
||||
bool issueConfirmedNotifications:1; /* optional */
|
||||
bool send_requested:1;
|
||||
} BACNET_COV_SUBSCRIPTION_FLAGS;
|
||||
|
||||
typedef struct BACnet_COV_Subscription {
|
||||
BACNET_COV_SUBSCRIPTION_FLAGS flag;
|
||||
uint8_t dest_index;
|
||||
uint8_t invokeID; /* for confirmed COV */
|
||||
uint32_t subscriberProcessIdentifier;
|
||||
uint32_t lifetime; /* optional */
|
||||
BACNET_OBJECT_ID monitoredObjectIdentifier;
|
||||
} BACNET_COV_SUBSCRIPTION;
|
||||
|
||||
#ifndef MAX_COV_SUBCRIPTIONS
|
||||
#define MAX_COV_SUBCRIPTIONS 128
|
||||
#endif
|
||||
static BACNET_COV_SUBSCRIPTION COV_Subscriptions[MAX_COV_SUBCRIPTIONS];
|
||||
#ifndef MAX_COV_ADDRESSES
|
||||
#define MAX_COV_ADDRESSES 16
|
||||
#endif
|
||||
static BACNET_COV_ADDRESS COV_Addresses[MAX_COV_ADDRESSES];
|
||||
|
||||
/**
|
||||
* Gets the address from the list of COV addresses
|
||||
*
|
||||
* @param index - offset into COV address list where address is stored
|
||||
* @param dest - address to be filled when found
|
||||
*
|
||||
* @return true if valid address, false if not valid or not found
|
||||
*/
|
||||
static BACNET_ADDRESS *cov_address_get(
|
||||
int index)
|
||||
{
|
||||
BACNET_ADDRESS *cov_dest = NULL;
|
||||
|
||||
if (index < MAX_COV_ADDRESSES) {
|
||||
if (COV_Addresses[index].valid) {
|
||||
cov_dest = &COV_Addresses[index].dest;
|
||||
}
|
||||
}
|
||||
|
||||
return cov_dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the address from the list of COV addresses, if it is not
|
||||
* used by other COV subscriptions
|
||||
*/
|
||||
static void cov_address_remove_unused(
|
||||
void)
|
||||
{
|
||||
unsigned index = 0;
|
||||
unsigned cov_index = 0;
|
||||
bool found = false;
|
||||
|
||||
for (cov_index = 0; cov_index < MAX_COV_ADDRESSES; cov_index++) {
|
||||
if (COV_Addresses[cov_index].valid) {
|
||||
found = false;
|
||||
for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) {
|
||||
if ((COV_Subscriptions[index].flag.valid) &&
|
||||
(COV_Subscriptions[index].dest_index == cov_index)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
COV_Addresses[cov_index].valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the address to the list of COV addresses
|
||||
*
|
||||
* @param dest - address to be added if there is room in the list
|
||||
*
|
||||
* @return index number 0..N, or -1 if unable to add
|
||||
*/
|
||||
static int cov_address_add(
|
||||
BACNET_ADDRESS * dest)
|
||||
{
|
||||
int index = -1;
|
||||
unsigned i = 0;
|
||||
bool found = false;
|
||||
bool valid = false;
|
||||
BACNET_ADDRESS *cov_dest = NULL;
|
||||
|
||||
if (dest) {
|
||||
for (i = 0; i < MAX_COV_ADDRESSES; i++) {
|
||||
valid = COV_Addresses[i].valid;
|
||||
if (valid) {
|
||||
cov_dest = &COV_Addresses[i].dest;
|
||||
found = bacnet_address_same(dest, cov_dest);
|
||||
if (found) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
/* find a free place to add a new address */
|
||||
for (i = 0; i < MAX_COV_ADDRESSES; i++) {
|
||||
valid = COV_Addresses[i].valid;
|
||||
if (!valid) {
|
||||
index = i;
|
||||
cov_dest = &COV_Addresses[i].dest;
|
||||
bacnet_address_copy(cov_dest, dest);
|
||||
COV_Addresses[i].valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/*
|
||||
BACnetCOVSubscription ::= SEQUENCE {
|
||||
Recipient [0] BACnetRecipientProcess,
|
||||
BACnetRecipient ::= CHOICE {
|
||||
device [0] BACnetObjectIdentifier,
|
||||
address [1] BACnetAddress
|
||||
BACnetAddress ::= SEQUENCE {
|
||||
network-number Unsigned16, -- A value of 0 indicates the local network
|
||||
mac-address OCTET STRING -- A string of length 0 indicates a broadcast
|
||||
}
|
||||
}
|
||||
BACnetRecipientProcess ::= SEQUENCE {
|
||||
recipient [0] BACnetRecipient,
|
||||
processIdentifier [1] Unsigned32
|
||||
}
|
||||
MonitoredPropertyReference [1] BACnetObjectPropertyReference,
|
||||
BACnetObjectPropertyReference ::= SEQUENCE {
|
||||
objectIdentifier [0] BACnetObjectIdentifier,
|
||||
propertyIdentifier [1] BACnetPropertyIdentifier,
|
||||
propertyArrayIndex [2] Unsigned OPTIONAL -- used only with array datatype
|
||||
-- if omitted with an array the entire array is referenced
|
||||
}
|
||||
IssueConfirmedNotifications [2] BOOLEAN,
|
||||
TimeRemaining [3] Unsigned,
|
||||
COVIncrement [4] REAL OPTIONAL
|
||||
*/
|
||||
|
||||
static int cov_encode_subscription(
|
||||
uint8_t * apdu,
|
||||
int max_apdu,
|
||||
BACNET_COV_SUBSCRIPTION * cov_subscription)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
BACNET_OCTET_STRING octet_string;
|
||||
BACNET_ADDRESS *dest = NULL;
|
||||
|
||||
|
||||
/* FIXME: unused parameter */
|
||||
max_apdu = max_apdu;
|
||||
if (!cov_subscription) {
|
||||
return 0;
|
||||
}
|
||||
dest = cov_address_get(cov_subscription->dest_index);
|
||||
if (!dest) {
|
||||
return 0;
|
||||
}
|
||||
/* Recipient [0] BACnetRecipientProcess - opening */
|
||||
len = encode_opening_tag(&apdu[apdu_len], 0);
|
||||
apdu_len += len;
|
||||
/* recipient [0] BACnetRecipient - opening */
|
||||
len = encode_opening_tag(&apdu[apdu_len], 0);
|
||||
apdu_len += len;
|
||||
/* CHOICE - address [1] BACnetAddress - opening */
|
||||
len = encode_opening_tag(&apdu[apdu_len], 1);
|
||||
apdu_len += len;
|
||||
/* network-number Unsigned16, */
|
||||
/* -- A value of 0 indicates the local network */
|
||||
len = encode_application_unsigned(&apdu[apdu_len], dest->net);
|
||||
apdu_len += len;
|
||||
/* mac-address OCTET STRING */
|
||||
/* -- A string of length 0 indicates a broadcast */
|
||||
if (dest->net) {
|
||||
octetstring_init(&octet_string, &dest->adr[0], dest->len);
|
||||
} else {
|
||||
octetstring_init(&octet_string, &dest->mac[0], dest->mac_len);
|
||||
}
|
||||
len = encode_application_octet_string(&apdu[apdu_len], &octet_string);
|
||||
apdu_len += len;
|
||||
/* CHOICE - address [1] BACnetAddress - closing */
|
||||
len = encode_closing_tag(&apdu[apdu_len], 1);
|
||||
apdu_len += len;
|
||||
/* recipient [0] BACnetRecipient - closing */
|
||||
len = encode_closing_tag(&apdu[apdu_len], 0);
|
||||
apdu_len += len;
|
||||
/* processIdentifier [1] Unsigned32 */
|
||||
len =
|
||||
encode_context_unsigned(&apdu[apdu_len], 1,
|
||||
cov_subscription->subscriberProcessIdentifier);
|
||||
apdu_len += len;
|
||||
/* Recipient [0] BACnetRecipientProcess - closing */
|
||||
len = encode_closing_tag(&apdu[apdu_len], 0);
|
||||
apdu_len += len;
|
||||
/* MonitoredPropertyReference [1] BACnetObjectPropertyReference, */
|
||||
len = encode_opening_tag(&apdu[apdu_len], 1);
|
||||
apdu_len += len;
|
||||
/* objectIdentifier [0] */
|
||||
len =
|
||||
encode_context_object_id(&apdu[apdu_len], 0,
|
||||
cov_subscription->monitoredObjectIdentifier.type,
|
||||
cov_subscription->monitoredObjectIdentifier.instance);
|
||||
apdu_len += len;
|
||||
/* propertyIdentifier [1] */
|
||||
/* FIXME: we are monitoring 2 properties! How to encode? */
|
||||
len = encode_context_enumerated(&apdu[apdu_len], 1, PROP_PRESENT_VALUE);
|
||||
apdu_len += len;
|
||||
/* MonitoredPropertyReference [1] - closing */
|
||||
len = encode_closing_tag(&apdu[apdu_len], 1);
|
||||
apdu_len += len;
|
||||
/* IssueConfirmedNotifications [2] BOOLEAN, */
|
||||
len =
|
||||
encode_context_boolean(&apdu[apdu_len], 2,
|
||||
cov_subscription->flag.issueConfirmedNotifications);
|
||||
apdu_len += len;
|
||||
/* TimeRemaining [3] Unsigned, */
|
||||
len =
|
||||
encode_context_unsigned(&apdu[apdu_len], 3,
|
||||
cov_subscription->lifetime);
|
||||
apdu_len += len;
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/** Handle a request to list all the COV subscriptions.
|
||||
* @ingroup DSCOV
|
||||
* Invoked by a request to read the Device object's PROP_ACTIVE_COV_SUBSCRIPTIONS.
|
||||
* Loops through the list of COV Subscriptions, and, for each valid one,
|
||||
* adds its description to the APDU.
|
||||
* @note This function needs some work to better handle buffer overruns.
|
||||
* @param apdu [out] Buffer in which the APDU contents are built.
|
||||
* @param max_apdu [in] Max length of the APDU buffer.
|
||||
* @return How many bytes were encoded in the buffer, or -2 if the response
|
||||
* would not fit within the buffer.
|
||||
*/
|
||||
int handler_cov_encode_subscriptions(
|
||||
uint8_t * apdu,
|
||||
int max_apdu)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
unsigned index = 0;
|
||||
|
||||
if (apdu) {
|
||||
for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) {
|
||||
if (COV_Subscriptions[index].flag.valid) {
|
||||
len =
|
||||
cov_encode_subscription(&apdu[apdu_len],
|
||||
max_apdu - apdu_len, &COV_Subscriptions[index]);
|
||||
apdu_len += len;
|
||||
/* TODO: too late here to notice that we overran the buffer */
|
||||
if (apdu_len > max_apdu) {
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/** Handler to initialize the COV list, clearing and disabling each entry.
|
||||
* @ingroup DSCOV
|
||||
*/
|
||||
void handler_cov_init(
|
||||
void)
|
||||
{
|
||||
unsigned index = 0;
|
||||
|
||||
for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) {
|
||||
COV_Subscriptions[index].flag.valid = false;
|
||||
COV_Subscriptions[index].dest_index = -1;
|
||||
COV_Subscriptions[index].subscriberProcessIdentifier = 0;
|
||||
COV_Subscriptions[index].monitoredObjectIdentifier.type =
|
||||
OBJECT_ANALOG_INPUT;
|
||||
COV_Subscriptions[index].monitoredObjectIdentifier.instance = 0;
|
||||
COV_Subscriptions[index].flag.issueConfirmedNotifications = false;
|
||||
COV_Subscriptions[index].invokeID = 0;
|
||||
COV_Subscriptions[index].lifetime = 0;
|
||||
COV_Subscriptions[index].flag.send_requested = false;
|
||||
}
|
||||
for (index = 0; index < MAX_COV_ADDRESSES; index++) {
|
||||
COV_Addresses[index].valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool cov_list_subscribe(
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_SUBSCRIBE_COV_DATA * cov_data,
|
||||
BACNET_ERROR_CLASS * error_class,
|
||||
BACNET_ERROR_CODE * error_code)
|
||||
{
|
||||
bool existing_entry = false;
|
||||
int index;
|
||||
int first_invalid_index = -1;
|
||||
bool found = true;
|
||||
bool address_match = false;
|
||||
BACNET_ADDRESS *dest = NULL;
|
||||
|
||||
/* unable to subscribe - resources? */
|
||||
/* unable to cancel subscription - other? */
|
||||
|
||||
/* existing? - match Object ID and Process ID and address */
|
||||
for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) {
|
||||
if (COV_Subscriptions[index].flag.valid) {
|
||||
dest = cov_address_get(COV_Subscriptions[index].dest_index);
|
||||
if (dest) {
|
||||
address_match = bacnet_address_same(src, dest);
|
||||
} else {
|
||||
/* skip address matching - we don't have an address */
|
||||
address_match = true;
|
||||
}
|
||||
if ((COV_Subscriptions[index].monitoredObjectIdentifier.type ==
|
||||
cov_data->monitoredObjectIdentifier.type) &&
|
||||
(COV_Subscriptions[index].monitoredObjectIdentifier.instance ==
|
||||
cov_data->monitoredObjectIdentifier.instance) &&
|
||||
(COV_Subscriptions[index].subscriberProcessIdentifier ==
|
||||
cov_data->subscriberProcessIdentifier) && address_match) {
|
||||
existing_entry = true;
|
||||
if (cov_data->cancellationRequest) {
|
||||
COV_Subscriptions[index].flag.valid = false;
|
||||
COV_Subscriptions[index].dest_index = -1;
|
||||
cov_address_remove_unused();
|
||||
} else {
|
||||
COV_Subscriptions[index].dest_index = cov_address_add(src);
|
||||
COV_Subscriptions[index].flag.issueConfirmedNotifications =
|
||||
cov_data->issueConfirmedNotifications;
|
||||
COV_Subscriptions[index].lifetime = cov_data->lifetime;
|
||||
COV_Subscriptions[index].flag.send_requested = true;
|
||||
}
|
||||
if (COV_Subscriptions[index].invokeID) {
|
||||
tsm_free_invoke_id(COV_Subscriptions[index].invokeID);
|
||||
COV_Subscriptions[index].invokeID = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (first_invalid_index < 0) {
|
||||
first_invalid_index = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!existing_entry && (first_invalid_index >= 0) &&
|
||||
(!cov_data->cancellationRequest)) {
|
||||
index = first_invalid_index;
|
||||
found = true;
|
||||
COV_Subscriptions[index].flag.valid = true;
|
||||
COV_Subscriptions[index].dest_index = cov_address_add(src);
|
||||
COV_Subscriptions[index].monitoredObjectIdentifier.type =
|
||||
cov_data->monitoredObjectIdentifier.type;
|
||||
COV_Subscriptions[index].monitoredObjectIdentifier.instance =
|
||||
cov_data->monitoredObjectIdentifier.instance;
|
||||
COV_Subscriptions[index].subscriberProcessIdentifier =
|
||||
cov_data->subscriberProcessIdentifier;
|
||||
COV_Subscriptions[index].flag.issueConfirmedNotifications =
|
||||
cov_data->issueConfirmedNotifications;
|
||||
COV_Subscriptions[index].invokeID = 0;
|
||||
COV_Subscriptions[index].lifetime = cov_data->lifetime;
|
||||
COV_Subscriptions[index].flag.send_requested = true;
|
||||
} else if (!existing_entry) {
|
||||
if (first_invalid_index < 0) {
|
||||
/* Out of resources */
|
||||
*error_class = ERROR_CLASS_RESOURCES;
|
||||
*error_code = ERROR_CODE_NO_SPACE_TO_ADD_LIST_ELEMENT;
|
||||
found = false;
|
||||
} else {
|
||||
/* cancellationRequest - valid object not subscribed */
|
||||
/* From BACnet Standard 135-2010-13.14.2
|
||||
...Cancellations that are issued for which no matching COV
|
||||
context can be found shall succeed as if a context had
|
||||
existed, returning 'Result(+)'. */
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static bool cov_send_request(
|
||||
BACNET_COV_SUBSCRIPTION * cov_subscription,
|
||||
BACNET_PROPERTY_VALUE * value_list)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
int bytes_sent = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
bool status = false; /* return value */
|
||||
BACNET_COV_DATA cov_data;
|
||||
BACNET_ADDRESS *dest = NULL;
|
||||
|
||||
if (!dcc_communication_enabled()) {
|
||||
return status;
|
||||
}
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "COVnotification: requested\n");
|
||||
#endif
|
||||
if (!cov_subscription) {
|
||||
return status;
|
||||
}
|
||||
dest = cov_address_get(cov_subscription->dest_index);
|
||||
if (!dest) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "COVnotification: dest not found!\n");
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
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], dest, &my_address,
|
||||
&npdu_data);
|
||||
/* load the COV data structure for outgoing message */
|
||||
cov_data.subscriberProcessIdentifier =
|
||||
cov_subscription->subscriberProcessIdentifier;
|
||||
cov_data.initiatingDeviceIdentifier = Device_Object_Instance_Number();
|
||||
cov_data.monitoredObjectIdentifier.type =
|
||||
cov_subscription->monitoredObjectIdentifier.type;
|
||||
cov_data.monitoredObjectIdentifier.instance =
|
||||
cov_subscription->monitoredObjectIdentifier.instance;
|
||||
cov_data.timeRemaining = cov_subscription->lifetime;
|
||||
cov_data.listOfValues = value_list;
|
||||
if (cov_subscription->flag.issueConfirmedNotifications) {
|
||||
npdu_data.data_expecting_reply = true;
|
||||
invoke_id = tsm_next_free_invokeID();
|
||||
if (invoke_id) {
|
||||
cov_subscription->invokeID = invoke_id;
|
||||
len =
|
||||
ccov_notify_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
sizeof(Handler_Transmit_Buffer) - pdu_len, invoke_id, &cov_data);
|
||||
} else {
|
||||
goto COV_FAILED;
|
||||
}
|
||||
} else {
|
||||
len =
|
||||
ucov_notify_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
sizeof(Handler_Transmit_Buffer) - pdu_len, &cov_data);
|
||||
}
|
||||
pdu_len += len;
|
||||
if (cov_subscription->flag.issueConfirmedNotifications) {
|
||||
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 (bytes_sent > 0) {
|
||||
status = true;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "COVnotification: Sent!\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
COV_FAILED:
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void cov_lifetime_expiration_handler(
|
||||
unsigned index,
|
||||
uint32_t elapsed_seconds,
|
||||
uint32_t lifetime_seconds)
|
||||
{
|
||||
if (index < MAX_COV_SUBCRIPTIONS) {
|
||||
/* handle lifetime expiration */
|
||||
if (lifetime_seconds >= elapsed_seconds) {
|
||||
COV_Subscriptions[index].lifetime -= elapsed_seconds;
|
||||
#if 0
|
||||
fprintf(stderr, "COVtimer: subscription[%d].lifetime=%lu\n", index,
|
||||
(unsigned long) COV_Subscriptions[index].lifetime);
|
||||
#endif
|
||||
} else {
|
||||
COV_Subscriptions[index].lifetime = 0;
|
||||
}
|
||||
if (COV_Subscriptions[index].lifetime == 0) {
|
||||
/* expire the subscription */
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "COVtimer: PID=%u ",
|
||||
COV_Subscriptions[index].subscriberProcessIdentifier);
|
||||
fprintf(stderr, "%s %u ",
|
||||
bactext_object_type_name(COV_Subscriptions[index].
|
||||
monitoredObjectIdentifier.type),
|
||||
COV_Subscriptions[index].monitoredObjectIdentifier.instance);
|
||||
fprintf(stderr, "time remaining=%u seconds ",
|
||||
COV_Subscriptions[index].lifetime);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
COV_Subscriptions[index].flag.valid = false;
|
||||
COV_Subscriptions[index].dest_index = -1;
|
||||
cov_address_remove_unused();
|
||||
if (COV_Subscriptions[index].flag.issueConfirmedNotifications) {
|
||||
if (COV_Subscriptions[index].invokeID) {
|
||||
tsm_free_invoke_id(COV_Subscriptions[index].invokeID);
|
||||
COV_Subscriptions[index].invokeID = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Handler to check the list of subscribed objects for any that have changed
|
||||
* and so need to have notifications sent.
|
||||
* @ingroup DSCOV
|
||||
* This handler will be invoked by the main program every second or so.
|
||||
* This example only handles Binary Inputs, but can be easily extended to
|
||||
* support other types.
|
||||
* For each subscribed object,
|
||||
* - See if the subscription has timed out
|
||||
* - Remove it if it has timed out.
|
||||
* - See if the subscribed object instance has changed
|
||||
* (eg, check with Binary_Input_Change_Of_Value() )
|
||||
* - If changed,
|
||||
* - Clear the COV (eg, Binary_Input_Change_Of_Value_Clear() )
|
||||
* - Send the notice with cov_send_request()
|
||||
* - Will be confirmed or unconfirmed, as per the subscription.
|
||||
*
|
||||
* @note worst case tasking: MS/TP with the ability to send only
|
||||
* one notification per task cycle.
|
||||
*
|
||||
* @param elapsed_seconds [in] How many seconds have elapsed since last called.
|
||||
*/
|
||||
void handler_cov_timer_seconds(
|
||||
uint32_t elapsed_seconds)
|
||||
{
|
||||
unsigned index = 0;
|
||||
uint32_t lifetime_seconds = 0;
|
||||
|
||||
if (elapsed_seconds) {
|
||||
/* handle the subscription timeouts */
|
||||
for (index = 0; index < MAX_COV_SUBCRIPTIONS; index++) {
|
||||
if (COV_Subscriptions[index].flag.valid) {
|
||||
lifetime_seconds = COV_Subscriptions[index].lifetime;
|
||||
if (lifetime_seconds) {
|
||||
/* only expire COV with definite lifetimes */
|
||||
cov_lifetime_expiration_handler(index, elapsed_seconds,
|
||||
lifetime_seconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool handler_cov_fsm(
|
||||
void)
|
||||
{
|
||||
static int index = 0;
|
||||
BACNET_OBJECT_TYPE object_type = MAX_BACNET_OBJECT_TYPE;
|
||||
uint32_t object_instance = 0;
|
||||
bool status = false;
|
||||
bool send = false;
|
||||
BACNET_PROPERTY_VALUE value_list[MAX_COV_PROPERTIES];
|
||||
/* states for transmitting */
|
||||
static enum {
|
||||
COV_STATE_IDLE = 0,
|
||||
COV_STATE_MARK,
|
||||
COV_STATE_CLEAR,
|
||||
COV_STATE_FREE,
|
||||
COV_STATE_SEND
|
||||
} cov_task_state = COV_STATE_IDLE;
|
||||
|
||||
switch (cov_task_state) {
|
||||
case COV_STATE_IDLE:
|
||||
index = 0;
|
||||
cov_task_state = COV_STATE_MARK;
|
||||
break;
|
||||
case COV_STATE_MARK:
|
||||
/* mark any subscriptions where the value has changed */
|
||||
if (COV_Subscriptions[index].flag.valid) {
|
||||
object_type = (BACNET_OBJECT_TYPE)
|
||||
COV_Subscriptions[index].monitoredObjectIdentifier.type;
|
||||
object_instance =
|
||||
COV_Subscriptions[index].
|
||||
monitoredObjectIdentifier.instance;
|
||||
status = Device_COV(object_type, object_instance);
|
||||
if (status) {
|
||||
COV_Subscriptions[index].flag.send_requested = true;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "COVtask: Marking...\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
index++;
|
||||
if (index >= MAX_COV_SUBCRIPTIONS) {
|
||||
index = 0;
|
||||
cov_task_state = COV_STATE_CLEAR;
|
||||
}
|
||||
break;
|
||||
case COV_STATE_CLEAR:
|
||||
/* clear the COV flag after checking all subscriptions */
|
||||
if ((COV_Subscriptions[index].flag.valid) &&
|
||||
(COV_Subscriptions[index].flag.send_requested)) {
|
||||
object_type = (BACNET_OBJECT_TYPE)
|
||||
COV_Subscriptions[index].monitoredObjectIdentifier.type;
|
||||
object_instance =
|
||||
COV_Subscriptions[index].
|
||||
monitoredObjectIdentifier.instance;
|
||||
Device_COV_Clear(object_type, object_instance);
|
||||
}
|
||||
index++;
|
||||
if (index >= MAX_COV_SUBCRIPTIONS) {
|
||||
index = 0;
|
||||
cov_task_state = COV_STATE_FREE;
|
||||
}
|
||||
break;
|
||||
case COV_STATE_FREE:
|
||||
/* confirmed notification house keeping */
|
||||
if ((COV_Subscriptions[index].flag.valid) &&
|
||||
(COV_Subscriptions[index].flag.issueConfirmedNotifications) &&
|
||||
(COV_Subscriptions[index].invokeID)) {
|
||||
if (tsm_invoke_id_free(COV_Subscriptions[index].invokeID)) {
|
||||
COV_Subscriptions[index].invokeID = 0;
|
||||
} else
|
||||
if (tsm_invoke_id_failed(COV_Subscriptions
|
||||
[index].invokeID)) {
|
||||
tsm_free_invoke_id(COV_Subscriptions[index].invokeID);
|
||||
COV_Subscriptions[index].invokeID = 0;
|
||||
}
|
||||
}
|
||||
index++;
|
||||
if (index >= MAX_COV_SUBCRIPTIONS) {
|
||||
index = 0;
|
||||
cov_task_state = COV_STATE_SEND;
|
||||
}
|
||||
break;
|
||||
case COV_STATE_SEND:
|
||||
/* send any COVs that are requested */
|
||||
if ((COV_Subscriptions[index].flag.valid) &&
|
||||
(COV_Subscriptions[index].flag.send_requested)) {
|
||||
send = true;
|
||||
if (COV_Subscriptions[index].flag.issueConfirmedNotifications) {
|
||||
if (COV_Subscriptions[index].invokeID != 0) {
|
||||
/* already sending */
|
||||
send = false;
|
||||
}
|
||||
if (!tsm_transaction_available()) {
|
||||
/* no transactions available - can't send now */
|
||||
send = false;
|
||||
}
|
||||
}
|
||||
if (send) {
|
||||
object_type = (BACNET_OBJECT_TYPE)
|
||||
COV_Subscriptions[index].
|
||||
monitoredObjectIdentifier.type;
|
||||
object_instance =
|
||||
COV_Subscriptions[index].
|
||||
monitoredObjectIdentifier.instance;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "COVtask: Sending...\n");
|
||||
#endif
|
||||
/* configure the linked list for the two properties */
|
||||
bacapp_property_value_list_init(&value_list[0],
|
||||
MAX_COV_PROPERTIES);
|
||||
status = Device_Encode_Value_List(object_type,
|
||||
object_instance, &value_list[0]);
|
||||
if (status) {
|
||||
status =
|
||||
cov_send_request(&COV_Subscriptions[index],
|
||||
&value_list[0]);
|
||||
}
|
||||
if (status) {
|
||||
COV_Subscriptions[index].flag.send_requested = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
index++;
|
||||
if (index >= MAX_COV_SUBCRIPTIONS) {
|
||||
index = 0;
|
||||
cov_task_state = COV_STATE_IDLE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
index = 0;
|
||||
cov_task_state = COV_STATE_IDLE;
|
||||
break;
|
||||
}
|
||||
return (cov_task_state == COV_STATE_IDLE);
|
||||
}
|
||||
|
||||
void handler_cov_task(
|
||||
void)
|
||||
{
|
||||
handler_cov_fsm();
|
||||
}
|
||||
|
||||
static bool cov_subscribe(
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_SUBSCRIBE_COV_DATA * cov_data,
|
||||
BACNET_ERROR_CLASS * error_class,
|
||||
BACNET_ERROR_CODE * error_code)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
BACNET_OBJECT_TYPE object_type = MAX_BACNET_OBJECT_TYPE;
|
||||
uint32_t object_instance = 0;
|
||||
|
||||
object_type =
|
||||
(BACNET_OBJECT_TYPE) cov_data->monitoredObjectIdentifier.type;
|
||||
object_instance = cov_data->monitoredObjectIdentifier.instance;
|
||||
status = Device_Valid_Object_Id(object_type, object_instance);
|
||||
if (status) {
|
||||
status = Device_Value_List_Supported(object_type);
|
||||
if (status) {
|
||||
status =
|
||||
cov_list_subscribe(src, cov_data, error_class, error_code);
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_OBJECT;
|
||||
*error_code = ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED;
|
||||
}
|
||||
} else {
|
||||
*error_class = ERROR_CLASS_OBJECT;
|
||||
*error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/** Handler for a COV Subscribe Service request.
|
||||
* @ingroup DSCOV
|
||||
* This handler will be invoked by apdu_handler() if it has been enabled
|
||||
* by a call to apdu_set_confirmed_handler().
|
||||
* This handler builds a response packet, which is
|
||||
* - an Abort if
|
||||
* - the message is segmented
|
||||
* - if decoding fails
|
||||
* - an ACK, if cov_subscribe() succeeds
|
||||
* - an Error if cov_subscribe() fails
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
void handler_cov_subscribe(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
BACNET_SUBSCRIBE_COV_DATA cov_data;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int npdu_len = 0;
|
||||
int apdu_len = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
bool success = false;
|
||||
int bytes_sent = 0;
|
||||
BACNET_ADDRESS my_address;
|
||||
bool error = false;
|
||||
|
||||
/* initialize a common abort code */
|
||||
cov_data.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
/* encode the NPDU portion of the packet */
|
||||
datalink_get_my_address(&my_address);
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
npdu_len =
|
||||
npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address,
|
||||
&npdu_data);
|
||||
if (service_data->segmented_message) {
|
||||
/* we don't support segmentation - send an abort */
|
||||
len = BACNET_STATUS_ABORT;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "SubscribeCOV: Segmented message. Sending Abort!\n");
|
||||
#endif
|
||||
error = true;
|
||||
goto COV_ABORT;
|
||||
}
|
||||
len =
|
||||
cov_subscribe_decode_service_request(service_request, service_len,
|
||||
&cov_data);
|
||||
#if PRINT_ENABLED
|
||||
if (len <= 0)
|
||||
fprintf(stderr, "SubscribeCOV: Unable to decode Request!\n");
|
||||
#endif
|
||||
if (len < 0) {
|
||||
error = true;
|
||||
goto COV_ABORT;
|
||||
}
|
||||
cov_data.error_class = ERROR_CLASS_OBJECT;
|
||||
cov_data.error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
success =
|
||||
cov_subscribe(src, &cov_data, &cov_data.error_class,
|
||||
&cov_data.error_code);
|
||||
if (success) {
|
||||
apdu_len =
|
||||
encode_simple_ack(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_SUBSCRIBE_COV);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "SubscribeCOV: Sending Simple Ack!\n");
|
||||
#endif
|
||||
} else {
|
||||
len = BACNET_STATUS_ERROR;
|
||||
error = true;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "SubscribeCOV: Sending Error!\n");
|
||||
#endif
|
||||
}
|
||||
COV_ABORT:
|
||||
if (error) {
|
||||
if (len == BACNET_STATUS_ABORT) {
|
||||
apdu_len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id,
|
||||
abort_convert_error_code(cov_data.error_code), true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "SubscribeCOV: Sending Abort!\n");
|
||||
#endif
|
||||
} else if (len == BACNET_STATUS_ERROR) {
|
||||
apdu_len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_SUBSCRIBE_COV,
|
||||
cov_data.error_class, cov_data.error_code);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "SubscribeCOV: Sending Error!\n");
|
||||
#endif
|
||||
} else if (len == BACNET_STATUS_REJECT) {
|
||||
apdu_len =
|
||||
reject_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id,
|
||||
reject_convert_error_code(cov_data.error_code));
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "SubscribeCOV: Sending Reject!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
pdu_len = npdu_len + apdu_len;
|
||||
bytes_sent =
|
||||
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
|
||||
pdu_len);
|
||||
if (bytes_sent <= 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "SubscribeCOV: Failed to send PDU (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include <errno.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacerror.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "reject.h"
|
||||
#include "dcc.h"
|
||||
#include "handlers.h"
|
||||
#include "device.h"
|
||||
|
||||
/** @file h_dcc.c Handles Device Communication Control request. */
|
||||
|
||||
static char My_Password[32] = "filister";
|
||||
|
||||
/** Sets (non-volatile hold) the password to be used for DCC requests.
|
||||
* @param new_password [in] The new DCC password, of up to 31 characters.
|
||||
*/
|
||||
void handler_dcc_password_set(
|
||||
char *new_password)
|
||||
{
|
||||
size_t i = 0; /* loop counter */
|
||||
|
||||
if (new_password) {
|
||||
for (i = 0; i < (sizeof(My_Password) - 1); i++) {
|
||||
My_Password[i] = new_password[i];
|
||||
My_Password[i + 1] = 0;
|
||||
if (new_password[i] == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < sizeof(My_Password); i++) {
|
||||
My_Password[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets (non-volatile hold) the password to be used for DCC requests.
|
||||
* @return DCC password
|
||||
*/
|
||||
char *handler_dcc_password(void)
|
||||
{
|
||||
return My_Password;
|
||||
}
|
||||
|
||||
/** Handler for a Device Communication Control (DCC) request.
|
||||
* @ingroup DMDCC
|
||||
* This handler will be invoked by apdu_handler() if it has been enabled
|
||||
* by a call to apdu_set_confirmed_handler().
|
||||
* This handler builds a response packet, which is
|
||||
* - an Abort if
|
||||
* - the message is segmented
|
||||
* - if decoding fails
|
||||
* - if not a known DCC state
|
||||
* - an Error if the DCC password is incorrect
|
||||
* - else tries to send a simple ACK for the DCC on success,
|
||||
* and sets the DCC state requested.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
void handler_device_communication_control(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
uint16_t timeDuration = 0;
|
||||
BACNET_COMMUNICATION_ENABLE_DISABLE state = COMMUNICATION_ENABLE;
|
||||
BACNET_CHARACTER_STRING password;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
/* encode the NPDU portion of the reply packet */
|
||||
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 PRINT_ENABLED
|
||||
fprintf(stderr, "DeviceCommunicationControl!\n");
|
||||
#endif
|
||||
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,
|
||||
"DeviceCommunicationControl: "
|
||||
"Sending Abort - segmented message.\n");
|
||||
#endif
|
||||
goto DCC_ABORT;
|
||||
}
|
||||
/* decode the service request only */
|
||||
len =
|
||||
dcc_decode_service_request(service_request, service_len, &timeDuration,
|
||||
&state, &password);
|
||||
#if PRINT_ENABLED
|
||||
if (len > 0)
|
||||
fprintf(stderr,
|
||||
"DeviceCommunicationControl: " "timeout=%u state=%u password=%s\n",
|
||||
(unsigned) timeDuration, (unsigned) state,
|
||||
characterstring_value(&password));
|
||||
#endif
|
||||
/* bad decoding or something we didn't understand - 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,
|
||||
"DeviceCommunicationControl: "
|
||||
"Sending Abort - could not decode.\n");
|
||||
#endif
|
||||
goto DCC_ABORT;
|
||||
}
|
||||
if (state >= MAX_BACNET_COMMUNICATION_ENABLE_DISABLE) {
|
||||
len =
|
||||
reject_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, REJECT_REASON_UNDEFINED_ENUMERATION);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"DeviceCommunicationControl: "
|
||||
"Sending Reject - undefined enumeration\n");
|
||||
#endif
|
||||
} else {
|
||||
#if BAC_ROUTING
|
||||
/* Check to see if the current Device supports this service. */
|
||||
len =
|
||||
Routed_Device_Service_Approval
|
||||
(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, (int) state,
|
||||
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id);
|
||||
if (len > 0)
|
||||
goto DCC_ABORT;
|
||||
#endif
|
||||
|
||||
if (characterstring_ansi_same(&password, My_Password)) {
|
||||
len =
|
||||
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"DeviceCommunicationControl: " "Sending Simple Ack!\n");
|
||||
#endif
|
||||
dcc_set_status_duration(state, timeDuration);
|
||||
} else {
|
||||
len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
|
||||
ERROR_CLASS_SECURITY, ERROR_CODE_PASSWORD_FAILURE);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"DeviceCommunicationControl: "
|
||||
"Sending Error - password failure.\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
DCC_ABORT:
|
||||
pdu_len += len;
|
||||
len =
|
||||
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
|
||||
pdu_len);
|
||||
if (len <= 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"DeviceCommunicationControl: " "Failed to send PDU (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Daniel Blazevic <daniel.blazevic@gmail.com>
|
||||
* @date 2013
|
||||
* @brief GetAlarmSummary ACK service handling
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* Copyright (C) 2013 Daniel Blazevic <daniel.blazevic@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @section DESCRIPTION
|
||||
*
|
||||
* The GetAlarmSummary ACK service handler is used by a client BACnet-user to
|
||||
* obtain a summary of "active alarms." The term "active alarm" refers to
|
||||
* BACnet standard objects that have an Event_State property whose value is
|
||||
* not equal to NORMAL and a Notify_Type property whose value is ALARM.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacerror.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "handlers.h"
|
||||
#include "get_alarm_sum.h"
|
||||
|
||||
|
||||
/** Example function to handle a GetAlarmSummary ACK.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
void get_alarm_summary_ack_handler(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data)
|
||||
{
|
||||
uint16_t apdu_len = 0;
|
||||
uint16_t len = 0;
|
||||
BACNET_GET_ALARM_SUMMARY_DATA data;
|
||||
|
||||
while (service_len - len) {
|
||||
apdu_len =
|
||||
get_alarm_summary_ack_decode_apdu_data(&service_request[len],
|
||||
service_len - len, &data);
|
||||
|
||||
len += apdu_len;
|
||||
|
||||
if (apdu_len > 0) {
|
||||
/* FIXME: Add code to process data */
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
|
||||
*
|
||||
* 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacerror.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "handlers.h"
|
||||
|
||||
/** @file h_alarm_sum.c Handles Get Alarm Summary request. */
|
||||
|
||||
static get_alarm_summary_function Get_Alarm_Summary[MAX_BACNET_OBJECT_TYPE];
|
||||
|
||||
void handler_get_alarm_summary_set(
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
get_alarm_summary_function pFunction)
|
||||
{
|
||||
if (object_type < MAX_BACNET_OBJECT_TYPE) {
|
||||
Get_Alarm_Summary[object_type] = pFunction;
|
||||
}
|
||||
}
|
||||
|
||||
void handler_get_alarm_summary(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int apdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
int alarm_value = 0;
|
||||
unsigned i = 0;
|
||||
unsigned j = 0;
|
||||
bool error = false;
|
||||
BACNET_ADDRESS my_address;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_GET_ALARM_SUMMARY_DATA getalarm_data;
|
||||
|
||||
|
||||
|
||||
/* encode the NPDU portion of the packet */
|
||||
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) {
|
||||
/* we don't support segmentation - send an abort */
|
||||
apdu_len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED,
|
||||
true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"GetAlarmSummary: Segmented message. Sending Abort!\n");
|
||||
#endif
|
||||
goto GET_ALARM_SUMMARY_ABORT;
|
||||
}
|
||||
|
||||
/* init header */
|
||||
apdu_len =
|
||||
get_alarm_summary_ack_encode_apdu_init(&Handler_Transmit_Buffer
|
||||
[pdu_len], service_data->invoke_id);
|
||||
|
||||
|
||||
for (i = 0; i < MAX_BACNET_OBJECT_TYPE; i++) {
|
||||
if (Get_Alarm_Summary[i]) {
|
||||
for (j = 0; j < 0xffff; j++) {
|
||||
alarm_value = Get_Alarm_Summary[i] (j, &getalarm_data);
|
||||
if (alarm_value > 0) {
|
||||
len =
|
||||
get_alarm_summary_ack_encode_apdu_data
|
||||
(&Handler_Transmit_Buffer[pdu_len + apdu_len],
|
||||
service_data->max_resp - apdu_len, &getalarm_data);
|
||||
if (len <= 0) {
|
||||
error = true;
|
||||
goto GET_ALARM_SUMMARY_ERROR;
|
||||
} else
|
||||
apdu_len += len;
|
||||
} else if (alarm_value < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "GetAlarmSummary: Sending response!\n");
|
||||
#endif
|
||||
|
||||
GET_ALARM_SUMMARY_ERROR:
|
||||
if (error) {
|
||||
if (len == BACNET_STATUS_ABORT) {
|
||||
/* BACnet APDU too small to fit data, so proper response is Abort */
|
||||
apdu_len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"GetAlarmSummary: Reply too big to fit into APDU!\n");
|
||||
#endif
|
||||
} else {
|
||||
apdu_len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_GET_ALARM_SUMMARY,
|
||||
ERROR_CLASS_PROPERTY, ERROR_CODE_OTHER);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "GetAlarmSummary: Sending Error!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GET_ALARM_SUMMARY_ABORT:
|
||||
pdu_len += apdu_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)); */
|
||||
}
|
||||
#else
|
||||
bytes_sent = bytes_sent;
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2009 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacerror.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "event.h"
|
||||
#include "getevent.h"
|
||||
#include "handlers.h"
|
||||
|
||||
/** @file h_getevent.c Handles Get Event Information request. */
|
||||
|
||||
static get_event_info_function Get_Event_Info[MAX_BACNET_OBJECT_TYPE];
|
||||
|
||||
|
||||
/** print eventState
|
||||
*/
|
||||
void ge_ack_print_data(
|
||||
BACNET_GET_EVENT_INFORMATION_DATA * data,
|
||||
uint32_t device_id)
|
||||
{
|
||||
BACNET_GET_EVENT_INFORMATION_DATA *act_data = data;
|
||||
const char* state_strs[] = {"NO", "FA", "ON", "HL", "LL"};
|
||||
printf("DeviceID\tType\tInstance\teventState\n");
|
||||
printf("--------------- ------- --------------- ---------------\n");
|
||||
int count = 0;
|
||||
while (act_data) {
|
||||
printf("%u\t\t%u\t%u\t\t%s\n",
|
||||
device_id,
|
||||
act_data->objectIdentifier.type,
|
||||
act_data->objectIdentifier.instance,
|
||||
state_strs[data->eventState]
|
||||
);
|
||||
act_data = act_data->next;
|
||||
count++;
|
||||
}
|
||||
printf("\n%u\t Total\n",count);
|
||||
}
|
||||
|
||||
void handler_get_event_information_set(
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
get_event_info_function pFunction)
|
||||
{
|
||||
if (object_type < MAX_BACNET_OBJECT_TYPE) {
|
||||
Get_Event_Info[object_type] = pFunction;
|
||||
}
|
||||
}
|
||||
|
||||
void handler_get_event_information(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int apdu_len = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
bool error = false;
|
||||
bool more_events = false;
|
||||
int bytes_sent = 0;
|
||||
BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT;
|
||||
BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
BACNET_ADDRESS my_address;
|
||||
BACNET_OBJECT_ID object_id;
|
||||
unsigned i = 0, j = 0; /* counter */
|
||||
BACNET_GET_EVENT_INFORMATION_DATA getevent_data;
|
||||
int valid_event = 0;
|
||||
|
||||
/* initialize type of 'Last Received Object Identifier' using max value */
|
||||
object_id.type = MAX_BACNET_OBJECT_TYPE;
|
||||
|
||||
/* encode the NPDU portion of the packet */
|
||||
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) {
|
||||
/* we don't support segmentation - send an abort */
|
||||
len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED,
|
||||
true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"GetEventInformation: " "Segmented message. Sending Abort!\n");
|
||||
#endif
|
||||
goto GET_EVENT_ABORT;
|
||||
}
|
||||
|
||||
len =
|
||||
getevent_decode_service_request(service_request, service_len,
|
||||
&object_id);
|
||||
if (len < 0) {
|
||||
/* bad decoding - send an abort */
|
||||
len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_OTHER, true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"GetEventInformation: Bad Encoding. Sending Abort!\n");
|
||||
#endif
|
||||
goto GET_EVENT_ABORT;
|
||||
}
|
||||
len =
|
||||
getevent_ack_encode_apdu_init(&Handler_Transmit_Buffer[pdu_len],
|
||||
sizeof(Handler_Transmit_Buffer) - pdu_len, service_data->invoke_id);
|
||||
if (len <= 0) {
|
||||
error = true;
|
||||
goto GET_EVENT_ERROR;
|
||||
}
|
||||
pdu_len += len;
|
||||
apdu_len = len;
|
||||
for (i = 0; i < MAX_BACNET_OBJECT_TYPE; i++) {
|
||||
if (Get_Event_Info[i]) {
|
||||
for (j = 0; j < 0xffff; j++) {
|
||||
valid_event = Get_Event_Info[i] (j, &getevent_data);
|
||||
if (valid_event > 0) {
|
||||
/* encode GetEvent_data only when type of object_id has max value */
|
||||
if (object_id.type != MAX_BACNET_OBJECT_TYPE) {
|
||||
if ((object_id.type ==
|
||||
getevent_data.objectIdentifier.type) &&
|
||||
(object_id.instance ==
|
||||
getevent_data.objectIdentifier.instance)) {
|
||||
/* found 'Last Received Object Identifier'
|
||||
so should set type of object_id to max value */
|
||||
object_id.type = MAX_BACNET_OBJECT_TYPE;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
getevent_data.next = NULL;
|
||||
len =
|
||||
getevent_ack_encode_apdu_data(&Handler_Transmit_Buffer
|
||||
[pdu_len], sizeof(Handler_Transmit_Buffer) - pdu_len,
|
||||
&getevent_data);
|
||||
if (len <= 0) {
|
||||
error = true;
|
||||
goto GET_EVENT_ERROR;
|
||||
}
|
||||
apdu_len += len;
|
||||
if ((apdu_len >= service_data->max_resp - 2) ||
|
||||
(apdu_len >= MAX_APDU - 2)) {
|
||||
/* Device must be able to fit minimum
|
||||
one event information.
|
||||
Length of one event informations needs
|
||||
more than 50 octets. */
|
||||
if ((service_data->max_resp < 128) ||
|
||||
(MAX_APDU < 128)) {
|
||||
len = BACNET_STATUS_ABORT;
|
||||
error = true;
|
||||
goto GET_EVENT_ERROR;
|
||||
} else {
|
||||
more_events = true;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
pdu_len += len;
|
||||
}
|
||||
} else if (valid_event < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
len =
|
||||
getevent_ack_encode_apdu_end(&Handler_Transmit_Buffer[pdu_len],
|
||||
sizeof(Handler_Transmit_Buffer) - pdu_len, more_events);
|
||||
if (len <= 0) {
|
||||
error = true;
|
||||
goto GET_EVENT_ERROR;
|
||||
}
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Got a GetEventInformation request: Sending Ack!\n");
|
||||
#endif
|
||||
GET_EVENT_ERROR:
|
||||
if (error) {
|
||||
pdu_len =
|
||||
npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address,
|
||||
&npdu_data);
|
||||
|
||||
if (len == -2) {
|
||||
/* BACnet APDU too small to fit data, so proper response is Abort */
|
||||
len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"GetEventInformation: " "Reply too big to fit into APDU!\n");
|
||||
#endif
|
||||
} else {
|
||||
len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_READ_PROPERTY,
|
||||
error_class, error_code);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "GetEventInformation: Sending Error!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
GET_EVENT_ABORT:
|
||||
pdu_len += len;
|
||||
bytes_sent =
|
||||
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
|
||||
pdu_len);
|
||||
#if PRINT_ENABLED
|
||||
if (bytes_sent <= 0)
|
||||
fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno));
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Daniel Blazevic <daniel.blazevic@gmail.com>
|
||||
* @date 2014
|
||||
* @brief GetEvent ACK service handling
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* Copyright (C) 2014 Daniel Blazevic <daniel.blazevic@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @section DESCRIPTION
|
||||
*
|
||||
* The GetEventInformation service ACK service handler is used by a client
|
||||
* BACnet-user to obtain a summary of all "active event states". The term
|
||||
* "active event states" refers to all event-initiating objects that have an
|
||||
* Event_State property whose value is not equal to NORMAL, or have an
|
||||
* Acked_Transitions property, which has at least one of the bits
|
||||
* (TO-OFFNORMAL, TO-FAULT, TONORMAL) set to FALSE.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacerror.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "handlers.h"
|
||||
#include "getevent.h"
|
||||
|
||||
/* 40 = min size of get event data in APDU */
|
||||
#define MAX_NUMBER_OF_EVENTS ((MAX_APDU / 40) + 1)
|
||||
|
||||
/** Example function to handle a GetEvent ACK.
|
||||
*
|
||||
* @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_ACK_DATA information
|
||||
* decoded from the APDU header of this message.
|
||||
*/
|
||||
void get_event_ack_handler(
|
||||
uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS *src,
|
||||
BACNET_CONFIRMED_SERVICE_ACK_DATA *service_data)
|
||||
{
|
||||
uint8_t i = 0;
|
||||
uint16_t apdu_len = 0;
|
||||
bool more_events = false;
|
||||
/* initialize array big enough to accommodate
|
||||
multiple get event data in APDU */
|
||||
BACNET_GET_EVENT_INFORMATION_DATA get_event_data[MAX_NUMBER_OF_EVENTS];
|
||||
|
||||
for (i = 1; i < MAX_NUMBER_OF_EVENTS; i++) {
|
||||
/* Create linked list */
|
||||
get_event_data[i - 1].next = &get_event_data[i];
|
||||
}
|
||||
|
||||
apdu_len =
|
||||
getevent_ack_decode_service_request(&service_request[0],
|
||||
service_len, &get_event_data[0], &more_events);
|
||||
|
||||
if (apdu_len > 0) {
|
||||
/* FIXME: Add code to process get_event_data */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "iam.h"
|
||||
#include "address.h"
|
||||
#include "handlers.h"
|
||||
|
||||
/** @file h_iam.c Handles I-Am requests. */
|
||||
|
||||
/** Handler for I-Am responses.
|
||||
* Will add the responder to our cache, or update its binding.
|
||||
* @ingroup DMDDB
|
||||
* @param service_request [in] The received message to be handled.
|
||||
* @param service_len [in] Length of the service_request message.
|
||||
* @param src [in] The BACNET_ADDRESS of the message's source.
|
||||
*/
|
||||
void handler_i_am_add(
|
||||
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) service_len;
|
||||
len =
|
||||
iam_decode_service_request(service_request, &device_id, &max_apdu,
|
||||
&segmentation, &vendor_id);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Received I-Am Request");
|
||||
#endif
|
||||
if (len != -1) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, " from %lu, MAC = %d.%d.%d.%d.%d.%d\n",
|
||||
(unsigned long) device_id, src->mac[0], src->mac[1], src->mac[2],
|
||||
src->mac[3], src->mac[4], src->mac[5]);
|
||||
#endif
|
||||
address_add(device_id, max_apdu, src);
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, ", but unable to decode it.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** Handler for I-Am responses (older binding-update-only version).
|
||||
* Will update the responder's binding, but if already in our cache.
|
||||
* @note This handler is deprecated, in favor of handler_i_am_add().
|
||||
*
|
||||
* @param service_request [in] The received message to be handled.
|
||||
* @param service_len [in] Length of the service_request message.
|
||||
* @param src [in] The BACNET_ADDRESS of the message's source.
|
||||
*/
|
||||
void handler_i_am_bind(
|
||||
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) service_len;
|
||||
len =
|
||||
iam_decode_service_request(service_request, &device_id, &max_apdu,
|
||||
&segmentation, &vendor_id);
|
||||
if (len > 0) {
|
||||
/* only add address if requested to bind */
|
||||
address_add_binding(device_id, max_apdu, src);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bactext.h"
|
||||
#include "ihave.h"
|
||||
#include "handlers.h"
|
||||
|
||||
/** @file h_ihave.c Handles incoming I-Have messages. */
|
||||
|
||||
/** Simple Handler for I-Have responses (just validates response).
|
||||
* @ingroup DMDOB
|
||||
* @param service_request [in] The received message to be handled.
|
||||
* @param service_len [in] Length of the service_request message.
|
||||
* @param src [in] The BACNET_ADDRESS of the message's source.
|
||||
*/
|
||||
void handler_i_have(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src)
|
||||
{
|
||||
int len = 0;
|
||||
BACNET_I_HAVE_DATA data;
|
||||
|
||||
(void) service_len;
|
||||
(void) src;
|
||||
len = ihave_decode_service_request(service_request, service_len, &data);
|
||||
if (len != -1) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "I-Have: %s %lu from %s %lu!\r\n",
|
||||
bactext_object_type_name(data.object_id.type),
|
||||
(unsigned long) data.object_id.instance,
|
||||
bactext_object_type_name(data.device_id.type),
|
||||
(unsigned long) data.device_id.instance);
|
||||
#endif
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "I-Have: received, but unable to decode!\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "lso.h"
|
||||
#include "handlers.h"
|
||||
#include "device.h"
|
||||
|
||||
/** @file h_lso.c Handles BACnet Life Safey Operation messages. */
|
||||
|
||||
void handler_lso(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
BACNET_LSO_DATA data;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
int bytes_sent = 0;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
/* encode the NPDU portion of the packet */
|
||||
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) {
|
||||
/* we don't support segmentation - send an abort */
|
||||
len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED,
|
||||
true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "LSO: Segmented message. Sending Abort!\n");
|
||||
#endif
|
||||
goto LSO_ABORT;
|
||||
}
|
||||
|
||||
len = lso_decode_service_request(service_request, service_len, &data);
|
||||
#if PRINT_ENABLED
|
||||
if (len <= 0)
|
||||
fprintf(stderr, "LSO: Unable to decode Request!\n");
|
||||
#endif
|
||||
if (len < 0) {
|
||||
/* bad decoding - send an abort */
|
||||
len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_OTHER, true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "LSO: Bad Encoding. Sending Abort!\n");
|
||||
#endif
|
||||
goto LSO_ABORT;
|
||||
}
|
||||
|
||||
/*
|
||||
** Process Life Safety Operation Here
|
||||
*/
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Life Safety Operation: Received operation %d from process id %lu for object %lu\n",
|
||||
data.operation, (unsigned long) data.processId,
|
||||
(unsigned long) data.targetObject.instance);
|
||||
#endif
|
||||
|
||||
len =
|
||||
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Life Safety Operation: " "Sending Simple Ack!\n");
|
||||
#endif
|
||||
|
||||
LSO_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, "Life Safety Operation: " "Failed to send PDU (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacint.h"
|
||||
#include "bacenum.h"
|
||||
#include "bits.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "handlers.h"
|
||||
#include "client.h"
|
||||
|
||||
#if PRINT_ENABLED
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
/** @file h_npdu.c Handles messages at the NPDU level of the BACnet stack. */
|
||||
|
||||
/** Handler for the NPDU portion of a received packet.
|
||||
* Aside from error-checking, if the NPDU doesn't contain routing info,
|
||||
* this handler doesn't do much besides stepping over the NPDU header
|
||||
* and passing the remaining bytes to the apdu_handler.
|
||||
* @note The routing (except src) and NCPI information, including
|
||||
* npdu_data->data_expecting_reply, are discarded.
|
||||
* @see routing_npdu_handler
|
||||
*
|
||||
* @ingroup MISCHNDLR
|
||||
*
|
||||
* @param src [out] Returned with routing source information if the NPDU
|
||||
* has any and if this points to non-null storage for it.
|
||||
* If src->net and src->len are 0 on return, there is no
|
||||
* routing source information.
|
||||
* This src describes the original source of the message when
|
||||
* it had to be routed to reach this BACnet Device, and this
|
||||
* is passed down into the apdu_handler; however, I don't
|
||||
* think this project's code has any use for the src info
|
||||
* on return from this handler, since the response has
|
||||
* already been sent via the apdu_handler.
|
||||
* @param pdu [in] Buffer containing the NPDU and APDU of the received packet.
|
||||
* @param pdu_len [in] The size of the received message in the pdu[] buffer.
|
||||
*/
|
||||
void 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 };
|
||||
|
||||
/* only handle the version that we know how to handle */
|
||||
if (pdu[0] == BACNET_PROTOCOL_VERSION) {
|
||||
apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data);
|
||||
if (npdu_data.network_layer_message) {
|
||||
/*FIXME: network layer message received! Handle it! */
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "NPDU: Network Layer Message discarded!\n");
|
||||
#endif
|
||||
} else if ((apdu_offset > 0) && (apdu_offset <= pdu_len)) {
|
||||
if ((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 */
|
||||
if ((dest.net == BACNET_BROADCAST_NETWORK) &&
|
||||
((pdu[apdu_offset] & 0xF0) ==
|
||||
PDU_TYPE_CONFIRMED_SERVICE_REQUEST)) {
|
||||
/* hack for 5.4.5.1 - IDLE */
|
||||
/* ConfirmedBroadcastReceived */
|
||||
/* then enter IDLE - ignore the PDU */
|
||||
} else {
|
||||
apdu_handler(src, &pdu[apdu_offset],
|
||||
(uint16_t) (pdu_len - apdu_offset));
|
||||
}
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
printf("NPDU: DNET=%u. Discarded!\n", (unsigned) dest.net);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
printf("NPDU: BACnet Protocol Version=%u. Discarded!\n",
|
||||
(unsigned) pdu[0]);
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
/*#include "arf.h" */
|
||||
/* demo objects */
|
||||
#if defined(BACFILE)
|
||||
#include "bacfile.h"
|
||||
#endif
|
||||
#include "mydata.h"
|
||||
#include "ptransfer.h"
|
||||
#include "handlers.h"
|
||||
|
||||
/** @file h_pt.c Handles Confirmed Private Transfer requests. */
|
||||
|
||||
#define MYMAXSTR 32
|
||||
#define MYMAXBLOCK 8
|
||||
|
||||
DATABLOCK MyData[MYMAXBLOCK];
|
||||
|
||||
uint8_t IOBufferPT[MAX_APDU]; /* Buffer for building response in */
|
||||
|
||||
static void ProcessPT(
|
||||
BACNET_PRIVATE_TRANSFER_DATA * data)
|
||||
{
|
||||
int iLen; /* Index to current location in data */
|
||||
char cBlockNumber;
|
||||
uint32_t ulTemp;
|
||||
int tag_len;
|
||||
uint8_t tag_number;
|
||||
uint32_t len_value_type;
|
||||
BACNET_CHARACTER_STRING bsTemp;
|
||||
|
||||
iLen = 0;
|
||||
|
||||
/* Decode the block number */
|
||||
tag_len =
|
||||
decode_tag_number_and_value(&data->serviceParameters[iLen],
|
||||
&tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
/* Bail out early if wrong type */
|
||||
/* and signal unexpected error */
|
||||
data->serviceParametersLen = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
iLen +=
|
||||
decode_unsigned(&data->serviceParameters[iLen], len_value_type,
|
||||
&ulTemp);
|
||||
cBlockNumber = (char) ulTemp;
|
||||
if (cBlockNumber < MY_MAX_BLOCK) {
|
||||
if (data->serviceNumber == MY_SVC_READ) {
|
||||
/* Read Response is an unsigned int with
|
||||
0 for success or a non 0 error code
|
||||
For a successful read the 0 success
|
||||
code is followed by the block number
|
||||
and then the block contents which
|
||||
consist of 2 unsigned ints (in 0 to 255
|
||||
range as they are really chars) a single
|
||||
precision real and a string which
|
||||
will be up to 32 chars + a nul */
|
||||
|
||||
iLen = 0;
|
||||
|
||||
/* Signal success */
|
||||
iLen += encode_application_unsigned(&IOBufferPT[iLen], MY_ERR_OK);
|
||||
/* Followed by the block number */
|
||||
iLen +=
|
||||
encode_application_unsigned(&IOBufferPT[iLen], cBlockNumber);
|
||||
/* And Then the block contents */
|
||||
iLen +=
|
||||
encode_application_unsigned(&IOBufferPT[iLen],
|
||||
MyData[(int8_t) cBlockNumber].cMyByte1);
|
||||
iLen +=
|
||||
encode_application_unsigned(&IOBufferPT[iLen],
|
||||
MyData[(int8_t) cBlockNumber].cMyByte2);
|
||||
iLen +=
|
||||
encode_application_real(&IOBufferPT[iLen],
|
||||
MyData[(int8_t) cBlockNumber].fMyReal);
|
||||
characterstring_init_ansi(&bsTemp,
|
||||
(char *) MyData[(int8_t) cBlockNumber].sMyString);
|
||||
iLen +=
|
||||
encode_application_character_string(&IOBufferPT[iLen],
|
||||
&bsTemp);
|
||||
} else {
|
||||
/* Write operation */
|
||||
/* Write block consists of the block number
|
||||
followed by the block contents as
|
||||
described above for the read operation.
|
||||
The returned result is an unsigned
|
||||
response which is 0 for success and
|
||||
a non 0 error code otherwise. */
|
||||
|
||||
tag_len =
|
||||
decode_tag_number_and_value(&data->serviceParameters[iLen],
|
||||
&tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
data->serviceParametersLen = 0;
|
||||
return;
|
||||
}
|
||||
iLen +=
|
||||
decode_unsigned(&data->serviceParameters[iLen], len_value_type,
|
||||
&ulTemp);
|
||||
MyData[(int8_t) cBlockNumber].cMyByte1 = (char) ulTemp;
|
||||
|
||||
tag_len =
|
||||
decode_tag_number_and_value(&data->serviceParameters[iLen],
|
||||
&tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
data->serviceParametersLen = 0;
|
||||
return;
|
||||
}
|
||||
iLen +=
|
||||
decode_unsigned(&data->serviceParameters[iLen], len_value_type,
|
||||
&ulTemp);
|
||||
MyData[(int8_t) cBlockNumber].cMyByte2 = (char) ulTemp;
|
||||
|
||||
tag_len =
|
||||
decode_tag_number_and_value(&data->serviceParameters[iLen],
|
||||
&tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_REAL) {
|
||||
data->serviceParametersLen = 0;
|
||||
return;
|
||||
}
|
||||
iLen +=
|
||||
decode_real(&data->serviceParameters[iLen],
|
||||
&MyData[(int8_t) cBlockNumber].fMyReal);
|
||||
|
||||
tag_len =
|
||||
decode_tag_number_and_value(&data->serviceParameters[iLen],
|
||||
&tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_CHARACTER_STRING) {
|
||||
data->serviceParametersLen = 0;
|
||||
return;
|
||||
}
|
||||
decode_character_string(&data->serviceParameters[iLen],
|
||||
len_value_type, &bsTemp);
|
||||
/* Only copy as much as we can accept */
|
||||
strncpy((char *) MyData[(int8_t) cBlockNumber].sMyString,
|
||||
characterstring_value(&bsTemp), MY_MAX_STR);
|
||||
/* Make sure it is nul terminated */
|
||||
MyData[(int8_t) cBlockNumber].sMyString[MY_MAX_STR] = '\0';
|
||||
/* Signal success */
|
||||
iLen = encode_application_unsigned(&IOBufferPT[0], MY_ERR_OK);
|
||||
}
|
||||
} else {
|
||||
/* Signal bad index */
|
||||
iLen = encode_application_unsigned(&IOBufferPT[0], MY_ERR_BAD_INDEX);
|
||||
}
|
||||
data->serviceParametersLen = iLen;
|
||||
data->serviceParameters = IOBufferPT;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called when we receive a private transfer packet.
|
||||
* We parse the data, send the private part for processing and then send the
|
||||
* response which the application generates.
|
||||
* If there are any BACnet level errors we send an error response from here.
|
||||
* If there are any application level errors they will be packeged up in the
|
||||
* response block which we send back to the originator of the request.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
void handler_conf_private_trans(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
BACNET_PRIVATE_TRANSFER_DATA data;
|
||||
int len;
|
||||
int pdu_len;
|
||||
bool error;
|
||||
int bytes_sent;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
BACNET_ERROR_CLASS error_class;
|
||||
BACNET_ERROR_CODE error_code;
|
||||
|
||||
len = 0;
|
||||
pdu_len = 0;
|
||||
error = false;
|
||||
bytes_sent = 0;
|
||||
error_class = ERROR_CLASS_OBJECT;
|
||||
error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Received Confirmed Private Transfer Request!\n");
|
||||
#endif
|
||||
/* encode the NPDU portion of the response packet as it will be needed */
|
||||
/* no matter what the outcome. */
|
||||
|
||||
datalink_get_my_address(&my_address);
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len =
|
||||
npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address,
|
||||
&npdu_data);
|
||||
|
||||
if (service_data->segmented_message) {
|
||||
len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED,
|
||||
true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "CPT: Segmented Message. Sending Abort!\n");
|
||||
#endif
|
||||
goto CPT_ABORT;
|
||||
}
|
||||
|
||||
len =
|
||||
ptransfer_decode_service_request(service_request, service_len, &data);
|
||||
/* bad decoding - send an abort */
|
||||
if (len < 0) {
|
||||
len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_OTHER, true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "CPT: Bad Encoding. Sending Abort!\n");
|
||||
#endif
|
||||
goto CPT_ABORT;
|
||||
}
|
||||
|
||||
/* Simple example with service number of 0 for
|
||||
read block and 1 for write block
|
||||
We also only support our own vendor ID.
|
||||
In theory we could support others
|
||||
for compatability purposes but these
|
||||
interfaces are rarely documented... */
|
||||
if ((data.vendorID == BACNET_VENDOR_ID) &&
|
||||
(data.serviceNumber <= MY_SVC_WRITE)) {
|
||||
/* We only try to understand our own IDs and service numbers */
|
||||
/* Will either return a result block or an app level status block */
|
||||
ProcessPT(&data);
|
||||
if (data.serviceParametersLen == 0) {
|
||||
/* No respopnse means fatal error */
|
||||
error = true;
|
||||
error_class = ERROR_CLASS_SERVICES;
|
||||
error_code = ERROR_CODE_OTHER;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "CPT: Error servicing request!\n");
|
||||
#endif
|
||||
}
|
||||
len =
|
||||
ptransfer_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, &data);
|
||||
} else { /* Not our vendor ID or bad service parameter */
|
||||
|
||||
error = true;
|
||||
error_class = ERROR_CLASS_SERVICES;
|
||||
error_code = ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "CPT: Not our Vendor ID or invalid service code!\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (error) {
|
||||
len =
|
||||
ptransfer_error_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, error_class, error_code, &data);
|
||||
}
|
||||
CPT_ABORT:
|
||||
pdu_len += len;
|
||||
bytes_sent =
|
||||
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
|
||||
pdu_len);
|
||||
|
||||
#if PRINT_ENABLED
|
||||
if (bytes_sent <= 0) {
|
||||
fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno));
|
||||
}
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
/*#include "arf.h" */
|
||||
/* demo objects */
|
||||
#include "ptransfer.h"
|
||||
#include "mydata.h"
|
||||
#if defined(BACFILE)
|
||||
#include "bacfile.h"
|
||||
#endif
|
||||
#include "handlers.h"
|
||||
|
||||
/** @file h_pt_a.c Handles Confirmed Private Transfer Acknowledgment. */
|
||||
|
||||
extern uint8_t IOBufferPT[300]; /* Somewhere to build the encoded result block for Private Transfers */
|
||||
|
||||
static void DecodeBlock(
|
||||
char cBlockNum,
|
||||
uint8_t * pData)
|
||||
{
|
||||
int iLen;
|
||||
uint32_t ulTemp;
|
||||
int tag_len;
|
||||
uint8_t tag_number;
|
||||
uint32_t len_value_type;
|
||||
BACNET_CHARACTER_STRING bsName;
|
||||
DATABLOCK Response;
|
||||
|
||||
iLen = 0;
|
||||
|
||||
if (cBlockNum >= MY_MAX_BLOCK)
|
||||
return;
|
||||
|
||||
tag_len =
|
||||
decode_tag_number_and_value(&pData[iLen], &tag_number,
|
||||
&len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT)
|
||||
return;
|
||||
|
||||
iLen += decode_unsigned(&pData[iLen], len_value_type, &ulTemp);
|
||||
Response.cMyByte1 = (char) ulTemp;
|
||||
|
||||
tag_len =
|
||||
decode_tag_number_and_value(&pData[iLen], &tag_number,
|
||||
&len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT)
|
||||
return;
|
||||
|
||||
iLen += decode_unsigned(&pData[iLen], len_value_type, &ulTemp);
|
||||
Response.cMyByte2 = (char) ulTemp;
|
||||
|
||||
tag_len =
|
||||
decode_tag_number_and_value(&pData[iLen], &tag_number,
|
||||
&len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_REAL)
|
||||
return;
|
||||
|
||||
iLen += decode_real(&pData[iLen], &Response.fMyReal);
|
||||
|
||||
tag_len =
|
||||
decode_tag_number_and_value(&pData[iLen], &tag_number,
|
||||
&len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_CHARACTER_STRING)
|
||||
return;
|
||||
|
||||
iLen += decode_character_string(&pData[iLen], len_value_type, &bsName);
|
||||
strncpy((char *) Response.sMyString, characterstring_value(&bsName),
|
||||
MY_MAX_STR);
|
||||
Response.sMyString[MY_MAX_STR] = '\0'; /* Make sure it is nul terminated */
|
||||
|
||||
printf("Private Transfer Read Block Response\n");
|
||||
printf("Data Block: %d\n", (int) cBlockNum);
|
||||
printf(" First Byte : %d\n", (int) Response.cMyByte1);
|
||||
printf(" Second Byte : %d\n", (int) Response.cMyByte2);
|
||||
printf(" Real : %f\n", Response.fMyReal);
|
||||
printf(" String : %s\n\n", Response.sMyString);
|
||||
}
|
||||
|
||||
static void ProcessPTA(
|
||||
BACNET_PRIVATE_TRANSFER_DATA * data)
|
||||
{
|
||||
int iLen; /* Index to current location in data */
|
||||
uint32_t uiErrorCode;
|
||||
char cBlockNumber;
|
||||
uint32_t ulTemp;
|
||||
int tag_len;
|
||||
uint8_t tag_number;
|
||||
uint32_t len_value_type;
|
||||
|
||||
iLen = 0;
|
||||
|
||||
/* Error code is returned for read and write operations */
|
||||
|
||||
tag_len =
|
||||
decode_tag_number_and_value(&data->serviceParameters[iLen],
|
||||
&tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
#if PRINT_ENABLED
|
||||
printf("CPTA: Bad Encoding!\n");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
iLen +=
|
||||
decode_unsigned(&data->serviceParameters[iLen], len_value_type,
|
||||
&uiErrorCode);
|
||||
|
||||
if (data->serviceNumber == MY_SVC_READ) { /* Read I/O block so should be full block of data or error */
|
||||
/* Decode the error type and if necessary block number and then fetch the info */
|
||||
|
||||
if (uiErrorCode == MY_ERR_OK) {
|
||||
/* Block Number */
|
||||
tag_len =
|
||||
decode_tag_number_and_value(&data->serviceParameters[iLen],
|
||||
&tag_number, &len_value_type);
|
||||
iLen += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
#if PRINT_ENABLED
|
||||
printf("CPTA: Bad Encoding!\n");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
iLen +=
|
||||
decode_unsigned(&data->serviceParameters[iLen], len_value_type,
|
||||
&ulTemp);
|
||||
cBlockNumber = (char) ulTemp;
|
||||
DecodeBlock(cBlockNumber, &data->serviceParameters[iLen]);
|
||||
} else { /* Read error */
|
||||
printf
|
||||
("Private Transfer read operation returned error code: %lu\n",
|
||||
(unsigned long) uiErrorCode);
|
||||
return;
|
||||
}
|
||||
} else { /* Write I/O block - should just be an OK type message */
|
||||
printf("Private Transfer write operation returned error code: %lu\n",
|
||||
(unsigned long) uiErrorCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* This is called when we receive a private transfer packet ack.
|
||||
* We parse the response which the remote application generated
|
||||
* and decide what to do next...
|
||||
*/
|
||||
|
||||
void handler_conf_private_trans_ack(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data)
|
||||
{
|
||||
BACNET_PRIVATE_TRANSFER_DATA data;
|
||||
int len;
|
||||
|
||||
/*
|
||||
* Note:
|
||||
* We currently don't look at the source address and service data
|
||||
* but we probably should to verify that the ack is oneit is what
|
||||
* we were expecting. But this is just to silence some compiler
|
||||
* warnings from Borland.
|
||||
*/
|
||||
src = src;
|
||||
service_data = service_data;
|
||||
|
||||
len = 0;
|
||||
|
||||
|
||||
|
||||
#if PRINT_ENABLED
|
||||
printf("Received Confirmed Private Transfer Ack!\n");
|
||||
#endif
|
||||
|
||||
len = ptransfer_decode_service_request(service_request, service_len, &data); /* Same decode for ack as for service request! */
|
||||
if (len < 0) {
|
||||
#if PRINT_ENABLED
|
||||
printf("cpta: Bad Encoding!\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
ProcessPTA(&data); /* See what to do with the response */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void PTErrorHandler(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id,
|
||||
BACNET_ERROR_CLASS error_class,
|
||||
BACNET_ERROR_CODE error_code)
|
||||
{
|
||||
/* FIXME: verify src and invoke id */
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
printf("BACnet Error: %s: %s\r\n",
|
||||
bactext_error_class_name((int) error_class),
|
||||
bactext_error_code_name((int) error_code));
|
||||
Error_Detected = true;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,173 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include <errno.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacerror.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "reject.h"
|
||||
#include "rd.h"
|
||||
/* custom handling in device object */
|
||||
#include "device.h"
|
||||
#include "handlers.h"
|
||||
|
||||
/** @file h_rd.c Handles Reinitialize Device requests. */
|
||||
|
||||
/** Handler for a Reinitialize Device (RD) request.
|
||||
* @ingroup DMRD
|
||||
* This handler will be invoked by apdu_handler() if it has been enabled
|
||||
* by a call to apdu_set_confirmed_handler().
|
||||
* This handler builds a response packet, which is
|
||||
* - an Abort if
|
||||
* - the message is segmented
|
||||
* - if decoding fails
|
||||
* - an Error if
|
||||
* - the RD password is incorrect
|
||||
* - the Reinitialize Device operation fails
|
||||
* - a Reject if the request state is invalid
|
||||
* - else tries to send a simple ACK for the RD on success.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
void handler_reinitialize_device(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
BACNET_REINITIALIZE_DEVICE_DATA rd_data;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
/* encode the NPDU portion of the packet */
|
||||
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 PRINT_ENABLED
|
||||
fprintf(stderr, "ReinitializeDevice!\n");
|
||||
#endif
|
||||
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,
|
||||
"ReinitializeDevice: Sending Abort - segmented message.\n");
|
||||
#endif
|
||||
goto RD_ABORT;
|
||||
}
|
||||
/* decode the service request only */
|
||||
len =
|
||||
rd_decode_service_request(service_request, service_len, &rd_data.state,
|
||||
&rd_data.password);
|
||||
#if PRINT_ENABLED
|
||||
if (len > 0) {
|
||||
fprintf(stderr, "ReinitializeDevice: state=%u password=%s\n",
|
||||
(unsigned) rd_data.state,
|
||||
characterstring_value(&rd_data.password));
|
||||
} else {
|
||||
fprintf(stderr, "ReinitializeDevice: Unable to decode request!\n");
|
||||
}
|
||||
#endif
|
||||
/* bad decoding or something we didn't understand - 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,
|
||||
"ReinitializeDevice: Sending Abort - could not decode.\n");
|
||||
#endif
|
||||
goto RD_ABORT;
|
||||
}
|
||||
/* check the data from the request */
|
||||
if (rd_data.state >= BACNET_REINIT_MAX) {
|
||||
len =
|
||||
reject_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, REJECT_REASON_UNDEFINED_ENUMERATION);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"ReinitializeDevice: Sending Reject - undefined enumeration\n");
|
||||
#endif
|
||||
} else {
|
||||
#if BAC_ROUTING
|
||||
/* Check to see if the current Device supports this service. */
|
||||
len =
|
||||
Routed_Device_Service_Approval
|
||||
(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, (int) rd_data.state,
|
||||
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id);
|
||||
if (len > 0)
|
||||
goto RD_ABORT;
|
||||
#endif
|
||||
|
||||
if (Device_Reinitialize(&rd_data)) {
|
||||
len =
|
||||
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_REINITIALIZE_DEVICE);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "ReinitializeDevice: Sending Simple Ack!\n");
|
||||
#endif
|
||||
} else {
|
||||
len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_REINITIALIZE_DEVICE,
|
||||
rd_data.error_class, rd_data.error_code);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "ReinitializeDevice: Sending Error.\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
RD_ABORT:
|
||||
pdu_len += len;
|
||||
len =
|
||||
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
|
||||
pdu_len);
|
||||
if (len <= 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "ReinitializeDevice: Failed to send PDU (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2010 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
/* Acknowledging the contribution of code and ideas used here that
|
||||
* came from Paul Chapman's vmac demo project. */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacint.h"
|
||||
#include "bacenum.h"
|
||||
#include "bits.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "handlers.h"
|
||||
#include "device.h"
|
||||
#include "client.h"
|
||||
#include "bactext.h"
|
||||
#include "debug.h"
|
||||
|
||||
#if PRINT_ENABLED
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#if defined(BACDL_BIP)
|
||||
#include "bvlc.h"
|
||||
#endif
|
||||
|
||||
|
||||
/** @file h_routed_npdu.c Handles messages at the NPDU level of the BACnet stack,
|
||||
* including routing and network control messages. */
|
||||
|
||||
|
||||
/** Handler to manage the Network Layer Control Messages received in a packet.
|
||||
* This handler is called if the NCPI bit 7 indicates that this packet is a
|
||||
* network layer message and there is no further DNET to pass it to.
|
||||
* The NCPI has already been decoded into the npdu_data structure.
|
||||
* @ingroup MISCHNDLR
|
||||
*
|
||||
* @param src [in] The routing source information, if any.
|
||||
* If src->net and src->len are 0, there is no
|
||||
* routing source information.
|
||||
* @param DNET_list [in] List of our reachable downstream BACnet Network numbers.
|
||||
* Normally just one valid entry; terminated with a -1 value.
|
||||
* @param npdu_data [in] Contains a filled-out structure with information
|
||||
* decoded from the NCPI and other NPDU bytes.
|
||||
* @param npdu [in] Buffer containing the rest of the NPDU, following the
|
||||
* bytes that have already been decoded.
|
||||
* @param npdu_len [in] The length of the remaining NPDU message in npdu[].
|
||||
*/
|
||||
static void network_control_handler(
|
||||
BACNET_ADDRESS * src,
|
||||
int *DNET_list,
|
||||
BACNET_NPDU_DATA * npdu_data,
|
||||
uint8_t * npdu,
|
||||
uint16_t npdu_len)
|
||||
{
|
||||
uint16_t npdu_offset = 0;
|
||||
uint16_t dnet = 0;
|
||||
uint16_t len = 0;
|
||||
|
||||
switch (npdu_data->network_message_type) {
|
||||
case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK:
|
||||
/* Send I-am-router-to-network with our one-network list if
|
||||
* our specific network is requested, or no specific
|
||||
* network is requested. Silently drop other DNET requests.
|
||||
*/
|
||||
if (npdu_len >= 2) {
|
||||
uint16_t network;
|
||||
len += decode_unsigned16(&npdu[len], &network);
|
||||
if (network == DNET_list[0]) {
|
||||
Send_I_Am_Router_To_Network(DNET_list);
|
||||
}
|
||||
} else {
|
||||
Send_I_Am_Router_To_Network(DNET_list);
|
||||
}
|
||||
break;
|
||||
case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK:
|
||||
/* Per the standard, we are supposed to process this message and
|
||||
* add its DNETs to our routing table.
|
||||
* However, since we only have one upstream port that these
|
||||
* messages can come from and replies go to, it doesn't seem
|
||||
* to provide us any value to do this; when we need to send to
|
||||
* some remote device, we will start by pushing it out the
|
||||
* upstream port and let the attached router(s) take it from there.
|
||||
* Consequently, we'll do nothing interesting here.
|
||||
* -- Unless we act upon NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK
|
||||
* later for congestion control - then it could matter.
|
||||
*/
|
||||
debug_printf("%s for Networks: ",
|
||||
bactext_network_layer_msg_name
|
||||
(NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK));
|
||||
while (npdu_len >= 2) {
|
||||
len = decode_unsigned16(&npdu[npdu_offset], &dnet);
|
||||
debug_printf("%hu", dnet);
|
||||
npdu_len -= len;
|
||||
npdu_offset += len;
|
||||
if (npdu_len >= 2) {
|
||||
debug_printf(", ");
|
||||
}
|
||||
}
|
||||
debug_printf("\n");
|
||||
break;
|
||||
case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK:
|
||||
/* Do nothing, same as previous case. */
|
||||
break;
|
||||
case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK:
|
||||
if (npdu_len >= 3) {
|
||||
decode_unsigned16(&npdu[1], &dnet);
|
||||
debug_printf("Received %s for Network: ",
|
||||
bactext_network_layer_msg_name
|
||||
(NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK));
|
||||
debug_printf("%hu, Reason code: %d \n", dnet, npdu[0]);
|
||||
}
|
||||
break;
|
||||
case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK:
|
||||
/* Do nothing - don't support upstream traffic congestion control */
|
||||
break;
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE:
|
||||
/* If sent with Number of Ports == 0, we respond with
|
||||
* NETWORK_MESSAGE_INIT_RT_TABLE_ACK and a list of all our
|
||||
* reachable networks.
|
||||
*/
|
||||
if (npdu_len > 0) {
|
||||
/* If Number of Ports is 0, broadcast our "full" table */
|
||||
if (npdu[0] == 0)
|
||||
Send_Initialize_Routing_Table_Ack(NULL, DNET_list);
|
||||
else {
|
||||
/* If they sent us a list, just politely ACK it
|
||||
* with no routing list of our own. But we don't DO
|
||||
* anything with the info, either.
|
||||
*/
|
||||
int listTerminator = -1;
|
||||
Send_Initialize_Routing_Table_Ack(src, &listTerminator);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* Else, fall through to do nothing. */
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
|
||||
/* Do nothing with the routing table info, since don't support
|
||||
* upstream traffic congestion control */
|
||||
break;
|
||||
case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK:
|
||||
/* Do nothing - don't support PTP half-router control */
|
||||
break;
|
||||
default:
|
||||
/* An unrecognized message is bad; send an error response. */
|
||||
Send_Reject_Message_To_Network(src,
|
||||
NETWORK_REJECT_UNKNOWN_MESSAGE_TYPE, DNET_list[0]);
|
||||
/* Sending our DNET doesn't make a lot of sense, does it? */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** An APDU pre-handler that makes sure that the subsequent APDU handler call
|
||||
* operates on the right Device Object(s), as addressed by the destination
|
||||
* (routing) information.
|
||||
*
|
||||
* @note Even when the destination is "routed" to our virtual BACnet network,
|
||||
* the src information does not need to change to reflect that (as it normally
|
||||
* would for a routed message) because the reply will be sent from the level
|
||||
* of the gateway Device.
|
||||
*
|
||||
* @param src [in] The BACNET_ADDRESS of the message's source.
|
||||
* @param dest [in] The BACNET_ADDRESS of the message's destination.
|
||||
* @param DNET_list [in] List of our reachable downstream BACnet Network numbers.
|
||||
* Normally just one valid entry; terminated with a -1 value.
|
||||
* @param apdu [in] The apdu portion of the request, to be processed.
|
||||
* @param apdu_len [in] The total (remaining) length of the apdu.
|
||||
*/
|
||||
static void routed_apdu_handler(
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_ADDRESS * dest,
|
||||
int *DNET_list,
|
||||
uint8_t * apdu,
|
||||
uint16_t apdu_len)
|
||||
{
|
||||
int cursor = 0; /* Starting hint */
|
||||
bool bGotOne = false;
|
||||
|
||||
if (!Routed_Device_Is_Valid_Network(dest->net, DNET_list)) {
|
||||
/* We don't know how to reach this one.
|
||||
* The protocol doesn't specifically state this, but if this message
|
||||
* was broadcast to us, we should assume "someone else" is handling
|
||||
* it and not get involved (ie, send a Reject-Message).
|
||||
* Since we can't reach other routers that src couldn't already reach,
|
||||
* we don't try the standard path of asking Who-Is-Router-to-Network. */
|
||||
#if defined(BACDL_BIP)
|
||||
/* If wasn't unicast to us, must have been one of the bcast types.
|
||||
* Drop it. */
|
||||
if (bvlc_get_function_code() != BVLC_ORIGINAL_UNICAST_NPDU)
|
||||
return;
|
||||
#endif
|
||||
/* Upper level handlers knew that this was sent as a bcast,
|
||||
* but our only other way to guess at that here is if the dest->adr
|
||||
* is absent, then we know this is some sort of bcast.
|
||||
*/
|
||||
if (dest->len > 0) {
|
||||
Send_Reject_Message_To_Network(src, NETWORK_REJECT_NO_ROUTE,
|
||||
dest->net);
|
||||
} /* else, silently drop it */
|
||||
return;
|
||||
}
|
||||
|
||||
while (Routed_Device_GetNext(dest, DNET_list, &cursor)) {
|
||||
apdu_handler(src, apdu, apdu_len);
|
||||
bGotOne = true;
|
||||
if (cursor < 0) /* If no more matches, */
|
||||
break; /* We don't need to keep looking */
|
||||
}
|
||||
if (!bGotOne) {
|
||||
/* Just silently drop this packet. */
|
||||
}
|
||||
}
|
||||
|
||||
/** Handler for the NPDU portion of a received packet, which may have routing.
|
||||
* This is a fuller handler than the regular npdu_handler, as it manages
|
||||
* - Decoding of the NCPI byte
|
||||
* - Further processing by network_control_handler() if this is a network
|
||||
* layer message.
|
||||
* - Further processing by routed_apdu_handler() if it contains an APDU
|
||||
* - Normally (no routing) by apdu_handler()
|
||||
* - With Routing (a further destination was indicated) by the decoded
|
||||
* destination.
|
||||
* - Errors in decoding.
|
||||
* @note The npdu_data->data_expecting_reply status is discarded.
|
||||
* @see npdu_handler
|
||||
* @ingroup NMRC
|
||||
*
|
||||
* @param src [out] Returned with routing source information if the NPDU
|
||||
* has any and if this points to non-null storage for it.
|
||||
* If src->net and src->len are 0 on return, there is no
|
||||
* routing source information.
|
||||
* This src describes the original source of the message when
|
||||
* it had to be routed to reach this BACnet Device, and this
|
||||
* is passed down into the apdu_handler; however, I don't
|
||||
* think this project's code has any use for the src info
|
||||
* on return from this handler, since the response has
|
||||
* already been sent via the apdu_handler.
|
||||
* @param DNET_list [in] List of our reachable downstream BACnet Network numbers.
|
||||
* Normally just one valid entry; terminated with a -1 value.
|
||||
* @param pdu [in] Buffer containing the NPDU and APDU of the received packet.
|
||||
* @param pdu_len [in] The size of the received message in the pdu[] buffer.
|
||||
*/
|
||||
void routing_npdu_handler(
|
||||
BACNET_ADDRESS * src,
|
||||
int *DNET_list,
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len)
|
||||
{
|
||||
int apdu_offset = 0;
|
||||
BACNET_ADDRESS dest = { 0 };
|
||||
BACNET_NPDU_DATA npdu_data = { 0 };
|
||||
|
||||
/* only handle the version that we know how to handle */
|
||||
if (pdu[0] == BACNET_PROTOCOL_VERSION) {
|
||||
apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data);
|
||||
if (apdu_offset <= 0) {
|
||||
debug_printf("NPDU: Decoding failed; Discarded!\n");
|
||||
} else if (npdu_data.network_layer_message) {
|
||||
if ((dest.net == 0) || (dest.net == BACNET_BROADCAST_NETWORK)) {
|
||||
network_control_handler(src, DNET_list, &npdu_data,
|
||||
&pdu[apdu_offset], (uint16_t) (pdu_len - apdu_offset));
|
||||
} else {
|
||||
/* The DNET is set, but we don't support downstream routers,
|
||||
* so we just silently drop this network layer message,
|
||||
* since only routers can handle it (even if for our DNET) */
|
||||
}
|
||||
} else if (apdu_offset <= pdu_len) {
|
||||
if ((dest.net == 0) || (npdu_data.hop_count > 1))
|
||||
routed_apdu_handler(src, &dest, DNET_list, &pdu[apdu_offset],
|
||||
(uint16_t) (pdu_len - apdu_offset));
|
||||
/* Else, hop_count bottomed out and we discard this one. */
|
||||
}
|
||||
} else {
|
||||
/* Should we send NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK? */
|
||||
debug_printf
|
||||
("NPDU: Unsupported BACnet Protocol Version=%u. Discarded!\n",
|
||||
(unsigned) pdu[0]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacerror.h"
|
||||
#include "bacdevobjpropref.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "reject.h"
|
||||
#include "rp.h"
|
||||
/* device object has custom handler for all objects */
|
||||
#include "device.h"
|
||||
#include "handlers.h"
|
||||
|
||||
/** @file h_rp.c Handles Read Property requests. */
|
||||
|
||||
|
||||
/** Handler for a ReadProperty Service request.
|
||||
* @ingroup DSRP
|
||||
* This handler will be invoked by apdu_handler() if it has been enabled
|
||||
* by a call to apdu_set_confirmed_handler().
|
||||
* This handler builds a response packet, which is
|
||||
* - an Abort if
|
||||
* - the message is segmented
|
||||
* - if decoding fails
|
||||
* - if the response would be too large
|
||||
* - the result from Device_Read_Property(), if it succeeds
|
||||
* - an Error if Device_Read_Property() fails
|
||||
* or there isn't enough room in the APDU to fit the data.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
void handler_read_property(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
BACNET_READ_PROPERTY_DATA rpdata;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int apdu_len = -1;
|
||||
int npdu_len = -1;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
bool error = true; /* assume that there is an error */
|
||||
int bytes_sent = 0;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
/* configure default error code as an abort since it is common */
|
||||
rpdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
/* encode the NPDU portion of the packet */
|
||||
datalink_get_my_address(&my_address);
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
npdu_len =
|
||||
npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address,
|
||||
&npdu_data);
|
||||
if (service_data->segmented_message) {
|
||||
/* we don't support segmentation - send an abort */
|
||||
len = BACNET_STATUS_ABORT;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RP: Segmented message. Sending Abort!\n");
|
||||
#endif
|
||||
goto RP_FAILURE;
|
||||
}
|
||||
len = rp_decode_service_request(service_request, service_len, &rpdata);
|
||||
#if PRINT_ENABLED
|
||||
if (len <= 0) {
|
||||
fprintf(stderr, "RP: Unable to decode Request!\n");
|
||||
}
|
||||
#endif
|
||||
if (len < 0) {
|
||||
/* bad decoding - skip to error/reject/abort handling */
|
||||
error = true;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RP: Bad Encoding.\n");
|
||||
#endif
|
||||
goto RP_FAILURE;
|
||||
}
|
||||
/* Test for case of indefinite Device object instance */
|
||||
if ((rpdata.object_type == OBJECT_DEVICE) &&
|
||||
(rpdata.object_instance == BACNET_MAX_INSTANCE)) {
|
||||
rpdata.object_instance = Device_Object_Instance_Number();
|
||||
}
|
||||
|
||||
apdu_len =
|
||||
rp_ack_encode_apdu_init(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id, &rpdata);
|
||||
/* configure our storage */
|
||||
rpdata.application_data = &Handler_Transmit_Buffer[npdu_len + apdu_len];
|
||||
rpdata.application_data_len =
|
||||
sizeof(Handler_Transmit_Buffer) - (npdu_len + apdu_len);
|
||||
len = Device_Read_Property(&rpdata);
|
||||
if (len >= 0) {
|
||||
apdu_len += len;
|
||||
len =
|
||||
rp_ack_encode_apdu_object_property_end(&Handler_Transmit_Buffer
|
||||
[npdu_len + apdu_len]);
|
||||
apdu_len += len;
|
||||
if (apdu_len > service_data->max_resp) {
|
||||
/* too big for the sender - send an abort
|
||||
* Setting of error code needed here as read property processing may
|
||||
* have overriden the default set at start */
|
||||
rpdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
len = BACNET_STATUS_ABORT;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RP: Message too large.\n");
|
||||
#endif
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RP: Sending Ack!\n");
|
||||
#endif
|
||||
error = false;
|
||||
}
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RP: Device_Read_Property: ");
|
||||
if (len == BACNET_STATUS_ABORT) {
|
||||
fprintf(stderr, "Abort!\n");
|
||||
} else if (len == BACNET_STATUS_ERROR) {
|
||||
fprintf(stderr, "Error!\n");
|
||||
} else if (len == BACNET_STATUS_REJECT) {
|
||||
fprintf(stderr, "Reject!\n");
|
||||
} else {
|
||||
fprintf(stderr, "Unknown Len=%d\n", len);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
RP_FAILURE:
|
||||
if (error) {
|
||||
if (len == BACNET_STATUS_ABORT) {
|
||||
apdu_len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id,
|
||||
abort_convert_error_code(rpdata.error_code), true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RP: Sending Abort!\n");
|
||||
#endif
|
||||
} else if (len == BACNET_STATUS_ERROR) {
|
||||
apdu_len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_READ_PROPERTY,
|
||||
rpdata.error_class, rpdata.error_code);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RP: Sending Error!\n");
|
||||
#endif
|
||||
} else if (len == BACNET_STATUS_REJECT) {
|
||||
apdu_len =
|
||||
reject_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id,
|
||||
reject_convert_error_code(rpdata.error_code));
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RP: Sending Reject!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
pdu_len = npdu_len + apdu_len;
|
||||
bytes_sent =
|
||||
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
|
||||
pdu_len);
|
||||
if (bytes_sent <= 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno));
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "bactext.h"
|
||||
#include "rp.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
|
||||
/** @file h_rp_a.c Handles Read Property Acknowledgments. */
|
||||
|
||||
/** For debugging...
|
||||
* @param [in] data portion of the ACK
|
||||
*/
|
||||
void rp_ack_print_data(
|
||||
BACNET_READ_PROPERTY_DATA * data)
|
||||
{
|
||||
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;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stdout, "{");
|
||||
#endif
|
||||
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_print_value(stdout, &object_value);
|
||||
if (len > 0) {
|
||||
if (len < application_data_len) {
|
||||
application_data += len;
|
||||
application_data_len -= len;
|
||||
/* there's more! */
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stdout, ",");
|
||||
#endif
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#if PRINT_ENABLED
|
||||
if (print_brace)
|
||||
fprintf(stdout, "}");
|
||||
fprintf(stdout, "\r\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Handler for a ReadProperty ACK.
|
||||
* @ingroup DSRP
|
||||
* Doesn't actually do anything, except, for debugging, to
|
||||
* print out the ACK message.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
void handler_read_property_ack(
|
||||
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;
|
||||
|
||||
(void) src;
|
||||
(void) service_data; /* we could use these... */
|
||||
len = rp_ack_decode_service_request(service_request, service_len, &data);
|
||||
#if 0
|
||||
fprintf(stderr, "Received Read-Property Ack!\n");
|
||||
#endif
|
||||
if (len > 0)
|
||||
rp_ack_print_data(&data);
|
||||
}
|
||||
|
||||
/** Decode the received RP data into a linked list of the results, with the
|
||||
* same data structure used by RPM ACK replies.
|
||||
* This function is provided to provide common handling for RP and RPM data,
|
||||
* and fully decodes the value(s) portion of the data for one property.
|
||||
* @ingroup DSRP
|
||||
* @see rp_ack_decode_service_request(), rpm_ack_decode_service_request()
|
||||
*
|
||||
* @param apdu [in] The received apdu data.
|
||||
* @param apdu_len [in] Total length of the apdu.
|
||||
* @param read_access_data [out] Pointer to the head of the linked list
|
||||
* where the RP data is to be stored.
|
||||
* @return Number of decoded bytes (could be less than apdu_len),
|
||||
* or -1 on decoding error.
|
||||
*/
|
||||
int rp_ack_fully_decode_service_request(
|
||||
uint8_t * apdu,
|
||||
int apdu_len,
|
||||
BACNET_READ_ACCESS_DATA * read_access_data)
|
||||
{
|
||||
int decoded_len = 0; /* return value */
|
||||
BACNET_READ_PROPERTY_DATA rp1data;
|
||||
BACNET_PROPERTY_REFERENCE *rp1_property; /* single property */
|
||||
BACNET_APPLICATION_DATA_VALUE *value, *old_value;
|
||||
uint8_t *vdata;
|
||||
int vlen, len;
|
||||
|
||||
decoded_len = rp_ack_decode_service_request(apdu, apdu_len, &rp1data);
|
||||
if (decoded_len > 0) {
|
||||
/* Then we have to transfer to the BACNET_READ_ACCESS_DATA structure
|
||||
* and decode the value(s) portion
|
||||
*/
|
||||
read_access_data->object_type = rp1data.object_type;
|
||||
read_access_data->object_instance = rp1data.object_instance;
|
||||
rp1_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE));
|
||||
read_access_data->listOfProperties = rp1_property;
|
||||
if (rp1_property == NULL) {
|
||||
/* can't proceed if calloc failed. */
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
rp1_property->propertyIdentifier = rp1data.object_property;
|
||||
rp1_property->propertyArrayIndex = rp1data.array_index;
|
||||
/* Is there no Error case possible here, as there is when decoding RPM? */
|
||||
/* rp1_property->error.error_class = ?? */
|
||||
/* rp_ack_decode_service_request() processing already removed the
|
||||
* Opening and Closing '3' Tags.
|
||||
* note: if this is an array, there will be
|
||||
more than one element to decode */
|
||||
vdata = rp1data.application_data;
|
||||
vlen = rp1data.application_data_len;
|
||||
value = calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE));
|
||||
rp1_property->value = value;
|
||||
old_value = value;
|
||||
while (value && vdata && (vlen > 0)) {
|
||||
if (IS_CONTEXT_SPECIFIC(*vdata)) {
|
||||
len =
|
||||
bacapp_decode_context_data(vdata, vlen, value,
|
||||
rp1_property->propertyIdentifier);
|
||||
} else {
|
||||
len = bacapp_decode_application_data(vdata, vlen, value);
|
||||
}
|
||||
if (len < 0) {
|
||||
/* unable to decode the data */
|
||||
while (value) {
|
||||
/* free the linked list of values */
|
||||
old_value = value;
|
||||
value = value->next;
|
||||
free(old_value);
|
||||
}
|
||||
free(rp1_property);
|
||||
read_access_data->listOfProperties = NULL;
|
||||
return len;
|
||||
}
|
||||
decoded_len += len;
|
||||
vlen -= len;
|
||||
vdata += len;
|
||||
/* If unexpected closing tag here: */
|
||||
if (vlen && decode_is_closing_tag_number(vdata, 3)) {
|
||||
decoded_len++;
|
||||
vlen--;
|
||||
vdata++;
|
||||
break;
|
||||
} else {
|
||||
if (len == 0) {
|
||||
/* nothing decoded and no closing tag, so malformed */
|
||||
while (value) {
|
||||
/* free the linked list of values */
|
||||
old_value = value;
|
||||
value = value->next;
|
||||
free(old_value);
|
||||
}
|
||||
free(rp1_property);
|
||||
read_access_data->listOfProperties = NULL;
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
if (vlen > 0) {
|
||||
/* If more values */
|
||||
old_value = value;
|
||||
value = calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE));
|
||||
old_value->next = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return decoded_len;
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
|
||||
* Inspired by John Stachler <John.Stachler@lennoxind.com>
|
||||
*
|
||||
* 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "memcopy.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "reject.h"
|
||||
#include "bacerror.h"
|
||||
#include "rpm.h"
|
||||
#include "handlers.h"
|
||||
/* device object has custom handler for all objects */
|
||||
#include "device.h"
|
||||
|
||||
/** @file h_rpm.c Handles Read Property Multiple requests. */
|
||||
|
||||
static uint8_t Temp_Buf[MAX_APDU] = { 0 };
|
||||
|
||||
static BACNET_PROPERTY_ID RPM_Object_Property(
|
||||
struct special_property_list_t *pPropertyList,
|
||||
BACNET_PROPERTY_ID special_property,
|
||||
unsigned index)
|
||||
{
|
||||
int property = -1; /* return value */
|
||||
unsigned required, optional, proprietary;
|
||||
|
||||
required = pPropertyList->Required.count;
|
||||
optional = pPropertyList->Optional.count;
|
||||
proprietary = pPropertyList->Proprietary.count;
|
||||
if (special_property == PROP_ALL) {
|
||||
if (index < required) {
|
||||
property = pPropertyList->Required.pList[index];
|
||||
} else if (index < (required + optional)) {
|
||||
index -= required;
|
||||
property = pPropertyList->Optional.pList[index];
|
||||
} else if (index < (required + optional + proprietary)) {
|
||||
index -= (required + optional);
|
||||
property = pPropertyList->Proprietary.pList[index];
|
||||
}
|
||||
} else if (special_property == PROP_REQUIRED) {
|
||||
if (index < required) {
|
||||
property = pPropertyList->Required.pList[index];
|
||||
}
|
||||
} else if (special_property == PROP_OPTIONAL) {
|
||||
if (index < optional) {
|
||||
property = pPropertyList->Optional.pList[index];
|
||||
}
|
||||
}
|
||||
|
||||
return (BACNET_PROPERTY_ID) property;
|
||||
}
|
||||
|
||||
static unsigned RPM_Object_Property_Count(
|
||||
struct special_property_list_t *pPropertyList,
|
||||
BACNET_PROPERTY_ID special_property)
|
||||
{
|
||||
unsigned count = 0; /* return value */
|
||||
|
||||
if (special_property == PROP_ALL) {
|
||||
count =
|
||||
pPropertyList->Required.count + pPropertyList->Optional.count +
|
||||
pPropertyList->Proprietary.count;
|
||||
} else if (special_property == PROP_REQUIRED) {
|
||||
count = pPropertyList->Required.count;
|
||||
} else if (special_property == PROP_OPTIONAL) {
|
||||
count = pPropertyList->Optional.count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/** Encode the RPM property returning the length of the encoding,
|
||||
or 0 if there is no room to fit the encoding. */
|
||||
static int RPM_Encode_Property(
|
||||
uint8_t * apdu,
|
||||
uint16_t offset,
|
||||
uint16_t max_apdu,
|
||||
BACNET_RPM_DATA * rpmdata)
|
||||
{
|
||||
int len = 0;
|
||||
size_t copy_len = 0;
|
||||
int apdu_len = 0;
|
||||
BACNET_READ_PROPERTY_DATA rpdata;
|
||||
|
||||
len =
|
||||
rpm_ack_encode_apdu_object_property(&Temp_Buf[0],
|
||||
rpmdata->object_property, rpmdata->array_index);
|
||||
copy_len = memcopy(&apdu[0], &Temp_Buf[0], offset, len, max_apdu);
|
||||
if (copy_len == 0) {
|
||||
rpmdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
return BACNET_STATUS_ABORT;
|
||||
}
|
||||
apdu_len += len;
|
||||
len = 0;
|
||||
rpdata.error_class = ERROR_CLASS_OBJECT;
|
||||
rpdata.error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
rpdata.object_type = rpmdata->object_type;
|
||||
rpdata.object_instance = rpmdata->object_instance;
|
||||
rpdata.object_property = rpmdata->object_property;
|
||||
rpdata.array_index = rpmdata->array_index;
|
||||
rpdata.application_data = &Temp_Buf[0];
|
||||
rpdata.application_data_len = sizeof(Temp_Buf);
|
||||
len = Device_Read_Property(&rpdata);
|
||||
if (len < 0) {
|
||||
if ((len == BACNET_STATUS_ABORT) || (len == BACNET_STATUS_REJECT)) {
|
||||
rpmdata->error_code = rpdata.error_code;
|
||||
/* pass along aborts and rejects for now */
|
||||
return len; /* Ie, Abort */
|
||||
}
|
||||
/* error was returned - encode that for the response */
|
||||
len =
|
||||
rpm_ack_encode_apdu_object_property_error(&Temp_Buf[0],
|
||||
rpdata.error_class, rpdata.error_code);
|
||||
copy_len =
|
||||
memcopy(&apdu[0], &Temp_Buf[0], offset + apdu_len, len, max_apdu);
|
||||
|
||||
if (copy_len == 0) {
|
||||
rpmdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
return BACNET_STATUS_ABORT;
|
||||
}
|
||||
} else if ((offset + apdu_len + 1 + len + 1) < max_apdu) {
|
||||
/* enough room to fit the property value and tags */
|
||||
len =
|
||||
rpm_ack_encode_apdu_object_property_value(&apdu[offset + apdu_len],
|
||||
&Temp_Buf[0], len);
|
||||
} else {
|
||||
/* not enough room - abort! */
|
||||
rpmdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
return BACNET_STATUS_ABORT;
|
||||
}
|
||||
apdu_len += len;
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/** Handler for a ReadPropertyMultiple Service request.
|
||||
* @ingroup DSRPM
|
||||
* This handler will be invoked by apdu_handler() if it has been enabled
|
||||
* by a call to apdu_set_confirmed_handler().
|
||||
* This handler builds a response packet, which is
|
||||
* - an Abort if
|
||||
* - the message is segmented
|
||||
* - if decoding fails
|
||||
* - if the response would be too large
|
||||
* - the result from each included read request, if it succeeds
|
||||
* - an Error if processing fails for all, or individual errors if only some fail,
|
||||
* or there isn't enough room in the APDU to fit the data.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
void handler_read_property_multiple(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
int len = 0;
|
||||
uint16_t copy_len = 0;
|
||||
uint16_t decode_len = 0;
|
||||
int pdu_len = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
int bytes_sent;
|
||||
BACNET_ADDRESS my_address;
|
||||
BACNET_RPM_DATA rpmdata;
|
||||
int apdu_len = 0;
|
||||
int npdu_len = 0;
|
||||
int error = 0;
|
||||
|
||||
/* jps_debug - see if we are utilizing all the buffer */
|
||||
/* memset(&Handler_Transmit_Buffer[0], 0xff, sizeof(Handler_Transmit_Buffer)); */
|
||||
/* encode the NPDU portion of the packet */
|
||||
datalink_get_my_address(&my_address);
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
npdu_len =
|
||||
npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address,
|
||||
&npdu_data);
|
||||
if (service_data->segmented_message) {
|
||||
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
error = BACNET_STATUS_ABORT;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Segmented message. Sending Abort!\r\n");
|
||||
#endif
|
||||
goto RPM_FAILURE;
|
||||
}
|
||||
/* decode apdu request & encode apdu reply
|
||||
encode complex ack, invoke id, service choice */
|
||||
apdu_len =
|
||||
rpm_ack_encode_apdu_init(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id);
|
||||
for (;;) {
|
||||
/* Start by looking for an object ID */
|
||||
len =
|
||||
rpm_decode_object_id(&service_request[decode_len],
|
||||
service_len - decode_len, &rpmdata);
|
||||
if (len >= 0) {
|
||||
/* Got one so skip to next stage */
|
||||
decode_len += len;
|
||||
} else {
|
||||
/* bad encoding - skip to error/reject/abort handling */
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Bad Encoding.\n");
|
||||
#endif
|
||||
error = len;
|
||||
goto RPM_FAILURE;
|
||||
}
|
||||
|
||||
/* Test for case of indefinite Device object instance */
|
||||
if ((rpmdata.object_type == OBJECT_DEVICE) &&
|
||||
(rpmdata.object_instance == BACNET_MAX_INSTANCE)) {
|
||||
rpmdata.object_instance = Device_Object_Instance_Number();
|
||||
}
|
||||
|
||||
/* Stick this object id into the reply - if it will fit */
|
||||
len = rpm_ack_encode_apdu_object_begin(&Temp_Buf[0], &rpmdata);
|
||||
copy_len =
|
||||
memcopy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0], apdu_len,
|
||||
len, MAX_APDU);
|
||||
if (copy_len == 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Response too big!\r\n");
|
||||
#endif
|
||||
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
error = BACNET_STATUS_ABORT;
|
||||
goto RPM_FAILURE;
|
||||
}
|
||||
|
||||
apdu_len += copy_len;
|
||||
/* do each property of this object of the RPM request */
|
||||
for (;;) {
|
||||
/* Fetch a property */
|
||||
len =
|
||||
rpm_decode_object_property(&service_request[decode_len],
|
||||
service_len - decode_len, &rpmdata);
|
||||
if (len < 0) {
|
||||
/* bad encoding - skip to error/reject/abort handling */
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Bad Encoding.\n");
|
||||
#endif
|
||||
error = len;
|
||||
goto RPM_FAILURE;
|
||||
}
|
||||
decode_len += len;
|
||||
/* handle the special properties */
|
||||
if ((rpmdata.object_property == PROP_ALL) ||
|
||||
(rpmdata.object_property == PROP_REQUIRED) ||
|
||||
(rpmdata.object_property == PROP_OPTIONAL)) {
|
||||
struct special_property_list_t property_list;
|
||||
unsigned property_count = 0;
|
||||
unsigned index = 0;
|
||||
BACNET_PROPERTY_ID special_object_property;
|
||||
|
||||
if (rpmdata.array_index != BACNET_ARRAY_ALL) {
|
||||
/* No array index options for this special property.
|
||||
Encode error for this object property response */
|
||||
len =
|
||||
rpm_ack_encode_apdu_object_property(&Temp_Buf[0],
|
||||
rpmdata.object_property, rpmdata.array_index);
|
||||
copy_len =
|
||||
memcopy(&Handler_Transmit_Buffer[npdu_len],
|
||||
&Temp_Buf[0], apdu_len, len, MAX_APDU);
|
||||
if (copy_len == 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"RPM: Too full to encode property!\r\n");
|
||||
#endif
|
||||
rpmdata.error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
error = BACNET_STATUS_ABORT;
|
||||
goto RPM_FAILURE;
|
||||
}
|
||||
apdu_len += len;
|
||||
len =
|
||||
rpm_ack_encode_apdu_object_property_error(&Temp_Buf[0],
|
||||
ERROR_CLASS_PROPERTY,
|
||||
ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY);
|
||||
copy_len =
|
||||
memcopy(&Handler_Transmit_Buffer[npdu_len],
|
||||
&Temp_Buf[0], apdu_len, len, MAX_APDU);
|
||||
if (copy_len == 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Too full to encode error!\r\n");
|
||||
#endif
|
||||
rpmdata.error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
error = BACNET_STATUS_ABORT;
|
||||
goto RPM_FAILURE;
|
||||
}
|
||||
apdu_len += len;
|
||||
} else {
|
||||
special_object_property = rpmdata.object_property;
|
||||
Device_Objects_Property_List(rpmdata.object_type,
|
||||
rpmdata.object_instance, &property_list);
|
||||
property_count =
|
||||
RPM_Object_Property_Count(&property_list,
|
||||
special_object_property);
|
||||
if (property_count == 0) {
|
||||
/* this only happens with the OPTIONAL property */
|
||||
/* 135-2016bl-2. Clarify ReadPropertyMultiple
|
||||
response on OPTIONAL when empty. */
|
||||
/* If no optional properties are supported then
|
||||
an empty 'List of Results' shall be returned
|
||||
for the specified property.*/
|
||||
} else {
|
||||
for (index = 0; index < property_count; index++) {
|
||||
rpmdata.object_property =
|
||||
RPM_Object_Property(&property_list,
|
||||
special_object_property, index);
|
||||
len =
|
||||
RPM_Encode_Property(&Handler_Transmit_Buffer
|
||||
[npdu_len], (uint16_t) apdu_len, MAX_APDU,
|
||||
&rpmdata);
|
||||
if (len > 0) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"RPM: Too full for property!\r\n");
|
||||
#endif
|
||||
error = len;
|
||||
goto RPM_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* handle an individual property */
|
||||
len =
|
||||
RPM_Encode_Property(&Handler_Transmit_Buffer[npdu_len],
|
||||
(uint16_t) apdu_len, MAX_APDU, &rpmdata);
|
||||
if (len > 0) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"RPM: Too full for individual property!\r\n");
|
||||
#endif
|
||||
error = len;
|
||||
goto RPM_FAILURE;
|
||||
}
|
||||
}
|
||||
if (decode_is_closing_tag_number(&service_request[decode_len], 1)) {
|
||||
/* Reached end of property list so cap the result list */
|
||||
decode_len++;
|
||||
len = rpm_ack_encode_apdu_object_end(&Temp_Buf[0]);
|
||||
copy_len =
|
||||
memcopy(&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0],
|
||||
apdu_len, len, MAX_APDU);
|
||||
if (copy_len == 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Too full to encode object end!\r\n");
|
||||
#endif
|
||||
rpmdata.error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
error = BACNET_STATUS_ABORT;
|
||||
goto RPM_FAILURE;
|
||||
} else {
|
||||
apdu_len += copy_len;
|
||||
}
|
||||
break; /* finished with this property list */
|
||||
}
|
||||
}
|
||||
if (decode_len >= service_len) {
|
||||
/* Reached the end so finish up */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (apdu_len > service_data->max_resp) {
|
||||
/* too big for the sender - send an abort */
|
||||
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
error = BACNET_STATUS_ABORT;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Message too large. Sending Abort!\n");
|
||||
#endif
|
||||
goto RPM_FAILURE;
|
||||
}
|
||||
|
||||
RPM_FAILURE:
|
||||
if (error) {
|
||||
if (error == BACNET_STATUS_ABORT) {
|
||||
apdu_len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id,
|
||||
abort_convert_error_code(rpmdata.error_code), true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Sending Abort!\n");
|
||||
#endif
|
||||
} else if (error == BACNET_STATUS_ERROR) {
|
||||
apdu_len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_READ_PROP_MULTIPLE,
|
||||
rpmdata.error_class, rpmdata.error_code);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Sending Error!\n");
|
||||
#endif
|
||||
} else if (error == BACNET_STATUS_REJECT) {
|
||||
apdu_len =
|
||||
reject_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id,
|
||||
reject_convert_error_code(rpmdata.error_code));
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Sending Reject!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
pdu_len = apdu_len + npdu_len;
|
||||
bytes_sent =
|
||||
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
|
||||
pdu_len);
|
||||
if (bytes_sent <= 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RPM: Failed to send PDU (%s)!\n", strerror(errno));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "bactext.h"
|
||||
#include "rpm.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
|
||||
/** @file h_rpm_a.c Handles Read Property Multiple Acknowledgments. */
|
||||
|
||||
/** Decode the received RPM data and make a linked list of the results.
|
||||
* @ingroup DSRPM
|
||||
*
|
||||
* @param apdu [in] The received apdu data.
|
||||
* @param apdu_len [in] Total length of the apdu.
|
||||
* @param read_access_data [out] Pointer to the head of the linked list
|
||||
* where the RPM data is to be stored.
|
||||
* @return The number of bytes decoded, or -1 on error
|
||||
*/
|
||||
int rpm_ack_decode_service_request(
|
||||
uint8_t * apdu,
|
||||
int apdu_len,
|
||||
BACNET_READ_ACCESS_DATA * read_access_data)
|
||||
{
|
||||
int decoded_len = 0; /* return value */
|
||||
uint32_t error_value = 0; /* decoded error value */
|
||||
int len = 0; /* number of bytes returned from decoding */
|
||||
uint8_t tag_number = 0; /* decoded tag number */
|
||||
uint32_t len_value = 0; /* decoded length value */
|
||||
BACNET_READ_ACCESS_DATA *rpm_object;
|
||||
BACNET_READ_ACCESS_DATA *old_rpm_object;
|
||||
BACNET_PROPERTY_REFERENCE *rpm_property;
|
||||
BACNET_PROPERTY_REFERENCE *old_rpm_property;
|
||||
BACNET_APPLICATION_DATA_VALUE *value;
|
||||
BACNET_APPLICATION_DATA_VALUE *old_value;
|
||||
|
||||
assert(read_access_data != NULL);
|
||||
rpm_object = read_access_data;
|
||||
old_rpm_object = rpm_object;
|
||||
while (rpm_object && apdu_len) {
|
||||
len =
|
||||
rpm_ack_decode_object_id(apdu, apdu_len, &rpm_object->object_type,
|
||||
&rpm_object->object_instance);
|
||||
if (len <= 0) {
|
||||
old_rpm_object->next = NULL;
|
||||
free(rpm_object);
|
||||
break;
|
||||
}
|
||||
decoded_len += len;
|
||||
apdu_len -= len;
|
||||
apdu += len;
|
||||
rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE));
|
||||
rpm_object->listOfProperties = rpm_property;
|
||||
old_rpm_property = rpm_property;
|
||||
while (rpm_property && apdu_len) {
|
||||
len =
|
||||
rpm_ack_decode_object_property(apdu, apdu_len,
|
||||
&rpm_property->propertyIdentifier,
|
||||
&rpm_property->propertyArrayIndex);
|
||||
if (len <= 0) {
|
||||
old_rpm_property->next = NULL;
|
||||
if (rpm_object->listOfProperties == rpm_property) {
|
||||
/* was this the only property in the list? */
|
||||
rpm_object->listOfProperties = NULL;
|
||||
}
|
||||
free(rpm_property);
|
||||
break;
|
||||
}
|
||||
decoded_len += len;
|
||||
apdu_len -= len;
|
||||
apdu += len;
|
||||
if (apdu_len && decode_is_opening_tag_number(apdu, 4)) {
|
||||
/* propertyValue */
|
||||
decoded_len++;
|
||||
apdu_len--;
|
||||
apdu++;
|
||||
/* note: if this is an array, there will be
|
||||
more than one element to decode */
|
||||
value = calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE));
|
||||
rpm_property->value = value;
|
||||
old_value = value;
|
||||
while (value && (apdu_len > 0)) {
|
||||
if (IS_CONTEXT_SPECIFIC(*apdu)) {
|
||||
len =
|
||||
bacapp_decode_context_data(apdu, apdu_len, value,
|
||||
rpm_property->propertyIdentifier);
|
||||
} else {
|
||||
len =
|
||||
bacapp_decode_application_data(apdu, apdu_len,
|
||||
value);
|
||||
}
|
||||
/* If len == 0 then it's an empty structure, which is OK. */
|
||||
if (len < 0) {
|
||||
/* problem decoding */
|
||||
/* calling function will free the memory */
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
decoded_len += len;
|
||||
apdu_len -= len;
|
||||
apdu += len;
|
||||
if (apdu_len && decode_is_closing_tag_number(apdu, 4)) {
|
||||
decoded_len++;
|
||||
apdu_len--;
|
||||
apdu++;
|
||||
break;
|
||||
} else {
|
||||
old_value = value;
|
||||
value =
|
||||
calloc(1, sizeof(BACNET_APPLICATION_DATA_VALUE));
|
||||
old_value->next = value;
|
||||
}
|
||||
}
|
||||
} else if (apdu_len && decode_is_opening_tag_number(apdu, 5)) {
|
||||
/* propertyAccessError */
|
||||
decoded_len++;
|
||||
apdu_len--;
|
||||
apdu++;
|
||||
/* decode the class and code sequence */
|
||||
len =
|
||||
decode_tag_number_and_value(apdu, &tag_number, &len_value);
|
||||
decoded_len += len;
|
||||
apdu_len -= len;
|
||||
apdu += len;
|
||||
/* FIXME: we could validate that the tag is enumerated... */
|
||||
len = decode_enumerated(apdu, len_value, &error_value);
|
||||
rpm_property->error.error_class = error_value;
|
||||
decoded_len += len;
|
||||
apdu_len -= len;
|
||||
apdu += len;
|
||||
len =
|
||||
decode_tag_number_and_value(apdu, &tag_number, &len_value);
|
||||
decoded_len += len;
|
||||
apdu_len -= len;
|
||||
apdu += len;
|
||||
/* FIXME: we could validate that the tag is enumerated... */
|
||||
len = decode_enumerated(apdu, len_value, &error_value);
|
||||
rpm_property->error.error_code = error_value;
|
||||
decoded_len += len;
|
||||
apdu_len -= len;
|
||||
apdu += len;
|
||||
if (apdu_len && decode_is_closing_tag_number(apdu, 5)) {
|
||||
decoded_len++;
|
||||
apdu_len--;
|
||||
apdu++;
|
||||
}
|
||||
}
|
||||
old_rpm_property = rpm_property;
|
||||
rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE));
|
||||
old_rpm_property->next = rpm_property;
|
||||
}
|
||||
len = rpm_decode_object_end(apdu, apdu_len);
|
||||
if (len) {
|
||||
decoded_len += len;
|
||||
apdu_len -= len;
|
||||
apdu += len;
|
||||
}
|
||||
if (apdu_len) {
|
||||
old_rpm_object = rpm_object;
|
||||
rpm_object = calloc(1, sizeof(BACNET_READ_ACCESS_DATA));
|
||||
old_rpm_object->next = rpm_object;
|
||||
}
|
||||
}
|
||||
|
||||
return decoded_len;
|
||||
}
|
||||
|
||||
/* for debugging... */
|
||||
void rpm_ack_print_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;
|
||||
|
||||
if (rpm_data) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stdout, "%s #%lu\r\n",
|
||||
bactext_object_type_name(rpm_data->object_type),
|
||||
(unsigned long) rpm_data->object_instance);
|
||||
fprintf(stdout, "{\r\n");
|
||||
#endif
|
||||
listOfProperties = rpm_data->listOfProperties;
|
||||
while (listOfProperties) {
|
||||
#if PRINT_ENABLED
|
||||
if (listOfProperties->propertyIdentifier < 512) {
|
||||
fprintf(stdout, " %s: ",
|
||||
bactext_property_name(listOfProperties->
|
||||
propertyIdentifier));
|
||||
} else {
|
||||
fprintf(stdout, " proprietary %u: ",
|
||||
(unsigned) listOfProperties->propertyIdentifier);
|
||||
}
|
||||
#endif
|
||||
if (listOfProperties->propertyArrayIndex != BACNET_ARRAY_ALL) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stdout, "[%d]", listOfProperties->propertyArrayIndex);
|
||||
#endif
|
||||
}
|
||||
value = listOfProperties->value;
|
||||
if (value) {
|
||||
#if PRINT_ENABLED
|
||||
if (value->next) {
|
||||
fprintf(stdout, "{");
|
||||
array_value = true;
|
||||
} else {
|
||||
array_value = false;
|
||||
}
|
||||
#endif
|
||||
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_print_value(stdout, &object_value);
|
||||
#if PRINT_ENABLED
|
||||
if (value->next) {
|
||||
fprintf(stdout, ",\r\n ");
|
||||
} else {
|
||||
if (array_value) {
|
||||
fprintf(stdout, "}\r\n");
|
||||
} else {
|
||||
fprintf(stdout, "\r\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
value = value->next;
|
||||
}
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
/* AccessError */
|
||||
fprintf(stdout, "BACnet Error: %s: %s\r\n",
|
||||
bactext_error_class_name((int) listOfProperties->
|
||||
error.error_class),
|
||||
bactext_error_code_name((int) listOfProperties->
|
||||
error.error_code));
|
||||
#endif
|
||||
}
|
||||
listOfProperties = listOfProperties->next;
|
||||
}
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stdout, "}\r\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/** Handler for a ReadPropertyMultiple ACK.
|
||||
* @ingroup DSRPM
|
||||
* For each read property, print out the ACK'd data for debugging,
|
||||
* 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.
|
||||
*/
|
||||
void handler_read_property_multiple_ack(
|
||||
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;
|
||||
|
||||
(void) src;
|
||||
(void) service_data; /* we could use these... */
|
||||
|
||||
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 1
|
||||
fprintf(stderr, "Received Read-Property-Multiple Ack!\n");
|
||||
#endif
|
||||
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 {
|
||||
#if 1
|
||||
fprintf(stderr, "RPM Ack Malformed! Freeing memory...\n");
|
||||
#endif
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2009 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacerror.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "readrange.h"
|
||||
#include "device.h"
|
||||
#include "handlers.h"
|
||||
|
||||
/** @file h_rr.c Handles Read Range requests. */
|
||||
|
||||
static uint8_t Temp_Buf[MAX_APDU] = { 0 };
|
||||
|
||||
/* Encodes the property APDU and returns the length,
|
||||
or sets the error, and returns -1 */
|
||||
static int Encode_RR_payload(
|
||||
uint8_t * apdu,
|
||||
BACNET_READ_RANGE_DATA * pRequest)
|
||||
{
|
||||
int apdu_len = -1;
|
||||
rr_info_function info_fn_ptr = NULL;
|
||||
RR_PROP_INFO PropInfo;
|
||||
|
||||
/* initialize the default return values */
|
||||
pRequest->error_class = ERROR_CLASS_SERVICES;
|
||||
pRequest->error_code = ERROR_CODE_OTHER;
|
||||
|
||||
/* handle each object type */
|
||||
info_fn_ptr = Device_Objects_RR_Info(pRequest->object_type);
|
||||
|
||||
if ((info_fn_ptr != NULL) && (info_fn_ptr(pRequest, &PropInfo) != false)) {
|
||||
/* We try and do some of the more generic error checking here to cut down on duplication of effort */
|
||||
|
||||
if ((pRequest->RequestType == RR_BY_POSITION) && (pRequest->Range.RefIndex == 0)) { /* First index is 1 so can't accept 0 */
|
||||
pRequest->error_code = ERROR_CODE_OTHER; /* I couldn't see anything more appropriate so... */
|
||||
} else if (((PropInfo.RequestTypes & RR_ARRAY_OF_LISTS) == 0) &&
|
||||
(pRequest->array_index != 0) &&
|
||||
(pRequest->array_index != BACNET_ARRAY_ALL)) {
|
||||
/* Array access attempted on a non array property */
|
||||
pRequest->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
} else if ((pRequest->RequestType != RR_READ_ALL) &&
|
||||
((PropInfo.RequestTypes & pRequest->RequestType) == 0)) {
|
||||
/* By Time or By Sequence not supported - By Position is always required */
|
||||
pRequest->error_code = ERROR_CODE_OTHER; /* I couldn't see anything more appropriate so... */
|
||||
} else if ((pRequest->Count == 0) && (pRequest->RequestType != RR_READ_ALL)) { /* Count cannot be zero */
|
||||
pRequest->error_code = ERROR_CODE_OTHER; /* I couldn't see anything more appropriate so... */
|
||||
} else if (PropInfo.Handler != NULL) {
|
||||
apdu_len = PropInfo.Handler(apdu, pRequest);
|
||||
}
|
||||
} else {
|
||||
/* Either we don't support RR for this property yet or it is not a list or array of lists */
|
||||
pRequest->error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
void handler_read_range(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
BACNET_READ_RANGE_DATA data;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
bool error = false;
|
||||
int bytes_sent = 0;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
data.error_class = ERROR_CLASS_OBJECT;
|
||||
data.error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
/* encode the NPDU portion of the packet */
|
||||
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) {
|
||||
/* we don't support segmentation - send an abort */
|
||||
len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED,
|
||||
true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RR: Segmented message. Sending Abort!\n");
|
||||
#endif
|
||||
goto RR_ABORT;
|
||||
}
|
||||
memset(&data, 0, sizeof(data)); /* start with blank canvas */
|
||||
len = rr_decode_service_request(service_request, service_len, &data);
|
||||
#if PRINT_ENABLED
|
||||
if (len <= 0)
|
||||
fprintf(stderr, "RR: Unable to decode Request!\n");
|
||||
#endif
|
||||
if (len < 0) {
|
||||
/* bad decoding - send an abort */
|
||||
len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_OTHER, true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RR: Bad Encoding. Sending Abort!\n");
|
||||
#endif
|
||||
goto RR_ABORT;
|
||||
}
|
||||
|
||||
/* assume that there is an error */
|
||||
error = true;
|
||||
len = Encode_RR_payload(&Temp_Buf[0], &data);
|
||||
if (len >= 0) {
|
||||
/* encode the APDU portion of the packet */
|
||||
data.application_data = &Temp_Buf[0];
|
||||
data.application_data_len = len;
|
||||
/* FIXME: probably need a length limitation sent with encode */
|
||||
len =
|
||||
rr_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, &data);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RR: Sending Ack!\n");
|
||||
#endif
|
||||
error = false;
|
||||
}
|
||||
if (error) {
|
||||
if (len == -2) {
|
||||
/* BACnet APDU too small to fit data, so proper response is Abort */
|
||||
len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RR: Reply too big to fit into APDU!\n");
|
||||
#endif
|
||||
} else {
|
||||
len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_READ_RANGE,
|
||||
data.error_class, data.error_code);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "RR: Sending Error!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
RR_ABORT:
|
||||
pdu_len += len;
|
||||
bytes_sent =
|
||||
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
|
||||
pdu_len);
|
||||
#if PRINT_ENABLED
|
||||
if (bytes_sent <= 0)
|
||||
fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno));
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2009 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "bactext.h"
|
||||
#include "readrange.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
|
||||
/** @file h_rr_a.c Handles Read Range Acknowledgments. */
|
||||
|
||||
/* for debugging... */
|
||||
static void PrintReadRangeData(
|
||||
BACNET_READ_RANGE_DATA * data)
|
||||
{
|
||||
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;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stdout, "{");
|
||||
#endif
|
||||
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_print_value(stdout, &object_value);
|
||||
if (len > 0) {
|
||||
if (len < application_data_len) {
|
||||
application_data += len;
|
||||
application_data_len -= len;
|
||||
/* there's more! */
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stdout, ",");
|
||||
#endif
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#if PRINT_ENABLED
|
||||
if (print_brace)
|
||||
fprintf(stdout, "}");
|
||||
fprintf(stdout, "\r\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void handler_read_range_ack(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data)
|
||||
{
|
||||
int len = 0;
|
||||
BACNET_READ_RANGE_DATA data;
|
||||
|
||||
(void) src;
|
||||
(void) service_data; /* we could use these... */
|
||||
len = rr_ack_decode_service_request(service_request, service_len, &data);
|
||||
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Received ReadRange Ack!\n");
|
||||
#endif
|
||||
|
||||
if (len > 0)
|
||||
PrintReadRangeData(&data);
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "config.h"
|
||||
#include "device.h"
|
||||
#include "datetime.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "timesync.h"
|
||||
#include "handlers.h"
|
||||
#include "client.h"
|
||||
#include "bacaddr.h"
|
||||
|
||||
/** @file h_ts.c Handles TimeSync requests. */
|
||||
|
||||
#if defined(BACNET_TIME_MASTER)
|
||||
/* sending time sync to recipients */
|
||||
#ifndef MAX_TIME_SYNC_RECIPIENTS
|
||||
#define MAX_TIME_SYNC_RECIPIENTS 16
|
||||
#endif
|
||||
BACNET_RECIPIENT_LIST Time_Sync_Recipients[MAX_TIME_SYNC_RECIPIENTS];
|
||||
/* variable used for controlling when to
|
||||
automatically send a TimeSynchronization request */
|
||||
static BACNET_DATE_TIME Next_Sync_Time;
|
||||
#endif
|
||||
|
||||
#if PRINT_ENABLED
|
||||
static void show_bacnet_date_time(
|
||||
BACNET_DATE * bdate,
|
||||
BACNET_TIME * btime)
|
||||
{
|
||||
/* show the date received */
|
||||
fprintf(stderr, "%u", (unsigned) bdate->year);
|
||||
fprintf(stderr, "/%u", (unsigned) bdate->month);
|
||||
fprintf(stderr, "/%u", (unsigned) bdate->day);
|
||||
/* show the time received */
|
||||
fprintf(stderr, " %02u", (unsigned) btime->hour);
|
||||
fprintf(stderr, ":%02u", (unsigned) btime->min);
|
||||
fprintf(stderr, ":%02u", (unsigned) btime->sec);
|
||||
fprintf(stderr, ".%02u", (unsigned) btime->hundredths);
|
||||
fprintf(stderr, "\r\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
void handler_timesync(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src)
|
||||
{
|
||||
int len = 0;
|
||||
BACNET_DATE bdate = {0};
|
||||
BACNET_TIME btime = {0};
|
||||
|
||||
(void) src;
|
||||
(void) service_len;
|
||||
len =
|
||||
timesync_decode_service_request(service_request, service_len, &bdate,
|
||||
&btime);
|
||||
if (len > 0) {
|
||||
if (datetime_is_valid(&bdate, &btime)) {
|
||||
/* fixme: only set the time if off by some amount */
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Received TimeSyncronization Request\r\n");
|
||||
show_bacnet_date_time(&bdate, &btime);
|
||||
#else
|
||||
/* FIXME: set the time?
|
||||
Maybe only set the time if off by some amount */
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void handler_timesync_utc(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src)
|
||||
{
|
||||
int len = 0;
|
||||
BACNET_DATE bdate;
|
||||
BACNET_TIME btime;
|
||||
|
||||
(void) src;
|
||||
(void) service_len;
|
||||
len =
|
||||
timesync_decode_service_request(service_request, service_len, &bdate,
|
||||
&btime);
|
||||
if (len > 0) {
|
||||
if (datetime_is_valid(&bdate, &btime)) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Received TimeSyncronization Request\r\n");
|
||||
show_bacnet_date_time(&bdate, &btime);
|
||||
#endif
|
||||
/* FIXME: set the time?
|
||||
only set the time if off by some amount */
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(BACNET_TIME_MASTER)
|
||||
/** Handle a request to list all the timesync recipients.
|
||||
*
|
||||
* Invoked by a request to read the Device object's
|
||||
* PROP_TIME_SYNCHRONIZATION_RECIPIENTS.
|
||||
* Loops through the list of timesync recipients, and, for each valid one,
|
||||
* adds its data to the APDU.
|
||||
*
|
||||
* @param apdu [out] Buffer in which the APDU contents are built.
|
||||
* @param max_apdu [in] Max length of the APDU buffer.
|
||||
*
|
||||
* @return How many bytes were encoded in the buffer, or
|
||||
* BACNET_STATUS_ABORT if the response would not fit within the buffer.
|
||||
*/
|
||||
int handler_timesync_encode_recipients(
|
||||
uint8_t * apdu,
|
||||
int max_apdu)
|
||||
{
|
||||
return timesync_encode_timesync_recipients(apdu, max_apdu,
|
||||
&Time_Sync_Recipients[0]);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BACNET_TIME_MASTER)
|
||||
bool handler_timesync_recipient_write(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data)
|
||||
{
|
||||
bool status = false;
|
||||
|
||||
/* fixme: handle writing of the recipient list */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BACNET_TIME_MASTER)
|
||||
static void handler_timesync_send(
|
||||
BACNET_DATE_TIME * current_date_time)
|
||||
{
|
||||
unsigned index = 0;
|
||||
bool status = false;
|
||||
|
||||
for (index = 0; index < MAX_TIME_SYNC_RECIPIENTS; index++) {
|
||||
if (Time_Sync_Recipients[index].tag == 1) {
|
||||
if (status) {
|
||||
Send_TimeSync_Remote(
|
||||
&Time_Sync_Recipients[index].type.address,
|
||||
¤t_date_time->date,
|
||||
¤t_date_time->time);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BACNET_TIME_MASTER)
|
||||
static void handler_timesync_update(
|
||||
uint32_t device_interval,
|
||||
BACNET_DATE_TIME * current_date_time)
|
||||
{
|
||||
uint32_t current_minutes = 0;
|
||||
uint32_t next_minutes = 0;
|
||||
uint32_t delta_minutes = 0;
|
||||
uint32_t offset_minutes = 0;
|
||||
uint32_t interval = 0;
|
||||
uint32_t interval_offset = 0;
|
||||
|
||||
datetime_copy(&Next_Sync_Time, current_date_time);
|
||||
if (Device_Align_Intervals()) {
|
||||
interval_offset = Device_Interval_Offset();
|
||||
/* If periodic time synchronization is enabled and
|
||||
the time synchronization interval is a factor of
|
||||
(divides without remainder) an hour or day, then
|
||||
the beginning of the period specified for time
|
||||
synchronization shall be aligned to the hour or
|
||||
day, respectively. */
|
||||
if ((60 % device_interval) == 0) {
|
||||
/* factor of an hour alignment */
|
||||
/* Interval_Minutes = 1 2 3 4 5 6 10 12 15 20 30 60 */
|
||||
/* determine next interval */
|
||||
current_minutes = Next_Sync_Time.time.min;
|
||||
interval = current_minutes/device_interval;
|
||||
interval++;
|
||||
next_minutes = interval * device_interval;
|
||||
offset_minutes = interval_offset % device_interval;
|
||||
next_minutes += offset_minutes;
|
||||
delta_minutes = next_minutes - current_minutes;
|
||||
datetime_add_minutes(&Next_Sync_Time, delta_minutes);
|
||||
Next_Sync_Time.time.sec = 0;
|
||||
Next_Sync_Time.time.hundredths = 0;
|
||||
} else if ((1440 % device_interval) == 0) {
|
||||
/* factor of a day alignment */
|
||||
/* Interval_Minutes = 1 2 3 4 5 6 8 9 10 12 15 16
|
||||
18 20 24 30 32 36 40 45 48 60 72 80 90 96 120
|
||||
144 160 180 240 288 360 480 720 1440 */
|
||||
current_minutes =
|
||||
datetime_minutes_since_midnight(&Next_Sync_Time.time);
|
||||
interval = current_minutes/device_interval;
|
||||
interval++;
|
||||
next_minutes = interval * device_interval;
|
||||
offset_minutes = interval_offset % device_interval;
|
||||
next_minutes += offset_minutes;
|
||||
delta_minutes = next_minutes - current_minutes;
|
||||
datetime_add_minutes(&Next_Sync_Time, delta_minutes);
|
||||
Next_Sync_Time.time.sec = 0;
|
||||
Next_Sync_Time.time.hundredths = 0;
|
||||
}
|
||||
} else {
|
||||
datetime_add_minutes(&Next_Sync_Time, device_interval);
|
||||
Next_Sync_Time.time.sec = 0;
|
||||
Next_Sync_Time.time.hundredths = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BACNET_TIME_MASTER)
|
||||
bool handler_timesync_recipient_address_set(
|
||||
unsigned index,
|
||||
BACNET_ADDRESS *address)
|
||||
{
|
||||
bool status = false;
|
||||
|
||||
if (address && (index < MAX_TIME_SYNC_RECIPIENTS)) {
|
||||
Time_Sync_Recipients[index].tag = 1;
|
||||
bacnet_address_copy(
|
||||
&Time_Sync_Recipients[index].type.address,
|
||||
address);
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BACNET_TIME_MASTER)
|
||||
void handler_timesync_task(
|
||||
BACNET_DATE_TIME * current_date_time)
|
||||
{
|
||||
int compare = 0;
|
||||
uint32_t device_interval = 0;
|
||||
|
||||
device_interval = Device_Time_Sync_Interval();
|
||||
if (device_interval) {
|
||||
compare = datetime_compare(current_date_time, &Next_Sync_Time);
|
||||
/* if the date/times are the same, return is 0
|
||||
if date1 is before date2, returns negative
|
||||
if date1 is after date2, returns positive */
|
||||
if (compare >= 0) {
|
||||
handler_timesync_update(device_interval, current_date_time);
|
||||
handler_timesync_send(current_date_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BACNET_TIME_MASTER)
|
||||
void handler_timesync_init(void)
|
||||
{
|
||||
unsigned i = 0;
|
||||
|
||||
/* connect linked list */
|
||||
for (; i < (MAX_TIME_SYNC_RECIPIENTS-1); i++) {
|
||||
Time_Sync_Recipients[i].next = &Time_Sync_Recipients[i+1];
|
||||
Time_Sync_Recipients[i+1].next = NULL;
|
||||
}
|
||||
for (i = 0; i < MAX_TIME_SYNC_RECIPIENTS; i++) {
|
||||
Time_Sync_Recipients[i].tag = 0xFF;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,113 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
/* special for this module */
|
||||
#include "cov.h"
|
||||
#include "bactext.h"
|
||||
#include "handlers.h"
|
||||
|
||||
#ifndef MAX_COV_PROPERTIES
|
||||
#define MAX_COV_PROPERTIES 2
|
||||
#endif
|
||||
|
||||
/** @file h_ucov.c Handles Unconfirmed COV Notifications. */
|
||||
|
||||
/* */
|
||||
/** Handler for an Unconfirmed COV Notification.
|
||||
* @ingroup DSCOV
|
||||
* Decodes the received list of Properties to update,
|
||||
* and print them out with the subscription information.
|
||||
* @note Nothing is specified in BACnet about what to do with the
|
||||
* information received from Unconfirmed COV Notifications.
|
||||
*
|
||||
* @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 (unused)
|
||||
*/
|
||||
void handler_ucov_notification(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src)
|
||||
{
|
||||
BACNET_COV_DATA cov_data;
|
||||
BACNET_PROPERTY_VALUE property_value[MAX_COV_PROPERTIES];
|
||||
BACNET_PROPERTY_VALUE *pProperty_value = NULL;
|
||||
int len = 0;
|
||||
|
||||
/* src not needed for this application */
|
||||
src = src;
|
||||
/* create linked list to store data if more
|
||||
than one property value is expected */
|
||||
bacapp_property_value_list_init(&property_value[0], MAX_COV_PROPERTIES);
|
||||
cov_data.listOfValues = &property_value[0];
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "UCOV: Received Notification!\n");
|
||||
#endif
|
||||
/* decode the service request only */
|
||||
len =
|
||||
cov_notify_decode_service_request(service_request, service_len,
|
||||
&cov_data);
|
||||
#if PRINT_ENABLED
|
||||
if (len > 0) {
|
||||
fprintf(stderr, "UCOV: PID=%u ", cov_data.subscriberProcessIdentifier);
|
||||
fprintf(stderr, "instance=%u ", cov_data.initiatingDeviceIdentifier);
|
||||
fprintf(stderr, "%s %u ",
|
||||
bactext_object_type_name(cov_data.monitoredObjectIdentifier.type),
|
||||
cov_data.monitoredObjectIdentifier.instance);
|
||||
fprintf(stderr, "time remaining=%u seconds ", cov_data.timeRemaining);
|
||||
fprintf(stderr, "\n");
|
||||
pProperty_value = &property_value[0];
|
||||
while (pProperty_value) {
|
||||
fprintf(stderr, "UCOV: ");
|
||||
if (pProperty_value->propertyIdentifier < 512) {
|
||||
fprintf(stderr, "%s ",
|
||||
bactext_property_name
|
||||
(pProperty_value->propertyIdentifier));
|
||||
} else {
|
||||
fprintf(stderr, "proprietary %u ",
|
||||
pProperty_value->propertyIdentifier);
|
||||
}
|
||||
if (pProperty_value->propertyArrayIndex != BACNET_ARRAY_ALL) {
|
||||
fprintf(stderr, "%u ", pProperty_value->propertyArrayIndex);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
pProperty_value = pProperty_value->next;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "UCOV: Unable to decode service request!\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2009 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "ptransfer.h"
|
||||
#include "handlers.h"
|
||||
|
||||
/** @file h_upt.c Handles Unconfirmed Private Transfer requests. */
|
||||
|
||||
void private_transfer_print_data(
|
||||
BACNET_PRIVATE_TRANSFER_DATA *private_data)
|
||||
{
|
||||
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 (private_data) {
|
||||
#if PRINT_ENABLED
|
||||
printf("PrivateTransfer:vendorID=%u\r\n",
|
||||
(unsigned) private_data->vendorID);
|
||||
printf("PrivateTransfer:serviceNumber=%lu\r\n",
|
||||
(unsigned long) private_data->serviceNumber);
|
||||
#endif
|
||||
application_data = private_data->serviceParameters;
|
||||
application_data_len = private_data->serviceParametersLen;
|
||||
for (;;) {
|
||||
len =
|
||||
bacapp_decode_application_data(application_data,
|
||||
(uint8_t) application_data_len, &value);
|
||||
if (first_value && (len < application_data_len)) {
|
||||
first_value = false;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stdout, "{");
|
||||
#endif
|
||||
print_brace = true;
|
||||
}
|
||||
/* private transfer doesn't provide any clues */
|
||||
object_value.object_type = MAX_BACNET_OBJECT_TYPE;
|
||||
object_value.object_instance = BACNET_MAX_INSTANCE;
|
||||
object_value.object_property = MAX_BACNET_PROPERTY_ID;
|
||||
object_value.array_index = BACNET_ARRAY_ALL;
|
||||
object_value.value = &value;
|
||||
bacapp_print_value(stdout, &object_value);
|
||||
if (len > 0) {
|
||||
if (len < application_data_len) {
|
||||
application_data += len;
|
||||
application_data_len -= len;
|
||||
/* there's more! */
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stdout, ",");
|
||||
#endif
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#if PRINT_ENABLED
|
||||
if (print_brace) {
|
||||
fprintf(stdout, "}");
|
||||
}
|
||||
fprintf(stdout, "\r\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void handler_unconfirmed_private_transfer(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src)
|
||||
{
|
||||
BACNET_PRIVATE_TRANSFER_DATA private_data;
|
||||
int len = 0;
|
||||
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Received Unconfirmed Private Transfer Request!\n");
|
||||
#endif
|
||||
len =
|
||||
ptransfer_decode_service_request(service_request, service_len,
|
||||
&private_data);
|
||||
if (len >= 0) {
|
||||
private_transfer_print_data(&private_data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include <errno.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "whohas.h"
|
||||
#include "device.h"
|
||||
#include "client.h"
|
||||
#include "handlers.h"
|
||||
|
||||
/** @file h_whohas.c Handles Who-Has requests. */
|
||||
|
||||
/** Local function which responds with either the requested object name
|
||||
* or object ID, if the Device has a match.
|
||||
* @param data [in] The decoded who-has payload from the request.
|
||||
*/
|
||||
static void match_name_or_object(
|
||||
BACNET_WHO_HAS_DATA * data)
|
||||
{
|
||||
int object_type = 0;
|
||||
uint32_t object_instance = 0;
|
||||
bool found = false;
|
||||
BACNET_CHARACTER_STRING object_name;
|
||||
|
||||
/* do we have such an object? If so, send an I-Have.
|
||||
note: we should have only 1 of such an object */
|
||||
if (data->is_object_name) {
|
||||
/* valid name in my device? */
|
||||
found =
|
||||
Device_Valid_Object_Name(&data->object.name, &object_type,
|
||||
&object_instance);
|
||||
if (found) {
|
||||
Send_I_Have(Device_Object_Instance_Number(),
|
||||
(BACNET_OBJECT_TYPE) object_type, object_instance,
|
||||
&data->object.name);
|
||||
}
|
||||
} else {
|
||||
/* valid object_name copy in my device? */
|
||||
found =
|
||||
Device_Object_Name_Copy((BACNET_OBJECT_TYPE) data->
|
||||
object.identifier.type, data->object.identifier.instance,
|
||||
&object_name);
|
||||
if (found) {
|
||||
Send_I_Have(Device_Object_Instance_Number(),
|
||||
(BACNET_OBJECT_TYPE) data->object.identifier.type,
|
||||
data->object.identifier.instance, &object_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Handler for Who-Has requests, with broadcast I-Have response.
|
||||
* Will respond if the device Object ID matches, and we have
|
||||
* the Object or Object Name requested.
|
||||
*
|
||||
* @ingroup DMDOB
|
||||
* @param service_request [in] The received message to be handled.
|
||||
* @param service_len [in] Length of the service_request message.
|
||||
* @param src [in] The BACNET_ADDRESS of the message's source.
|
||||
*/
|
||||
void handler_who_has(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src)
|
||||
{
|
||||
int len = 0;
|
||||
BACNET_WHO_HAS_DATA data;
|
||||
bool directed_to_me = false;
|
||||
|
||||
(void) src;
|
||||
len = whohas_decode_service_request(service_request, service_len, &data);
|
||||
if (len > 0) {
|
||||
if ((data.low_limit == -1) || (data.high_limit == -1))
|
||||
directed_to_me = true;
|
||||
else if ((Device_Object_Instance_Number() >= (uint32_t) data.low_limit)
|
||||
&& (Device_Object_Instance_Number() <= (uint32_t) data.high_limit))
|
||||
directed_to_me = true;
|
||||
if (directed_to_me) {
|
||||
match_name_or_object(&data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef BAC_ROUTING /* was for BAC_ROUTING - delete in 2/2012 if still unused */
|
||||
/* EKH: I restored this to BAC_ROUTING (from DEPRECATED) because I found that the server demo with the built-in
|
||||
virtual Router did not insert the SADRs of the virtual devices on the virtual network without it */
|
||||
|
||||
/** Handler for Who-Has requests in the virtual routing setup,
|
||||
* with broadcast I-Have response.
|
||||
* Will respond if the device Object ID matches, and we have
|
||||
* the Object or Object Name requested.
|
||||
*
|
||||
* @ingroup DMDOB
|
||||
* @param service_request [in] The received message to be handled.
|
||||
* @param service_len [in] Length of the service_request message.
|
||||
* @param src [in] The BACNET_ADDRESS of the message's source (ignored).
|
||||
*/
|
||||
void handler_who_has_for_routing(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src)
|
||||
{
|
||||
int len = 0;
|
||||
BACNET_WHO_HAS_DATA data;
|
||||
int32_t dev_instance;
|
||||
int cursor = 0; /* Starting hint */
|
||||
int my_list[2] = { 0, -1 }; /* Not really used, so dummy values */
|
||||
BACNET_ADDRESS bcast_net;
|
||||
|
||||
(void) src;
|
||||
len = whohas_decode_service_request(service_request, service_len, &data);
|
||||
if (len > 0) {
|
||||
/* Go through all devices, starting with the root gateway Device */
|
||||
memset(&bcast_net, 0, sizeof(BACNET_ADDRESS));
|
||||
bcast_net.net = BACNET_BROADCAST_NETWORK; /* That's all we have to set */
|
||||
while (Routed_Device_GetNext(&bcast_net, my_list, &cursor)) {
|
||||
dev_instance = Device_Object_Instance_Number();
|
||||
if ((data.low_limit == -1) || (data.high_limit == -1) ||
|
||||
((dev_instance >= data.low_limit) &&
|
||||
(dev_instance <= data.high_limit)))
|
||||
match_name_or_object(&data);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* BAC_ROUTING */
|
||||
@@ -0,0 +1,203 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "whois.h"
|
||||
#include "iam.h"
|
||||
#include "device.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "txbuf.h"
|
||||
#include "handlers.h"
|
||||
|
||||
/** @file h_whois.c Handles Who-Is requests. */
|
||||
|
||||
/** Handler for Who-Is requests, with broadcast I-Am response.
|
||||
* @ingroup DMDDB
|
||||
* @param service_request [in] The received message to be handled.
|
||||
* @param service_len [in] Length of the service_request message.
|
||||
* @param src [in] The BACNET_ADDRESS of the message's source (ignored).
|
||||
*/
|
||||
void handler_who_is(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src)
|
||||
{
|
||||
int len = 0;
|
||||
int32_t low_limit = 0;
|
||||
int32_t high_limit = 0;
|
||||
|
||||
(void) src;
|
||||
len =
|
||||
whois_decode_service_request(service_request, service_len, &low_limit,
|
||||
&high_limit);
|
||||
if (len == 0) {
|
||||
Send_I_Am(&Handler_Transmit_Buffer[0]);
|
||||
} else if (len != BACNET_STATUS_ERROR) {
|
||||
/* is my device id within the limits? */
|
||||
if ((Device_Object_Instance_Number() >= (uint32_t) low_limit) &&
|
||||
(Device_Object_Instance_Number() <= (uint32_t) high_limit)) {
|
||||
Send_I_Am(&Handler_Transmit_Buffer[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** Handler for Who-Is requests, with Unicast I-Am response (per Addendum 135-2004q).
|
||||
* @ingroup DMDDB
|
||||
* @param service_request [in] The received message to be handled.
|
||||
* @param service_len [in] Length of the service_request message.
|
||||
* @param src [in] The BACNET_ADDRESS of the message's source that the
|
||||
* response will be sent back to.
|
||||
*/
|
||||
void handler_who_is_unicast(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src)
|
||||
{
|
||||
int len = 0;
|
||||
int32_t low_limit = 0;
|
||||
int32_t high_limit = 0;
|
||||
|
||||
len =
|
||||
whois_decode_service_request(service_request, service_len, &low_limit,
|
||||
&high_limit);
|
||||
/* If no limits, then always respond */
|
||||
if (len == 0) {
|
||||
Send_I_Am_Unicast(&Handler_Transmit_Buffer[0], src);
|
||||
} else if (len != BACNET_STATUS_ERROR) {
|
||||
/* is my device id within the limits? */
|
||||
if ((Device_Object_Instance_Number() >= (uint32_t) low_limit) &&
|
||||
(Device_Object_Instance_Number() <= (uint32_t) high_limit)) {
|
||||
Send_I_Am_Unicast(&Handler_Transmit_Buffer[0], src);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#ifdef BAC_ROUTING /* was for BAC_ROUTING - delete in 2/2012 if still unused */
|
||||
/* EKH: I restored this to BAC_ROUTING (from DEPRECATED) because I found that the server demo with the built-in
|
||||
virtual Router did not insert the SADRs of the virtual devices on the virtual network without it */
|
||||
|
||||
|
||||
/** Local function to check Who-Is requests against our Device IDs.
|
||||
* Will check the gateway (root Device) and all virtual routed
|
||||
* Devices against the range and respond for each that matches.
|
||||
*
|
||||
* @param service_request [in] The received message to be handled.
|
||||
* @param service_len [in] Length of the service_request message.
|
||||
* @param src [in] The BACNET_ADDRESS of the message's source.
|
||||
* @param is_unicast [in] True if should send unicast response(s)
|
||||
* back to the src, else False if should broadcast response(s).
|
||||
*/
|
||||
static void check_who_is_for_routing(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
bool is_unicast)
|
||||
{
|
||||
int len = 0;
|
||||
int32_t low_limit = 0;
|
||||
int32_t high_limit = 0;
|
||||
int32_t dev_instance;
|
||||
int cursor = 0; /* Starting hint */
|
||||
int my_list[2] = { 0, -1 }; /* Not really used, so dummy values */
|
||||
BACNET_ADDRESS bcast_net;
|
||||
|
||||
len =
|
||||
whois_decode_service_request(service_request, service_len, &low_limit,
|
||||
&high_limit);
|
||||
if (len == BACNET_STATUS_ERROR) {
|
||||
/* Invalid; just leave */
|
||||
return;
|
||||
}
|
||||
/* Go through all devices, starting with the root gateway Device */
|
||||
memset(&bcast_net, 0, sizeof(BACNET_ADDRESS));
|
||||
bcast_net.net = BACNET_BROADCAST_NETWORK; /* That's all we have to set */
|
||||
|
||||
while (Routed_Device_GetNext(&bcast_net, my_list, &cursor)) {
|
||||
dev_instance = Device_Object_Instance_Number();
|
||||
/* If len == 0, no limits and always respond */
|
||||
if ((len == 0) || ((dev_instance >= low_limit) &&
|
||||
(dev_instance <= high_limit))) {
|
||||
if (is_unicast)
|
||||
Send_I_Am_Unicast(&Handler_Transmit_Buffer[0], src);
|
||||
else
|
||||
Send_I_Am(&Handler_Transmit_Buffer[0]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** Handler for Who-Is requests in the virtual routing setup,
|
||||
* with broadcast I-Am response(s).
|
||||
* @ingroup DMDDB
|
||||
* Will check the gateway (root Device) and all virtual routed
|
||||
* Devices against the range and respond for each that matches.
|
||||
*
|
||||
* @ingroup DMDDB
|
||||
* @param service_request [in] The received message to be handled.
|
||||
* @param service_len [in] Length of the service_request message.
|
||||
* @param src [in] The BACNET_ADDRESS of the message's source (ignored).
|
||||
*/
|
||||
void handler_who_is_bcast_for_routing(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src)
|
||||
{
|
||||
check_who_is_for_routing(service_request, service_len, src, false);
|
||||
}
|
||||
|
||||
|
||||
/** Handler for Who-Is requests in the virtual routing setup,
|
||||
* with unicast I-Am response(s) returned to the src.
|
||||
* Will check the gateway (root Device) and all virtual routed
|
||||
* Devices against the range and respond for each that matches.
|
||||
*
|
||||
* @ingroup DMDDB
|
||||
* @param service_request [in] The received message to be handled.
|
||||
* @param service_len [in] Length of the service_request message.
|
||||
* @param src [in] The BACNET_ADDRESS of the message's source that the
|
||||
* response will be sent back to.
|
||||
*/
|
||||
void handler_who_is_unicast_for_routing(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src)
|
||||
{
|
||||
check_who_is_for_routing(service_request, service_len, src, true);
|
||||
}
|
||||
#endif /* BAC_ROUTING */
|
||||
@@ -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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacerror.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "wp.h"
|
||||
/* device object has the handling for all objects */
|
||||
#include "device.h"
|
||||
#include "handlers.h"
|
||||
|
||||
/** @file h_wp.c Handles Write Property requests. */
|
||||
|
||||
|
||||
/** Handler for a WriteProperty Service request.
|
||||
* @ingroup DSWP
|
||||
* This handler will be invoked by apdu_handler() if it has been enabled
|
||||
* by a call to apdu_set_confirmed_handler().
|
||||
* This handler builds a response packet, which is
|
||||
* - an Abort if
|
||||
* - the message is segmented
|
||||
* - if decoding fails
|
||||
* - an ACK if Device_Write_Property() succeeds
|
||||
* - an Error if Device_Write_Property() fails
|
||||
* or there isn't enough room in the APDU to fit the data.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
void handler_write_property(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
BACNET_WRITE_PROPERTY_DATA wp_data;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
int bytes_sent = 0;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
/* encode the NPDU portion of the packet */
|
||||
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 PRINT_ENABLED
|
||||
fprintf(stderr, "WP: Received Request!\n");
|
||||
#endif
|
||||
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, "WP: Segmented message. Sending Abort!\n");
|
||||
#endif
|
||||
goto WP_ABORT;
|
||||
} /* decode the service request only */
|
||||
len = wp_decode_service_request(service_request, service_len, &wp_data);
|
||||
#if PRINT_ENABLED
|
||||
if (len > 0)
|
||||
fprintf(stderr,
|
||||
"WP: type=%lu instance=%lu property=%lu priority=%lu index=%ld\n",
|
||||
(unsigned long) wp_data.object_type,
|
||||
(unsigned long) wp_data.object_instance,
|
||||
(unsigned long) wp_data.object_property,
|
||||
(unsigned long) wp_data.priority, (long) wp_data.array_index);
|
||||
else
|
||||
fprintf(stderr, "WP: Unable to decode Request!\n");
|
||||
#endif
|
||||
/* bad decoding or something we didn't understand - 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, "WP: Bad Encoding. Sending Abort!\n");
|
||||
#endif
|
||||
goto WP_ABORT;
|
||||
}
|
||||
if (Device_Write_Property(&wp_data)) {
|
||||
len =
|
||||
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "WP: Sending Simple Ack!\n");
|
||||
#endif
|
||||
} else {
|
||||
len =
|
||||
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY,
|
||||
wp_data.error_class, wp_data.error_code);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "WP: Sending Error!\n");
|
||||
#endif
|
||||
}
|
||||
WP_ABORT:
|
||||
pdu_len += len;
|
||||
bytes_sent =
|
||||
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
|
||||
pdu_len);
|
||||
if (bytes_sent <= 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "WP: Failed to send PDU (%s)!\n", strerror(errno));
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/** Perform basic validation of Write Property argument based on
|
||||
* the assumption that it is a string. Check for correct data type,
|
||||
* correct encoding (fixed here as ANSI X34),correct length, and
|
||||
* finally if it is allowed to be empty.
|
||||
*/
|
||||
|
||||
bool WPValidateString(
|
||||
BACNET_APPLICATION_DATA_VALUE * pValue,
|
||||
int iMaxLen,
|
||||
bool bEmptyAllowed,
|
||||
BACNET_ERROR_CLASS * pErrorClass,
|
||||
BACNET_ERROR_CODE * pErrorCode)
|
||||
{
|
||||
bool bResult;
|
||||
|
||||
/* Save on a bit of code duplication by pre selecting the most
|
||||
* common outcomes from the tests (not necessarily the most likely
|
||||
* outcome of the tests).
|
||||
*/
|
||||
bResult = false;
|
||||
*pErrorClass = ERROR_CLASS_PROPERTY;
|
||||
|
||||
if (pValue->tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) {
|
||||
if (characterstring_encoding(&pValue->type.Character_String) ==
|
||||
CHARACTER_ANSI_X34) {
|
||||
if ((bEmptyAllowed == false) &&
|
||||
(characterstring_length(&pValue->type.Character_String) ==
|
||||
0)) {
|
||||
*pErrorCode = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
} else if ((bEmptyAllowed == false) &&
|
||||
(!characterstring_printable(&pValue->type.Character_String))) {
|
||||
/* assumption: non-empty also means must be "printable" */
|
||||
*pErrorCode = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
} else if (characterstring_length(&pValue->type.Character_String) >
|
||||
(uint16_t) iMaxLen) {
|
||||
*pErrorClass = ERROR_CLASS_RESOURCES;
|
||||
*pErrorCode = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY;
|
||||
} else
|
||||
bResult = true; /* It's all good! */
|
||||
} else
|
||||
*pErrorCode = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED;
|
||||
} else
|
||||
*pErrorCode = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
|
||||
return (bResult);
|
||||
}
|
||||
|
||||
/** Perform simple validation of type of Write Property argument based
|
||||
* the expected type vs the actual. Set up error response if the
|
||||
* validation fails. Cuts out reams of repeated code in the object code.
|
||||
*/
|
||||
|
||||
bool WPValidateArgType(
|
||||
BACNET_APPLICATION_DATA_VALUE * pValue,
|
||||
uint8_t ucExpectedTag,
|
||||
BACNET_ERROR_CLASS * pErrorClass,
|
||||
BACNET_ERROR_CODE * pErrorCode)
|
||||
{
|
||||
bool bResult;
|
||||
|
||||
/*
|
||||
* start out assuming success and only set up error
|
||||
* response if validation fails.
|
||||
*/
|
||||
bResult = true;
|
||||
if (pValue->tag != ucExpectedTag) {
|
||||
bResult = false;
|
||||
*pErrorClass = ERROR_CLASS_PROPERTY;
|
||||
*pErrorCode = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
|
||||
return (bResult);
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
|
||||
*
|
||||
* 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacerror.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "abort.h"
|
||||
#include "wp.h"
|
||||
#include "reject.h"
|
||||
#include "wpm.h"
|
||||
/* device object has the handling for all objects */
|
||||
#include "device.h"
|
||||
#include "handlers.h"
|
||||
|
||||
/** @file h_wpm.c Handles Write Property Multiple requests. */
|
||||
|
||||
|
||||
/** Handler for a WriteProperty Service request.
|
||||
* @ingroup DSWP
|
||||
* This handler will be invoked by apdu_handler() if it has been enabled
|
||||
* by a call to apdu_set_confirmed_handler().
|
||||
* This handler builds a response packet, which is
|
||||
* - an Abort if
|
||||
* - the message is segmented
|
||||
* - if decoding fails
|
||||
* - an ACK if Device_Write_Property_Multiple() succeeds
|
||||
* - an Error if Device_Write_PropertyMultiple() encounters an error
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
void handler_write_property_multiple(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
int npdu_len = 0;
|
||||
int pdu_len = 0;
|
||||
int decode_len = 0;
|
||||
bool error = false;
|
||||
BACNET_WRITE_PROPERTY_DATA wp_data;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
int bytes_sent = 0;
|
||||
|
||||
if (service_data->segmented_message) {
|
||||
wp_data.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
len = BACNET_STATUS_ABORT;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "WPM: Segmented message. Sending Abort!\n");
|
||||
#endif
|
||||
goto WPM_ABORT;
|
||||
}
|
||||
|
||||
/* decode service request */
|
||||
decode_len = 0;
|
||||
do {
|
||||
/* decode Object Identifier */
|
||||
len =
|
||||
wpm_decode_object_id(&service_request[decode_len],
|
||||
service_len - decode_len, &wp_data);
|
||||
if (len > 0) {
|
||||
uint8_t tag_number = 0;
|
||||
|
||||
decode_len += len;
|
||||
/* Opening tag 1 - List of Properties */
|
||||
if (decode_is_opening_tag_number(&service_request[decode_len++],
|
||||
1)) {
|
||||
do {
|
||||
/* decode a 'Property Identifier'; (3) an optional 'Property Array Index' */
|
||||
/* (4) a 'Property Value'; and (5) an optional 'Priority'. */
|
||||
len =
|
||||
wpm_decode_object_property(&service_request
|
||||
[decode_len], service_len - decode_len, &wp_data);
|
||||
if (len > 0) {
|
||||
decode_len += len;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"WPM: type=%lu instance=%lu property=%lu priority=%lu index=%ld\n",
|
||||
(unsigned long) wp_data.object_type,
|
||||
(unsigned long) wp_data.object_instance,
|
||||
(unsigned long) wp_data.object_property,
|
||||
(unsigned long) wp_data.priority,
|
||||
(long) wp_data.array_index);
|
||||
#endif
|
||||
if (Device_Write_Property(&wp_data) == false) {
|
||||
error = true;
|
||||
len = BACNET_STATUS_ERROR;
|
||||
goto WPM_ABORT;
|
||||
}
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "WPM: Bad Encoding!\n");
|
||||
#endif
|
||||
error = true;
|
||||
goto WPM_ABORT;
|
||||
}
|
||||
|
||||
/* Closing tag 1 - List of Properties */
|
||||
if (decode_is_closing_tag_number(&service_request
|
||||
[decode_len], 1)) {
|
||||
tag_number = 1;
|
||||
decode_len++;
|
||||
} else
|
||||
tag_number = 0; /* it was not tag 1, decode next Property Identifier ... */
|
||||
|
||||
}
|
||||
while (tag_number != 1); /* end decoding List of Properties for "that" object */
|
||||
|
||||
if (error) {
|
||||
goto WPM_ABORT;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "WPM: Bad Encoding!\n");
|
||||
#endif
|
||||
error = true;
|
||||
goto WPM_ABORT;
|
||||
}
|
||||
} while (decode_len < service_len);
|
||||
|
||||
WPM_ABORT:
|
||||
/* encode the NPDU portion of the packet */
|
||||
datalink_get_my_address(&my_address);
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
npdu_len =
|
||||
npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address,
|
||||
&npdu_data);
|
||||
apdu_len = 0;
|
||||
/* handle any errors */
|
||||
if (error) {
|
||||
if (len == BACNET_STATUS_ABORT) {
|
||||
apdu_len =
|
||||
abort_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id,
|
||||
abort_convert_error_code(wp_data.error_code), true);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "WPM: Sending Abort!\n");
|
||||
#endif
|
||||
} else if (len == BACNET_STATUS_ERROR) {
|
||||
apdu_len =
|
||||
wpm_error_ack_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id, &wp_data);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "WPM: Sending Error!\n");
|
||||
#endif
|
||||
} else if (len == BACNET_STATUS_REJECT) {
|
||||
apdu_len =
|
||||
reject_encode_apdu(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id,
|
||||
reject_convert_error_code(wp_data.error_code));
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "WPM: Sending Reject!\n");
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
apdu_len =
|
||||
wpm_ack_encode_apdu_init(&Handler_Transmit_Buffer[npdu_len],
|
||||
service_data->invoke_id);
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "WPM: Sending Ack!\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
pdu_len = npdu_len + apdu_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));
|
||||
}
|
||||
#else
|
||||
bytes_sent = bytes_sent;
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "apdu.h"
|
||||
#include "npdu.h"
|
||||
#include "reject.h"
|
||||
#include "handlers.h"
|
||||
#include "device.h"
|
||||
|
||||
/** @file noserv.c Handles an unrecognized/unsupported service. */
|
||||
|
||||
/** Handler to be invoked when a Service request is received for which no
|
||||
* handler has been defined.
|
||||
* @ingroup MISCHNDLR
|
||||
* This handler builds a Reject response packet, and sends it.
|
||||
*
|
||||
* @param service_request [in] The contents of the service request (unused).
|
||||
* @param service_len [in] The length of the service_request (unused).
|
||||
* @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.
|
||||
*/
|
||||
void handler_unrecognized_service(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA * service_data)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
(void) service_request;
|
||||
(void) service_len;
|
||||
|
||||
/* encode the NPDU portion of the packet */
|
||||
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);
|
||||
/* encode the APDU portion of the packet */
|
||||
len =
|
||||
reject_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, REJECT_REASON_UNRECOGNIZED_SERVICE);
|
||||
pdu_len += len;
|
||||
/* send the data */
|
||||
bytes_sent =
|
||||
datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0],
|
||||
pdu_len);
|
||||
if (bytes_sent > 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Sent Reject!\n");
|
||||
#endif
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Failed to Send Reject (%s)!\n", strerror(errno));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2008 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include "keylist.h"
|
||||
#include "objects.h"
|
||||
|
||||
/** @file objects.c Manage Device Objects. */
|
||||
|
||||
/* list of devices */
|
||||
static OS_Keylist Device_List = NULL;
|
||||
|
||||
void objects_init(
|
||||
void)
|
||||
{
|
||||
if (!Device_List)
|
||||
Device_List = Keylist_Create();
|
||||
}
|
||||
|
||||
int objects_device_count(
|
||||
void)
|
||||
{
|
||||
objects_init();
|
||||
return Keylist_Count(Device_List);
|
||||
}
|
||||
|
||||
OBJECT_DEVICE_T *objects_device_data(
|
||||
int index)
|
||||
{
|
||||
objects_init();
|
||||
return Keylist_Data_Index(Device_List, index);
|
||||
}
|
||||
|
||||
OBJECT_DEVICE_T *objects_device_by_instance(
|
||||
uint32_t device_instance)
|
||||
{
|
||||
objects_init();
|
||||
return Keylist_Data(Device_List, device_instance);
|
||||
}
|
||||
|
||||
OBJECT_DEVICE_T *objects_device_new(
|
||||
uint32_t device_instance)
|
||||
{
|
||||
OBJECT_DEVICE_T *pDevice = NULL;
|
||||
KEY key = device_instance;
|
||||
|
||||
if (Device_List) {
|
||||
/* does this device already exist? */
|
||||
pDevice = Keylist_Data(Device_List, key);
|
||||
if (pDevice) {
|
||||
memset(pDevice, 0, sizeof(OBJECT_DEVICE_T));
|
||||
} else {
|
||||
pDevice = calloc(1, sizeof(OBJECT_DEVICE_T));
|
||||
if (pDevice) {
|
||||
pDevice->Object_Identifier.type = OBJECT_DEVICE;
|
||||
pDevice->Object_Identifier.instance = device_instance;
|
||||
pDevice->Object_Type = OBJECT_DEVICE;
|
||||
pDevice->Object_List = Keylist_Create();
|
||||
Keylist_Data_Add(Device_List, key, pDevice);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"Objects: Unable to allocate device %lu buffer\n",
|
||||
(unsigned long) device_instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pDevice;
|
||||
}
|
||||
|
||||
OBJECT_DEVICE_T *objects_device_delete(
|
||||
int index)
|
||||
{
|
||||
OBJECT_DEVICE_T *pDevice = NULL;
|
||||
BACNET_OBJECT_ID *pObject;
|
||||
|
||||
if (Device_List) {
|
||||
pDevice = Keylist_Data_Delete_By_Index(Device_List, index);
|
||||
if (pDevice) {
|
||||
fprintf(stderr, "Objects: removing device %lu",
|
||||
(unsigned long) pDevice->Object_Identifier.instance);
|
||||
if (pDevice->Object_List) {
|
||||
do {
|
||||
pObject =
|
||||
Keylist_Data_Delete_By_Index(pDevice->Object_List, 0);
|
||||
/* free any dynamic memory used */
|
||||
if (pObject) {
|
||||
free(pObject);
|
||||
}
|
||||
} while (pObject);
|
||||
Keylist_Delete(pDevice->Object_List);
|
||||
}
|
||||
free(pDevice);
|
||||
}
|
||||
}
|
||||
return pDevice;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ctest.h"
|
||||
|
||||
/* test the object creation and deletion */
|
||||
void testBACnetObjectsCompare(
|
||||
Test * pTest,
|
||||
OBJECT_DEVICE_T * pDevice,
|
||||
uint32_t device_id)
|
||||
{
|
||||
ct_test(pTest, pDevice != NULL);
|
||||
if (pDevice) {
|
||||
ct_test(pTest, pDevice->Object_List != NULL);
|
||||
ct_test(pTest, pDevice->Object_Identifier.instance == device_id);
|
||||
ct_test(pTest, pDevice->Object_Identifier.type == OBJECT_DEVICE);
|
||||
ct_test(pTest, pDevice->Object_Type == OBJECT_DEVICE);
|
||||
}
|
||||
}
|
||||
|
||||
void testBACnetObjects(
|
||||
Test * pTest)
|
||||
{
|
||||
uint32_t device_id = 0;
|
||||
unsigned test_point = 0;
|
||||
const unsigned max_test_points = 20;
|
||||
OBJECT_DEVICE_T *pDevice;
|
||||
|
||||
for (test_point = 0; test_point < max_test_points; test_point++) {
|
||||
device_id = test_point * (BACNET_MAX_INSTANCE / max_test_points);
|
||||
pDevice = objects_device_new(device_id);
|
||||
testBACnetObjectsCompare(pTest, pDevice, device_id);
|
||||
pDevice = objects_device_by_instance(device_id);
|
||||
testBACnetObjectsCompare(pTest, pDevice, device_id);
|
||||
}
|
||||
ct_test(pTest, max_test_points == objects_device_count());
|
||||
for (test_point = 0; test_point < max_test_points; test_point++) {
|
||||
device_id = test_point * (BACNET_MAX_INSTANCE / max_test_points);
|
||||
pDevice = objects_device_by_instance(device_id);
|
||||
testBACnetObjectsCompare(pTest, pDevice, device_id);
|
||||
}
|
||||
for (test_point = 0; test_point < max_test_points; test_point++) {
|
||||
device_id = test_point * (BACNET_MAX_INSTANCE / max_test_points);
|
||||
pDevice = objects_device_data(test_point);
|
||||
testBACnetObjectsCompare(pTest, pDevice, Keylist_Key(Device_List,
|
||||
test_point));
|
||||
}
|
||||
for (test_point = 0; test_point < max_test_points; test_point++) {
|
||||
pDevice = objects_device_delete(0);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TEST_OBJECT_LIST
|
||||
int main(
|
||||
void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Objects", NULL);
|
||||
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testBACnetObjects);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void) ct_report(pTest);
|
||||
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,108 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "abort.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "dcc.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "iam.h"
|
||||
#include "txbuf.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "client.h"
|
||||
|
||||
|
||||
/** Encodes an Abort message
|
||||
* @param buffer The buffer to build the message for sending.
|
||||
* @param dest - Destination address to send the message
|
||||
* @param src - Source address from which the message originates
|
||||
* @param npdu_data - buffer to hold NPDU data encoded
|
||||
* @param invoke_id - use to match up a reply
|
||||
* @param reason - #BACNET_ABORT_REASON enumeration
|
||||
* @param server - true or false
|
||||
*
|
||||
* @return Size of the message sent (bytes), or a negative value on error.
|
||||
*/
|
||||
int abort_encode_pdu(
|
||||
uint8_t * buffer,
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_NPDU_DATA * npdu_data,
|
||||
uint8_t invoke_id,
|
||||
BACNET_ABORT_REASON reason,
|
||||
bool server)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
|
||||
/* encode the NPDU portion of the packet */
|
||||
npdu_encode_npdu_data(npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len = npdu_encode_pdu(&buffer[0], dest, src, npdu_data);
|
||||
|
||||
/* encode the APDU portion of the packet */
|
||||
len = abort_encode_apdu(&buffer[pdu_len], invoke_id, reason, server);
|
||||
pdu_len += len;
|
||||
|
||||
return pdu_len;
|
||||
}
|
||||
|
||||
/** Sends an Abort message
|
||||
* @param buffer The buffer to build the message for sending.
|
||||
* @param dest - Destination address to send the message
|
||||
* @param invoke_id - use to match up a reply
|
||||
* @param reason - #BACNET_ABORT_REASON enumeration
|
||||
* @param server - true or false
|
||||
*
|
||||
* @return Size of the message sent (bytes), or a negative value on error.
|
||||
*/
|
||||
int Send_Abort_To_Network(
|
||||
uint8_t * buffer,
|
||||
BACNET_ADDRESS *dest,
|
||||
uint8_t invoke_id,
|
||||
BACNET_ABORT_REASON reason,
|
||||
bool server)
|
||||
{
|
||||
int pdu_len = 0;
|
||||
BACNET_ADDRESS src;
|
||||
int bytes_sent = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
|
||||
datalink_get_my_address(&src);
|
||||
pdu_len = abort_encode_pdu(buffer, dest, &src, &npdu_data,
|
||||
invoke_id, reason, server);
|
||||
bytes_sent = datalink_send_pdu(dest, &npdu_data, &buffer[0], pdu_len);
|
||||
|
||||
return bytes_sent;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2009 John Minack <minack@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 <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "dcc.h"
|
||||
#include "whois.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "alarm_ack.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_ack_alarm.c Send an Alarm Acknowledgment. */
|
||||
|
||||
/* returns the invoke ID for confirmed request, or zero on failure */
|
||||
|
||||
|
||||
uint8_t Send_Alarm_Acknowledgement(
|
||||
uint32_t device_id,
|
||||
BACNET_ALARM_ACK_DATA * data)
|
||||
{
|
||||
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;
|
||||
|
||||
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);
|
||||
len =
|
||||
alarm_ack_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id,
|
||||
data);
|
||||
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 Alarm Ack Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send Alarm Ack Request "
|
||||
"(exceeds destination maximum APDU)!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "dcc.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "arf.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_arfs.c Send part of an Atomic Read File Stream. */
|
||||
|
||||
uint8_t Send_Atomic_Read_File_Stream(
|
||||
uint32_t device_id,
|
||||
uint32_t file_instance,
|
||||
int fileStartPosition,
|
||||
unsigned requestedOctetCount)
|
||||
{
|
||||
BACNET_ADDRESS dest;
|
||||
BACNET_ADDRESS my_address;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
unsigned max_apdu = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
bool status = false;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
BACNET_ATOMIC_READ_FILE_DATA 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) {
|
||||
/* load the data for the encoding */
|
||||
data.object_type = OBJECT_FILE;
|
||||
data.object_instance = file_instance;
|
||||
data.access = FILE_STREAM_ACCESS;
|
||||
data.type.stream.fileStartPosition = fileStartPosition;
|
||||
data.type.stream.requestedOctetCount = requestedOctetCount;
|
||||
/* 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);
|
||||
len =
|
||||
arf_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id,
|
||||
&data);
|
||||
pdu_len += len;
|
||||
/* will the APDU fit the target device?
|
||||
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 AtomicReadFile Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send AtomicReadFile Request "
|
||||
"(payload exceeds destination maximum APDU)!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "dcc.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "awf.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_awfs.c Send part of an Atomic Write File Stream request. */
|
||||
|
||||
uint8_t Send_Atomic_Write_File_Stream(
|
||||
uint32_t device_id,
|
||||
uint32_t file_instance,
|
||||
int fileStartPosition,
|
||||
BACNET_OCTET_STRING * fileData)
|
||||
{
|
||||
BACNET_ADDRESS dest;
|
||||
BACNET_ADDRESS my_address;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
unsigned max_apdu = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
bool status = false;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA 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) {
|
||||
/* load the data for the encoding */
|
||||
data.object_type = OBJECT_FILE;
|
||||
data.object_instance = file_instance;
|
||||
data.access = FILE_STREAM_ACCESS;
|
||||
data.type.stream.fileStartPosition = fileStartPosition;
|
||||
status = octetstring_copy(&data.fileData[0], fileData);
|
||||
if (status) {
|
||||
/* 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 */
|
||||
len =
|
||||
awf_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id,
|
||||
&data);
|
||||
pdu_len += len;
|
||||
/* will the APDU fit the target device?
|
||||
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 AtomicWriteFile Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send AtomicWriteFile Request "
|
||||
"(payload [%d] exceeds destination maximum APDU [%u])!\n",
|
||||
pdu_len, max_apdu);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send AtomicWriteFile Request "
|
||||
"(payload [%d] exceeds octet string capacity)!\n", pdu_len);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <string.h>
|
||||
#include "event.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "tsm.h"
|
||||
#include "dcc.h"
|
||||
#include "address.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_cevent.c Send a ConfirmedEventNotification Request. */
|
||||
|
||||
/** Sends an Confirmed Alarm/Event Notification.
|
||||
* @ingroup EVNOTFCN
|
||||
*
|
||||
* @param device_id [in] ID of the destination device
|
||||
* @param data [in] The information about the Event to be sent.
|
||||
* @return invoke id of outgoing message, or 0 if communication is disabled,
|
||||
* or no tsm slot is available.
|
||||
*/
|
||||
uint8_t Send_CEvent_Notify(
|
||||
uint32_t device_id,
|
||||
BACNET_EVENT_NOTIFICATION_DATA * data)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS dest;
|
||||
BACNET_ADDRESS my_address;
|
||||
unsigned max_apdu = 0;
|
||||
bool status = false;
|
||||
uint8_t invoke_id = 0;
|
||||
|
||||
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 */
|
||||
len =
|
||||
cevent_notify_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
invoke_id, data);
|
||||
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 ConfirmedEventNotification Request (%s)!\n",
|
||||
strerror(errno));
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send ConfirmedEventNotification Request "
|
||||
"(exceeds destination maximum APDU)!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "dcc.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "cov.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_cov.c Send a Change of Value (COV) update or a Subscribe COV request. */
|
||||
|
||||
/** Encodes an Unconfirmed COV Notification.
|
||||
* @ingroup DSCOV
|
||||
*
|
||||
* @param buffer [in,out] The buffer to build the message in for sending.
|
||||
* @param buffer_len [in] Number of bytes in the buffer
|
||||
* @param dest [in] Destination address
|
||||
* @param npdu_data [in] Network Layer information
|
||||
* @param cov_data [in] The COV update information to be encoded.
|
||||
* @return Size of the message sent (bytes), or a negative value on error.
|
||||
*/
|
||||
int ucov_notify_encode_pdu(
|
||||
uint8_t * buffer,
|
||||
unsigned buffer_len,
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_NPDU_DATA * npdu_data,
|
||||
BACNET_COV_DATA * cov_data)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
BACNET_ADDRESS my_address;
|
||||
datalink_get_my_address(&my_address);
|
||||
|
||||
/* unconfirmed is a broadcast */
|
||||
datalink_get_broadcast_address(dest);
|
||||
/* encode the NPDU portion of the packet */
|
||||
npdu_encode_npdu_data(npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len = npdu_encode_pdu(&buffer[0], dest, &my_address, npdu_data);
|
||||
|
||||
/* encode the APDU portion of the packet */
|
||||
len = ucov_notify_encode_apdu(&buffer[pdu_len],
|
||||
buffer_len - pdu_len, cov_data);
|
||||
if (len) {
|
||||
pdu_len += len;
|
||||
} else {
|
||||
pdu_len = 0;
|
||||
}
|
||||
|
||||
return pdu_len;
|
||||
}
|
||||
|
||||
/** Sends an Unconfirmed COV Notification.
|
||||
* @ingroup DSCOV
|
||||
*
|
||||
* @param buffer [in,out] The buffer to build the message in for sending.
|
||||
* @param buffer_len [in] Number of bytes in the buffer
|
||||
* @param cov_data [in] The COV update information to be encoded.
|
||||
* @return Size of the message sent (bytes), or a negative value on error.
|
||||
*/
|
||||
int Send_UCOV_Notify(
|
||||
uint8_t * buffer,
|
||||
unsigned buffer_len,
|
||||
BACNET_COV_DATA * cov_data)
|
||||
{
|
||||
int pdu_len = 0;
|
||||
BACNET_ADDRESS dest;
|
||||
int bytes_sent = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
|
||||
pdu_len = ucov_notify_encode_pdu(buffer, buffer_len, &dest, &npdu_data,
|
||||
cov_data);
|
||||
bytes_sent = datalink_send_pdu(&dest, &npdu_data, &buffer[0], pdu_len);
|
||||
|
||||
return bytes_sent;
|
||||
}
|
||||
|
||||
/** Sends a COV Subscription request.
|
||||
* @ingroup DSCOV
|
||||
*
|
||||
* @param device_id [in] ID of the destination device
|
||||
* @param cov_data [in] The COV subscription information to be encoded.
|
||||
* @return invoke id of outgoing message, or 0 if communication is disabled or
|
||||
* no slot is available from the tsm for sending.
|
||||
*/
|
||||
uint8_t Send_COV_Subscribe(
|
||||
uint32_t device_id,
|
||||
BACNET_SUBSCRIBE_COV_DATA * cov_data)
|
||||
{
|
||||
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;
|
||||
|
||||
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 */
|
||||
len =
|
||||
cov_subscribe_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
sizeof(Handler_Transmit_Buffer)-pdu_len, invoke_id, cov_data);
|
||||
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 (bytes_sent <= 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Failed to Send SubscribeCOV Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send SubscribeCOV Request "
|
||||
"(exceeds destination maximum APDU)!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "dcc.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_dcc.c Send a Device Communication Control (DCC) request. */
|
||||
|
||||
/** Sends a Device Communication Control (DCC) request.
|
||||
* @ingroup DMDCC
|
||||
*
|
||||
* @param device_id [in] The index to the device address in our address cache.
|
||||
* @param timeDuration [in] If non-zero, the minutes that the remote device
|
||||
* shall ignore all APDUs except DCC and, if supported, RD APDUs.
|
||||
* @param state [in] Choice to Enable or Disable communication.
|
||||
* @param password [in] Optional password, up to 20 chars.
|
||||
* @return The invokeID of the transmitted message, or 0 on failure.
|
||||
*/
|
||||
|
||||
uint8_t Send_Device_Communication_Control_Request(
|
||||
uint32_t device_id,
|
||||
uint16_t timeDuration, /* 0=optional */
|
||||
BACNET_COMMUNICATION_ENABLE_DISABLE state,
|
||||
char *password)
|
||||
{ /* 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_CHARACTER_STRING password_string;
|
||||
BACNET_NPDU_DATA npdu_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 */
|
||||
characterstring_init_ansi(&password_string, password);
|
||||
len =
|
||||
dcc_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id,
|
||||
timeDuration, state, password ? &password_string : NULL);
|
||||
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 DeviceCommunicationControl Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send DeviceCommunicationControl Request "
|
||||
"(exceeds destination maximum APDU)!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacenum.h"
|
||||
#include "bacerror.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "dcc.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "client.h"
|
||||
|
||||
|
||||
/** Encodes an Error message
|
||||
* @param buffer The buffer to build the message for sending.
|
||||
* @param dest - Destination address to send the message
|
||||
* @param src - Source address from which the message originates
|
||||
* @param npdu_data - buffer to hold NPDU data encoded
|
||||
* @param invoke_id - use to match up a reply
|
||||
* @param reason - #BACNET_ABORT_REASON enumeration
|
||||
* @param server - true or false
|
||||
*
|
||||
* @return Size of the message sent (bytes), or a negative value on error.
|
||||
*/
|
||||
int error_encode_pdu(
|
||||
uint8_t * buffer,
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_NPDU_DATA * npdu_data,
|
||||
uint8_t invoke_id,
|
||||
BACNET_CONFIRMED_SERVICE service,
|
||||
BACNET_ERROR_CLASS error_class,
|
||||
BACNET_ERROR_CODE error_code)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
|
||||
/* encode the NPDU portion of the packet */
|
||||
npdu_encode_npdu_data(npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len = npdu_encode_pdu(&buffer[0], dest, src, npdu_data);
|
||||
|
||||
/* encode the APDU portion of the packet */
|
||||
len = bacerror_encode_apdu(&buffer[pdu_len], invoke_id,
|
||||
service, error_class, error_code);
|
||||
pdu_len += len;
|
||||
|
||||
return pdu_len;
|
||||
}
|
||||
|
||||
/** Sends an Abort message
|
||||
* @param buffer The buffer to build the message for sending.
|
||||
* @param dest - Destination address to send the message
|
||||
* @param invoke_id - use to match up a reply
|
||||
* @param reason - #BACNET_ABORT_REASON enumeration
|
||||
* @param server - true or false
|
||||
*
|
||||
* @return Size of the message sent (bytes), or a negative value on error.
|
||||
*/
|
||||
int Send_Error_To_Network(
|
||||
uint8_t * buffer,
|
||||
BACNET_ADDRESS *dest,
|
||||
uint8_t invoke_id,
|
||||
BACNET_CONFIRMED_SERVICE service,
|
||||
BACNET_ERROR_CLASS error_class,
|
||||
BACNET_ERROR_CODE error_code)
|
||||
{
|
||||
int pdu_len = 0;
|
||||
BACNET_ADDRESS src;
|
||||
int bytes_sent = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
|
||||
datalink_get_my_address(&src);
|
||||
pdu_len = error_encode_pdu(buffer, dest, &src, &npdu_data,
|
||||
invoke_id, service, error_class, error_code);
|
||||
bytes_sent = datalink_send_pdu(dest, &npdu_data, &buffer[0], pdu_len);
|
||||
|
||||
return bytes_sent;
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Daniel Blazevic <daniel.blazevic@gmail.com>
|
||||
* @date 2014
|
||||
* @brief Get Alarm Summary Request
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* Copyright (C) 2014 Daniel Blazevic <daniel.blazevic@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @section DESCRIPTION
|
||||
*
|
||||
* The Get Alarm Summary Request is used by a client BACnet-user to
|
||||
* obtain a summary of "active alarms." The term "active alarm" refers to
|
||||
* BACnet standard objects that have an Event_State property whose value is
|
||||
* not equal to NORMAL and a Notify_Type property whose value is ALARM.
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "handlers.h"
|
||||
#include "client.h"
|
||||
#include "get_alarm_sum.h"
|
||||
|
||||
uint8_t Send_Get_Alarm_Summary_Address(
|
||||
BACNET_ADDRESS *dest,
|
||||
uint16_t max_apdu)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
#if PRINT_ENABLED
|
||||
int bytes_sent = 0;
|
||||
#endif
|
||||
|
||||
/* is there a tsm available? */
|
||||
invoke_id = tsm_next_free_invokeID();
|
||||
if (invoke_id) {
|
||||
datalink_get_my_address(&my_address);
|
||||
/* encode the NPDU portion of the packet */
|
||||
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 */
|
||||
len = get_alarm_summary_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
invoke_id);
|
||||
|
||||
pdu_len += len;
|
||||
if ((uint16_t) pdu_len < max_apdu) {
|
||||
tsm_set_confirmed_unsegmented_transaction(invoke_id, dest,
|
||||
&npdu_data, &Handler_Transmit_Buffer[0],
|
||||
(uint16_t) pdu_len);
|
||||
#if PRINT_ENABLED
|
||||
bytes_sent =
|
||||
#endif
|
||||
datalink_send_pdu(dest, &npdu_data,
|
||||
&Handler_Transmit_Buffer[0], pdu_len);
|
||||
#if PRINT_ENABLED
|
||||
if (bytes_sent <= 0)
|
||||
fprintf(stderr,
|
||||
"Failed to Send Get Alarm Summary Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send Get Alarm Summary Request "
|
||||
"(exceeds destination maximum APDU)!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
|
||||
uint8_t Send_Get_Alarm_Summary(
|
||||
uint32_t device_id)
|
||||
{
|
||||
BACNET_ADDRESS dest;
|
||||
unsigned max_apdu = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
bool status = false;
|
||||
|
||||
/* is the device bound? */
|
||||
status = address_get_by_device(device_id, &max_apdu, &dest);
|
||||
if (status) {
|
||||
invoke_id = Send_Get_Alarm_Summary_Address(
|
||||
&dest, max_apdu);
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Daniel Blazevic <daniel.blazevic@gmail.com>
|
||||
* @date 2014
|
||||
* @brief Get Event Request
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* Copyright (C) 2014 Daniel Blazevic <daniel.blazevic@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @section DESCRIPTION
|
||||
*
|
||||
* The GetEventInformation service is used by a client BACnet-user to obtain
|
||||
* a summary of all "active event states". The term "active event states"
|
||||
* refers to all event-initiating objects that have an Event_State property
|
||||
* whose value is not equal to NORMAL, or have an Acked_Transitions property,
|
||||
* which has at least one of the bits (TO-OFFNORMAL, TO-FAULT, TONORMAL)
|
||||
* set to FALSE.
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "handlers.h"
|
||||
#include "client.h"
|
||||
#include "getevent.h"
|
||||
|
||||
uint8_t Send_Get_Event_Information_Address(
|
||||
BACNET_ADDRESS *dest,
|
||||
uint16_t max_apdu,
|
||||
BACNET_OBJECT_ID * lastReceivedObjectIdentifier)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
#if PRINT_ENABLED
|
||||
int bytes_sent = 0;
|
||||
#endif
|
||||
|
||||
/* is there a tsm available? */
|
||||
invoke_id = tsm_next_free_invokeID();
|
||||
if (invoke_id) {
|
||||
datalink_get_my_address(&my_address);
|
||||
/* encode the NPDU portion of the packet */
|
||||
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 */
|
||||
len = getevent_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
invoke_id, lastReceivedObjectIdentifier);
|
||||
|
||||
pdu_len += len;
|
||||
if ((uint16_t) pdu_len < max_apdu) {
|
||||
tsm_set_confirmed_unsegmented_transaction(invoke_id, dest,
|
||||
&npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len);
|
||||
#if PRINT_ENABLED
|
||||
bytes_sent =
|
||||
#endif
|
||||
datalink_send_pdu(dest, &npdu_data,
|
||||
&Handler_Transmit_Buffer[0], pdu_len);
|
||||
#if PRINT_ENABLED
|
||||
if (bytes_sent <= 0)
|
||||
fprintf(stderr, "Failed to Send Get Event Information Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send Get Event Information Request "
|
||||
"(exceeds destination maximum APDU)!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
|
||||
uint8_t Send_Get_Event_Information(
|
||||
uint32_t device_id,
|
||||
BACNET_OBJECT_ID * lastReceivedObjectIdentifier)
|
||||
{
|
||||
BACNET_ADDRESS dest = {0};
|
||||
unsigned max_apdu = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
bool status = false;
|
||||
|
||||
/* is the device bound? */
|
||||
status = address_get_by_device(device_id, &max_apdu, &dest);
|
||||
if (status) {
|
||||
invoke_id = Send_Get_Event_Information_Address(
|
||||
&dest, max_apdu, lastReceivedObjectIdentifier);
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 bowe
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "dcc.h"
|
||||
#include "getevent.h"
|
||||
#include "bacenum.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_getevent.c Send a GetEventInformation request. */
|
||||
|
||||
/** Send a GetEventInformation request to a remote network for a specific device, a range,
|
||||
* or any device.
|
||||
* @param target_address [in] BACnet address of target or broadcast
|
||||
*/
|
||||
uint8_t Send_GetEvent(
|
||||
BACNET_ADDRESS * target_address,
|
||||
BACNET_OBJECT_ID * lastReceivedObjectIdentifier)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
datalink_get_my_address(&my_address);
|
||||
/* encode the NPDU portion of the packet */
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
|
||||
pdu_len =
|
||||
npdu_encode_pdu(&Handler_Transmit_Buffer[0], target_address,
|
||||
&my_address, &npdu_data);
|
||||
|
||||
invoke_id = tsm_next_free_invokeID();
|
||||
if (invoke_id) {
|
||||
/* encode the APDU portion of the packet */
|
||||
len =
|
||||
getevent_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id, lastReceivedObjectIdentifier);
|
||||
pdu_len += len;
|
||||
bytes_sent =
|
||||
datalink_send_pdu(target_address, &npdu_data,
|
||||
&Handler_Transmit_Buffer[0], pdu_len);
|
||||
#if PRINT_ENABLED
|
||||
if (bytes_sent <= 0)
|
||||
fprintf(stderr, "Failed to Send GetEventInformation Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send GetEventInformation Request "
|
||||
"(exceeds destination maximum APDU)!\n");
|
||||
#endif
|
||||
}
|
||||
return invoke_id;
|
||||
}
|
||||
|
||||
/** Send a global GetEventInformation request.
|
||||
*/
|
||||
uint8_t Send_GetEvent_Global( void )
|
||||
{
|
||||
BACNET_ADDRESS dest;
|
||||
|
||||
if (!dcc_communication_enabled())
|
||||
return -1;
|
||||
|
||||
datalink_get_broadcast_address(&dest);
|
||||
|
||||
return Send_GetEvent(&dest, NULL);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "dcc.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "iam.h"
|
||||
#include "txbuf.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_iam.c Send an I-Am message. */
|
||||
|
||||
/** Send a I-Am request to a remote network for a specific device.
|
||||
* @param target_address [in] BACnet address of target router
|
||||
* @param device_id [in] Device Instance 0 - 4194303
|
||||
* @param max_apdu [in] Max APDU 0-65535
|
||||
* @param segmentation [in] #BACNET_SEGMENTATION enumeration
|
||||
* @param vendor_id [in] BACnet vendor ID 0-65535
|
||||
*/
|
||||
void Send_I_Am_To_Network(
|
||||
BACNET_ADDRESS * target_address,
|
||||
uint32_t device_id,
|
||||
unsigned int max_apdu,
|
||||
int segmentation,
|
||||
uint16_t vendor_id)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
datalink_get_my_address(&my_address);
|
||||
/* encode the NPDU portion of the packet */
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
|
||||
pdu_len =
|
||||
npdu_encode_pdu(&Handler_Transmit_Buffer[0], target_address,
|
||||
&my_address, &npdu_data);
|
||||
/* encode the APDU portion of the packet */
|
||||
/* encode the APDU portion of the packet */
|
||||
len =
|
||||
iam_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
device_id, max_apdu, segmentation, vendor_id);
|
||||
pdu_len += len;
|
||||
bytes_sent =
|
||||
datalink_send_pdu(target_address, &npdu_data,
|
||||
&Handler_Transmit_Buffer[0], pdu_len);
|
||||
if (bytes_sent <= 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Failed to Send I-Am Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Encode an I Am message to be broadcast.
|
||||
* @param buffer [in,out] The buffer to use for building the message.
|
||||
* @param dest [out] The destination address information.
|
||||
* @param npdu_data [out] The NPDU structure describing the message.
|
||||
* @return The length of the message in buffer[].
|
||||
*/
|
||||
int iam_encode_pdu(
|
||||
uint8_t * buffer,
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_NPDU_DATA * npdu_data)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
BACNET_ADDRESS my_address;
|
||||
datalink_get_my_address(&my_address);
|
||||
|
||||
datalink_get_broadcast_address(dest);
|
||||
/* encode the NPDU portion of the packet */
|
||||
npdu_encode_npdu_data(npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len = npdu_encode_pdu(&buffer[0], dest, &my_address, npdu_data);
|
||||
|
||||
/* encode the APDU portion of the packet */
|
||||
len =
|
||||
iam_encode_apdu(&buffer[pdu_len], Device_Object_Instance_Number(),
|
||||
MAX_APDU, SEGMENTATION_NONE, Device_Vendor_Identifier());
|
||||
pdu_len += len;
|
||||
|
||||
return pdu_len;
|
||||
}
|
||||
|
||||
/** Broadcast an I Am message.
|
||||
* @ingroup DMDDB
|
||||
*
|
||||
* @param buffer [in] The buffer to use for building and sending the message.
|
||||
*/
|
||||
void Send_I_Am(
|
||||
uint8_t * buffer)
|
||||
{
|
||||
int pdu_len = 0;
|
||||
BACNET_ADDRESS dest;
|
||||
int bytes_sent = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
|
||||
#if 0
|
||||
/* note: there is discussion in the BACnet committee
|
||||
that we should allow a device to reply with I-Am
|
||||
so that dynamic binding always work. If the DCC
|
||||
initiator loses the MAC address and routing info,
|
||||
they can never re-enable DCC because they can't
|
||||
find the device with WhoIs/I-Am */
|
||||
/* are we are forbidden to send? */
|
||||
if (!dcc_communication_enabled())
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
/* encode the data */
|
||||
pdu_len = iam_encode_pdu(buffer, &dest, &npdu_data);
|
||||
/* send data */
|
||||
bytes_sent = datalink_send_pdu(&dest, &npdu_data, &buffer[0], pdu_len);
|
||||
|
||||
if (bytes_sent <= 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Failed to Send I-Am Reply (%s)!\n", strerror(errno));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/** Encode an I Am message to be unicast directly back to the src.
|
||||
*
|
||||
* @param buffer [in,out] The buffer to use for building the message.
|
||||
* @param src [in] The source address information. If the src address is not
|
||||
* given, the dest address will be a broadcast address.
|
||||
* @param dest [out] The destination address information.
|
||||
* @param npdu_data [out] The NPDU structure describing the message.
|
||||
* @return The length of the message in buffer[].
|
||||
*/
|
||||
int iam_unicast_encode_pdu(
|
||||
uint8_t * buffer,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_NPDU_DATA * npdu_data)
|
||||
{
|
||||
int npdu_len = 0;
|
||||
int apdu_len = 0;
|
||||
int pdu_len = 0;
|
||||
BACNET_ADDRESS my_address;
|
||||
/* The destination will be the same as the src, so copy it over. */
|
||||
bacnet_address_copy(dest, src);
|
||||
/* dest->net = 0; - no, must direct back to src->net to meet BTL tests */
|
||||
|
||||
datalink_get_my_address(&my_address);
|
||||
/* encode the NPDU portion of the packet */
|
||||
npdu_encode_npdu_data(npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
npdu_len = npdu_encode_pdu(&buffer[0], dest, &my_address, npdu_data);
|
||||
/* encode the APDU portion of the packet */
|
||||
apdu_len =
|
||||
iam_encode_apdu(&buffer[npdu_len], Device_Object_Instance_Number(),
|
||||
MAX_APDU, SEGMENTATION_NONE, Device_Vendor_Identifier());
|
||||
pdu_len = npdu_len + apdu_len;
|
||||
|
||||
return pdu_len;
|
||||
}
|
||||
|
||||
/** Send an I-Am message by unicasting directly back to the src.
|
||||
* @ingroup DMDDB
|
||||
* @note As of Addendum 135-2008q-1, unicast responses are allowed;
|
||||
* in modern firewalled corporate networks, this may be the
|
||||
* only type of response that will reach the source on
|
||||
* another subnet (without using the BBMD). <br>
|
||||
* However, some BACnet routers may not correctly handle this message.
|
||||
*
|
||||
* @param buffer [in] The buffer to use for building and sending the message.
|
||||
* @param src [in] The source address information from service handler.
|
||||
*/
|
||||
void Send_I_Am_Unicast(
|
||||
uint8_t * buffer,
|
||||
BACNET_ADDRESS * src)
|
||||
{
|
||||
int pdu_len = 0;
|
||||
BACNET_ADDRESS dest;
|
||||
int bytes_sent = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
|
||||
#if 0
|
||||
/* note: there is discussion in the BACnet committee
|
||||
that we should allow a device to reply with I-Am
|
||||
so that dynamic binding always work. If the DCC
|
||||
initiator loses the MAC address and routing info,
|
||||
they can never re-enable DCC because they can't
|
||||
find the device with WhoIs/I-Am */
|
||||
/* are we are forbidden to send? */
|
||||
if (!dcc_communication_enabled())
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
/* encode the data */
|
||||
pdu_len = iam_unicast_encode_pdu(buffer, src, &dest, &npdu_data);
|
||||
/* send data */
|
||||
bytes_sent = datalink_send_pdu(&dest, &npdu_data, &buffer[0], pdu_len);
|
||||
|
||||
if (bytes_sent <= 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Failed to Send I-Am Reply (%s)!\n", strerror(errno));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "dcc.h"
|
||||
#include "ihave.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_ihave.c Send an I-Have (property) message. */
|
||||
|
||||
/** Broadcast an I Have message.
|
||||
* @ingroup DMDOB
|
||||
*
|
||||
* @param device_id [in] My device ID.
|
||||
* @param object_type [in] The BACNET_OBJECT_TYPE that I Have.
|
||||
* @param object_instance [in] The Object ID that I Have.
|
||||
* @param object_name [in] The Name of the Object I Have.
|
||||
*/
|
||||
void Send_I_Have(
|
||||
uint32_t device_id,
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
uint32_t object_instance,
|
||||
BACNET_CHARACTER_STRING * object_name)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
BACNET_ADDRESS dest;
|
||||
int bytes_sent = 0;
|
||||
BACNET_I_HAVE_DATA data;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
datalink_get_my_address(&my_address);
|
||||
/* if we are forbidden to send, don't send! */
|
||||
if (!dcc_communication_enabled())
|
||||
return;
|
||||
/* Who-Has is a global broadcast */
|
||||
datalink_get_broadcast_address(&dest);
|
||||
/* encode the NPDU portion of the packet */
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len =
|
||||
npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address,
|
||||
&npdu_data);
|
||||
|
||||
/* encode the APDU portion of the packet */
|
||||
data.device_id.type = OBJECT_DEVICE;
|
||||
data.device_id.instance = device_id;
|
||||
data.object_id.type = object_type;
|
||||
data.object_id.instance = object_instance;
|
||||
characterstring_copy(&data.object_name, object_name);
|
||||
len = ihave_encode_apdu(&Handler_Transmit_Buffer[pdu_len], &data);
|
||||
pdu_len += len;
|
||||
/* send the data */
|
||||
bytes_sent =
|
||||
datalink_send_pdu(&dest, &npdu_data, &Handler_Transmit_Buffer[0],
|
||||
pdu_len);
|
||||
if (bytes_sent <= 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Failed to Send I-Have Reply (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2009 John Minack <minack@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 <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "dcc.h"
|
||||
#include "whois.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "lso.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_lso.c Send BACnet Life Safety Operation message. */
|
||||
|
||||
/* returns the invoke ID for confirmed request, or zero on failure */
|
||||
|
||||
|
||||
uint8_t Send_Life_Safety_Operation_Data(
|
||||
uint32_t device_id,
|
||||
BACNET_LSO_DATA * data)
|
||||
{
|
||||
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;
|
||||
|
||||
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);
|
||||
len =
|
||||
lso_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id,
|
||||
data);
|
||||
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 Life Safe Op Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send Life Safe Op Request "
|
||||
"(exceeds destination maximum APDU)!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "dcc.h"
|
||||
#include "ptransfer.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "mydata.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_ptransfer.c Send a Private Transfer request. */
|
||||
|
||||
/* This function is exported. Hence this unnecessary prototype. */
|
||||
uint8_t Send_Private_Transfer_Request(
|
||||
uint32_t device_id,
|
||||
uint16_t vendor_id,
|
||||
uint32_t service_number,
|
||||
char block_number,
|
||||
DATABLOCK * block);
|
||||
|
||||
uint8_t Send_Private_Transfer_Request(
|
||||
uint32_t device_id,
|
||||
uint16_t vendor_id,
|
||||
uint32_t service_number,
|
||||
char block_number,
|
||||
DATABLOCK * 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 pt_block;
|
||||
BACNET_CHARACTER_STRING bsTemp;
|
||||
|
||||
/* 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 */
|
||||
|
||||
pt_block.vendorID = vendor_id;
|
||||
pt_block.serviceNumber = service_number;
|
||||
if (service_number == MY_SVC_READ) {
|
||||
len += encode_application_unsigned(&pt_req_buffer[len], block_number); /* The block number we want to retrieve */
|
||||
} else {
|
||||
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;
|
||||
len =
|
||||
ptransfer_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id,
|
||||
&pt_block);
|
||||
pdu_len += len;
|
||||
|
||||
/* will it fit in the sender?
|
||||
note: if there is a bottleneck router in between
|
||||
us and the destination, we won't know unless
|
||||
we have a way to check for that and update the
|
||||
max_apdu in the address binding table. */
|
||||
|
||||
if ((unsigned) pdu_len < max_apdu) {
|
||||
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
|
||||
&npdu_data, &Handler_Transmit_Buffer[0], (uint16_t) pdu_len);
|
||||
bytes_sent =
|
||||
datalink_send_pdu(&dest, &npdu_data,
|
||||
&Handler_Transmit_Buffer[0], pdu_len);
|
||||
#if PRINT_ENABLED
|
||||
if (bytes_sent <= 0)
|
||||
fprintf(stderr,
|
||||
"Failed to Send Private Transfer Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send Private Transfer Request "
|
||||
"(exceeds destination maximum APDU)!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "dcc.h"
|
||||
#include "rd.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_rd.c Send a Reinitialize Device request. */
|
||||
|
||||
/** Sends a Reinitialize Device (RD) request.
|
||||
* @ingroup DMRD
|
||||
*
|
||||
* @param device_id [in] The index to the device address in our address cache.
|
||||
* @param state [in] Specifies the desired state of the device after reinitialization.
|
||||
* @param password [in] Optional password, up to 20 chars.
|
||||
* @return The invokeID of the transmitted message, or 0 on failure.
|
||||
*/
|
||||
uint8_t Send_Reinitialize_Device_Request(
|
||||
uint32_t device_id,
|
||||
BACNET_REINITIALIZED_STATE state,
|
||||
char *password)
|
||||
{
|
||||
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_CHARACTER_STRING password_string;
|
||||
BACNET_NPDU_DATA npdu_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 */
|
||||
characterstring_init_ansi(&password_string, password);
|
||||
len =
|
||||
rd_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id, state,
|
||||
password ? &password_string : NULL);
|
||||
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 ReinitializeDevice Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send ReinitializeDevice Request "
|
||||
"(exceeds destination maximum APDU)!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "dcc.h"
|
||||
#include "rpm.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "readrange.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_readrange.c Send a ReadRange request. */
|
||||
|
||||
/* returns invoke id of 0 if device is not bound or no tsm available */
|
||||
uint8_t Send_ReadRange_Request(
|
||||
uint32_t device_id, /* destination device */
|
||||
BACNET_READ_RANGE_DATA * read_access_data)
|
||||
{
|
||||
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;
|
||||
|
||||
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 */
|
||||
len =
|
||||
rr_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id,
|
||||
read_access_data);
|
||||
if (len <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pdu_len += len;
|
||||
/* is it small enough for the the destination to receive?
|
||||
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 ReadRange Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send ReadRange Request (exceeds destination maximum APDU)!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "bactext.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "client.h"
|
||||
#include "debug.h"
|
||||
|
||||
/** @file s_router.c Methods to send various BACnet Router Network Layer Messages. */
|
||||
|
||||
/** Initialize an npdu_data structure with given parameters and good defaults,
|
||||
* and add the Network Layer Message fields.
|
||||
* The name is a misnomer, as it doesn't do any actual encoding here.
|
||||
* @see npdu_encode_npdu_data for a simpler version to use when sending an
|
||||
* APDU instead of a Network Layer Message.
|
||||
*
|
||||
* @param npdu_data [out] Returns a filled-out structure with information
|
||||
* provided by the other arguments and good defaults.
|
||||
* @param network_message_type [in] The type of Network Layer Message.
|
||||
* @param data_expecting_reply [in] True if message should have a reply.
|
||||
* @param priority [in] One of the 4 priorities defined in section 6.2.2,
|
||||
* like B'11' = Life Safety message
|
||||
*/
|
||||
static void npdu_encode_npdu_network(
|
||||
BACNET_NPDU_DATA * npdu_data,
|
||||
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
|
||||
bool data_expecting_reply,
|
||||
BACNET_MESSAGE_PRIORITY priority)
|
||||
{
|
||||
if (npdu_data) {
|
||||
npdu_data->data_expecting_reply = data_expecting_reply;
|
||||
npdu_data->protocol_version = BACNET_PROTOCOL_VERSION;
|
||||
npdu_data->network_layer_message = true; /* false if APDU */
|
||||
npdu_data->network_message_type = network_message_type; /* optional */
|
||||
npdu_data->vendor_id = 0; /* optional, if net message type is > 0x80 */
|
||||
npdu_data->priority = priority;
|
||||
npdu_data->hop_count = HOP_COUNT_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Function to encode and send any supported Network Layer Message.
|
||||
* The payload for the message is encoded from information in the iArgs[] array.
|
||||
* The contents of iArgs are are, per message type:
|
||||
* - NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK: Single int for DNET requested
|
||||
* - NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: Array of DNET(s) to send,
|
||||
* terminated with -1
|
||||
* - NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK: array of 2 ints,
|
||||
* first is reason, second is DNET of interest
|
||||
* - NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK: same as I-Am-Router msg
|
||||
* - NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK: same as I-Am-Router msg
|
||||
* - NETWORK_MESSAGE_INIT_RT_TABLE and NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
|
||||
* Array of DNET(s) to process as "Ports", terminated with -1. Each DNET
|
||||
* will be expanded to a BACNET_ROUTER_PORT (with simple defaults for
|
||||
* most fields) and encoded.
|
||||
*
|
||||
* @param network_message_type [in] The type of message to be sent.
|
||||
* @param dst [in/out] If not NULL, contains the destination for the message.
|
||||
* @param iArgs [in] An optional array of values whose meaning depends on
|
||||
* the type of message.
|
||||
* @return Number of bytes sent, or <=0 if no message was sent.
|
||||
*/
|
||||
int Send_Network_Layer_Message(
|
||||
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
|
||||
BACNET_ADDRESS * dst,
|
||||
int *iArgs)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
int *pVal = iArgs; /* Start with first value */
|
||||
bool data_expecting_reply = false;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS bcastDest;
|
||||
|
||||
if (iArgs == NULL)
|
||||
return 0; /* Can't do anything here */
|
||||
|
||||
/* If dst was NULL, get our (local net) broadcast MAC address. */
|
||||
if (dst == NULL) {
|
||||
datalink_get_broadcast_address(&bcastDest);
|
||||
dst = &bcastDest;
|
||||
}
|
||||
|
||||
if (network_message_type == NETWORK_MESSAGE_INIT_RT_TABLE)
|
||||
data_expecting_reply = true; /* DER in this one case */
|
||||
npdu_encode_npdu_network(&npdu_data, network_message_type,
|
||||
data_expecting_reply, MESSAGE_PRIORITY_NORMAL);
|
||||
|
||||
/* We don't need src information, since a message can't originate from
|
||||
* our downstream BACnet network.
|
||||
*/
|
||||
pdu_len =
|
||||
npdu_encode_pdu(&Handler_Transmit_Buffer[0], dst, NULL, &npdu_data);
|
||||
|
||||
/* Now encode the optional payload bytes, per message type */
|
||||
switch (network_message_type) {
|
||||
case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK:
|
||||
if (*pVal >= 0) {
|
||||
len =
|
||||
encode_unsigned16(&Handler_Transmit_Buffer[pdu_len],
|
||||
(uint16_t) * pVal);
|
||||
pdu_len += len;
|
||||
}
|
||||
/* else, don't encode a DNET */
|
||||
break;
|
||||
|
||||
case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK:
|
||||
while (*pVal >= 0) {
|
||||
len =
|
||||
encode_unsigned16(&Handler_Transmit_Buffer[pdu_len],
|
||||
(uint16_t) * pVal);
|
||||
pdu_len += len;
|
||||
pVal++;
|
||||
}
|
||||
break;
|
||||
|
||||
case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK:
|
||||
/* Encode the Reason byte, then the DNET */
|
||||
Handler_Transmit_Buffer[pdu_len++] = (uint8_t) * pVal;
|
||||
pVal++;
|
||||
len =
|
||||
encode_unsigned16(&Handler_Transmit_Buffer[pdu_len],
|
||||
(uint16_t) * pVal);
|
||||
pdu_len += len;
|
||||
break;
|
||||
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE:
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
|
||||
/* First, count the number of Ports we will encode */
|
||||
len = 0; /* Re-purpose len as our counter here */
|
||||
while (*pVal >= 0) {
|
||||
len++;
|
||||
pVal++;
|
||||
}
|
||||
Handler_Transmit_Buffer[pdu_len++] = (uint8_t) len;
|
||||
|
||||
if (len > 0) {
|
||||
uint8_t portID = 1;
|
||||
pVal = iArgs; /* Reset to beginning */
|
||||
/* Now encode each (virtual) BACNET_ROUTER_PORT.
|
||||
* We will simply use a positive index for PortID,
|
||||
* and have no PortInfo.
|
||||
*/
|
||||
while (*pVal >= 0) {
|
||||
len =
|
||||
encode_unsigned16(&Handler_Transmit_Buffer[pdu_len],
|
||||
(uint16_t) * pVal);
|
||||
pdu_len += len;
|
||||
Handler_Transmit_Buffer[pdu_len++] = portID++;
|
||||
Handler_Transmit_Buffer[pdu_len++] = 0;
|
||||
debug_printf(" Sending Routing Table entry for %u \n",
|
||||
*pVal);
|
||||
pVal++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
debug_printf("Not sent: %s message unsupported \n",
|
||||
bactext_network_layer_msg_name(network_message_type));
|
||||
return 0;
|
||||
break; /* Will never reach this line */
|
||||
}
|
||||
|
||||
if (dst != NULL)
|
||||
debug_printf("Sending %s message to BACnet network %u \n",
|
||||
bactext_network_layer_msg_name(network_message_type), dst->net);
|
||||
else
|
||||
debug_printf("Sending %s message to local BACnet network \n",
|
||||
bactext_network_layer_msg_name(network_message_type));
|
||||
|
||||
/* Now send the message */
|
||||
bytes_sent =
|
||||
datalink_send_pdu(dst, &npdu_data, &Handler_Transmit_Buffer[0],
|
||||
pdu_len);
|
||||
#if PRINT_ENABLED
|
||||
if (bytes_sent <= 0) {
|
||||
int wasErrno = errno; /* preserve the errno */
|
||||
debug_printf("Failed to send %s message (%s)!\n",
|
||||
bactext_network_layer_msg_name(network_message_type),
|
||||
strerror(wasErrno));
|
||||
}
|
||||
#endif
|
||||
return bytes_sent;
|
||||
}
|
||||
|
||||
|
||||
/** Finds a specific router, or all reachable BACnet networks.
|
||||
* The response(s) will come in I-am-router-to-network message(s).
|
||||
* @ingroup NMRC
|
||||
*
|
||||
* @param dst [in] If NULL, request will be broadcast to the local BACnet
|
||||
* network. Optionally may designate a particular router
|
||||
* destination to respond.
|
||||
* @param dnet [in] Which BACnet network to request for; if -1, no DNET
|
||||
* will be sent and the receiving router(s) will send
|
||||
* their full list of reachable BACnet networks.
|
||||
*/
|
||||
void Send_Who_Is_Router_To_Network(
|
||||
BACNET_ADDRESS * dst,
|
||||
int dnet)
|
||||
{
|
||||
Send_Network_Layer_Message(NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK, dst,
|
||||
&dnet);
|
||||
}
|
||||
|
||||
/** Broadcast an I-am-router-to-network message, giving the list of networks
|
||||
* we can reach.
|
||||
* The message will be sent to our normal DataLink Layer interface,
|
||||
* not the routed backend.
|
||||
* @ingroup NMRC
|
||||
*
|
||||
* @param DNET_list [in] List of BACnet network numbers for which I am a router,
|
||||
* terminated with -1
|
||||
*/
|
||||
void Send_I_Am_Router_To_Network(
|
||||
const int DNET_list[])
|
||||
{
|
||||
/* Use a NULL dst here since we want a broadcast MAC address. */
|
||||
Send_Network_Layer_Message(NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, NULL,
|
||||
(int *) DNET_list);
|
||||
}
|
||||
|
||||
/** Finds a specific router, or all reachable BACnet networks.
|
||||
* The response(s) will come in I-am-router-to-network message(s).
|
||||
* @ingroup NMRC
|
||||
*
|
||||
* @param dst [in] If NULL, request will be broadcast to the local BACnet
|
||||
* network. Otherwise, designates a particular router
|
||||
* destination.
|
||||
* @param reject_reason [in] One of the BACNET_NETWORK_REJECT_REASONS codes.
|
||||
* @param dnet [in] Which BACnet network orginated the message.
|
||||
*/
|
||||
void Send_Reject_Message_To_Network(
|
||||
BACNET_ADDRESS * dst,
|
||||
uint8_t reject_reason,
|
||||
int dnet)
|
||||
{
|
||||
int iArgs[2];
|
||||
iArgs[0] = reject_reason;
|
||||
iArgs[1] = dnet;
|
||||
Send_Network_Layer_Message(NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK, dst,
|
||||
iArgs);
|
||||
debug_printf(" Reject Reason=%d, DNET=%u\n", reject_reason, dnet);
|
||||
}
|
||||
|
||||
|
||||
/** Send an Initialize Routing Table message, built from an optional DNET[]
|
||||
* array.
|
||||
* There are two cases here:
|
||||
* 1) We are requesting a destination router's Routing Table.
|
||||
* In that case, DNET[] should just have one entry of -1 (no routing table
|
||||
* is sent).
|
||||
* 2) We are sending out our Routing Table for some reason (normally bcast it).
|
||||
* @ingroup NMRC
|
||||
*
|
||||
* @param dst [in] If NULL, msg will be broadcast to the local BACnet network.
|
||||
* Optionally may designate a particular router destination,
|
||||
* especially when requesting a Routing Table.
|
||||
* @param DNET_list [in] List of BACnet network numbers for which I am a router,
|
||||
* terminated with -1. Will be just -1 when we are
|
||||
* requesting a routing table.
|
||||
*/
|
||||
void Send_Initialize_Routing_Table(
|
||||
BACNET_ADDRESS * dst,
|
||||
const int DNET_list[])
|
||||
{
|
||||
/* Use a NULL dst here since we want a broadcast MAC address. */
|
||||
Send_Network_Layer_Message(NETWORK_MESSAGE_INIT_RT_TABLE, dst,
|
||||
(int *) DNET_list);
|
||||
}
|
||||
|
||||
|
||||
/** Sends our Routing Table, built from our DNET[] array, as an ACK.
|
||||
* There are two cases here:
|
||||
* 1) We are responding to a NETWORK_MESSAGE_INIT_RT_TABLE requesting our table.
|
||||
* We will normally broadcast that response.
|
||||
* 2) We are ACKing the receipt of a NETWORK_MESSAGE_INIT_RT_TABLE containing a
|
||||
* routing table, and then we will want to respond to that dst router.
|
||||
* In that case, DNET[] should just have one entry of -1 (no routing table
|
||||
* is sent).
|
||||
* @ingroup NMRC
|
||||
*
|
||||
* @param dst [in] If NULL, Ack will be broadcast to the local BACnet network.
|
||||
* Optionally may designate a particular router destination,
|
||||
* especially when ACKing receipt of this message type.
|
||||
* @param DNET_list [in] List of BACnet network numbers for which I am a router,
|
||||
* terminated with -1. May be just -1 when no table
|
||||
* should be sent.
|
||||
*/
|
||||
void Send_Initialize_Routing_Table_Ack(
|
||||
BACNET_ADDRESS * dst,
|
||||
const int DNET_list[])
|
||||
{
|
||||
Send_Network_Layer_Message(NETWORK_MESSAGE_INIT_RT_TABLE_ACK, dst,
|
||||
(int *) DNET_list);
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "dcc.h"
|
||||
#include "rp.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_rp.c Send Read Property request. */
|
||||
|
||||
/** Sends a Read Property request
|
||||
* @ingroup DSRP
|
||||
*
|
||||
* @param dest [in] BACNET_ADDRESS of the destination device
|
||||
* @param max_apdu [in]
|
||||
* @param object_type [in] Type of the object whose property is to be read.
|
||||
* @param object_instance [in] Instance # of the object to be read.
|
||||
* @param object_property [in] Property to be read, but not ALL, REQUIRED, or OPTIONAL.
|
||||
* @param array_index [in] Optional: if the Property is an array,
|
||||
* - 0 for the array size
|
||||
* - 1 to n for individual array members
|
||||
* - BACNET_ARRAY_ALL (~0) for the full array to be read.
|
||||
* @return invoke id of outgoing message, or 0 if device is not bound or no tsm available
|
||||
*/
|
||||
uint8_t Send_Read_Property_Request_Address(
|
||||
BACNET_ADDRESS * dest,
|
||||
uint16_t max_apdu,
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_ID object_property,
|
||||
uint32_t array_index)
|
||||
{
|
||||
BACNET_ADDRESS my_address;
|
||||
uint8_t invoke_id = 0;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
BACNET_READ_PROPERTY_DATA data;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
|
||||
if (!dcc_communication_enabled()) {
|
||||
return 0;
|
||||
}
|
||||
if (!dest) {
|
||||
return 0;
|
||||
}
|
||||
/* is there a tsm available? */
|
||||
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 */
|
||||
data.object_type = object_type;
|
||||
data.object_instance = object_instance;
|
||||
data.object_property = object_property;
|
||||
data.array_index = array_index;
|
||||
len =
|
||||
rp_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id,
|
||||
&data);
|
||||
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 ((uint16_t) 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 (bytes_sent <= 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Failed to Send ReadProperty Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send ReadProperty Request "
|
||||
"(exceeds destination maximum APDU)!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
|
||||
/** Sends a Read Property request.
|
||||
* @ingroup DSRP
|
||||
*
|
||||
* @param device_id [in] ID of the destination device
|
||||
* @param object_type [in] Type of the object whose property is to be read.
|
||||
* @param object_instance [in] Instance # of the object to be read.
|
||||
* @param object_property [in] Property to be read, but not ALL, REQUIRED, or OPTIONAL.
|
||||
* @param array_index [in] Optional: if the Property is an array,
|
||||
* - 0 for the array size
|
||||
* - 1 to n for individual array members
|
||||
* - BACNET_ARRAY_ALL (~0) for the full array to be read.
|
||||
* @return invoke id of outgoing message, or 0 if device is not bound or no tsm available
|
||||
*/
|
||||
uint8_t Send_Read_Property_Request(
|
||||
uint32_t device_id, /* destination device */
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_ID object_property,
|
||||
uint32_t array_index)
|
||||
{
|
||||
BACNET_ADDRESS dest = { 0 };
|
||||
unsigned max_apdu = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
bool status = false;
|
||||
|
||||
/* is the device bound? */
|
||||
status = address_get_by_device(device_id, &max_apdu, &dest);
|
||||
if (status) {
|
||||
invoke_id =
|
||||
Send_Read_Property_Request_Address(&dest, max_apdu, object_type,
|
||||
object_instance, object_property, array_index);
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "dcc.h"
|
||||
#include "rpm.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "sbuf.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_rpm.c Send Read Property Multiple request. */
|
||||
|
||||
/** Sends a Read Property Multiple request.
|
||||
* @ingroup DSRPM
|
||||
*
|
||||
* @param pdu [out] Buffer to build the outgoing message into
|
||||
* @param max_pdu [in] Length of the pdu buffer.
|
||||
* @param device_id [in] ID of the destination device
|
||||
* @param read_access_data [in] Ptr to structure with the linked list of
|
||||
* properties to be read.
|
||||
* @return invoke id of outgoing message, or 0 if device is not bound or no tsm available
|
||||
*/
|
||||
uint8_t Send_Read_Property_Multiple_Request(
|
||||
uint8_t * pdu,
|
||||
size_t max_pdu,
|
||||
uint32_t device_id, /* destination device */
|
||||
BACNET_READ_ACCESS_DATA * read_access_data)
|
||||
{
|
||||
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;
|
||||
|
||||
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(&pdu[0], &dest, &my_address, &npdu_data);
|
||||
/* encode the APDU portion of the packet */
|
||||
len =
|
||||
rpm_encode_apdu(&pdu[pdu_len], max_pdu - pdu_len, invoke_id,
|
||||
read_access_data);
|
||||
if (len <= 0) {
|
||||
return 0;
|
||||
}
|
||||
pdu_len += len;
|
||||
/* is it small enough for the destination to receive?
|
||||
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, &pdu[0], (uint16_t) pdu_len);
|
||||
bytes_sent =
|
||||
datalink_send_pdu(&dest, &npdu_data, &pdu[0], pdu_len);
|
||||
#if PRINT_ENABLED
|
||||
if (bytes_sent <= 0)
|
||||
fprintf(stderr,
|
||||
"Failed to Send ReadPropertyMultiple Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send ReadPropertyMultiple Request "
|
||||
"(exceeds destination maximum APDU)!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "dcc.h"
|
||||
#include "timesync.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_ts.c Send TimeSync requests. */
|
||||
|
||||
/**
|
||||
* Sends a TimeSync message to a specific destination
|
||||
*
|
||||
* @param dest - #BACNET_ADDRESS - the specific destination
|
||||
* @param bdate - #BACNET_DATE
|
||||
* @param btime - #BACNET_TIME
|
||||
*/
|
||||
void Send_TimeSync_Remote(
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_DATE * bdate,
|
||||
BACNET_TIME * btime)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
if (!dcc_communication_enabled())
|
||||
return;
|
||||
|
||||
datalink_get_my_address(&my_address);
|
||||
/* encode the NPDU portion of the packet */
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len =
|
||||
npdu_encode_pdu(&Handler_Transmit_Buffer[0], dest, &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(dest, &npdu_data, &Handler_Transmit_Buffer[0],
|
||||
pdu_len);
|
||||
#if PRINT_ENABLED
|
||||
if (bytes_sent <= 0)
|
||||
fprintf(stderr, "Failed to Send Time-Synchronization Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a TimeSync message as a broadcast
|
||||
*
|
||||
* @param bdate - #BACNET_DATE
|
||||
* @param btime - #BACNET_TIME
|
||||
*/
|
||||
void Send_TimeSync(
|
||||
BACNET_DATE * bdate,
|
||||
BACNET_TIME * btime)
|
||||
{
|
||||
BACNET_ADDRESS dest;
|
||||
|
||||
datalink_get_broadcast_address(&dest);
|
||||
Send_TimeSync_Remote(&dest, bdate, btime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a UTC TimeSync message to a specific destination
|
||||
*
|
||||
* @param dest - #BACNET_ADDRESS - the specific destination
|
||||
* @param bdate - #BACNET_DATE
|
||||
* @param btime - #BACNET_TIME
|
||||
*/
|
||||
void Send_TimeSyncUTC_Remote(
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_DATE * bdate,
|
||||
BACNET_TIME * btime)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
if (!dcc_communication_enabled())
|
||||
return;
|
||||
|
||||
datalink_get_my_address(&my_address);
|
||||
/* encode the NPDU portion of the packet */
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len =
|
||||
npdu_encode_pdu(&Handler_Transmit_Buffer[0], dest, &my_address,
|
||||
&npdu_data);
|
||||
/* encode the APDU portion of the packet */
|
||||
len =
|
||||
timesync_utc_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
bdate, btime);
|
||||
pdu_len += 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 UTC-Time-Synchronization Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a UTC TimeSync message as a broadcast
|
||||
*
|
||||
* @param bdate - #BACNET_DATE
|
||||
* @param btime - #BACNET_TIME
|
||||
*/
|
||||
void Send_TimeSyncUTC(
|
||||
BACNET_DATE * bdate,
|
||||
BACNET_TIME * btime)
|
||||
{
|
||||
BACNET_ADDRESS dest;
|
||||
|
||||
datalink_get_broadcast_address(&dest);
|
||||
Send_TimeSyncUTC_Remote(&dest, bdate, btime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a UTC TimeSync message using the local time from the device.
|
||||
*/
|
||||
void Send_TimeSyncUTC_Device(void)
|
||||
{
|
||||
int32_t utc_offset_minutes = 0;
|
||||
bool dst = false;
|
||||
BACNET_DATE_TIME local_time;
|
||||
BACNET_DATE_TIME utc_time;
|
||||
|
||||
Device_getCurrentDateTime(&local_time);
|
||||
dst = Device_Daylight_Savings_Status();
|
||||
utc_offset_minutes = Device_UTC_Offset();
|
||||
datetime_copy(&utc_time, &local_time);
|
||||
datetime_add_minutes(&utc_time, utc_offset_minutes);
|
||||
if (dst) {
|
||||
datetime_add_minutes(&utc_time, -60);
|
||||
}
|
||||
Send_TimeSyncUTC(&utc_time.date, &utc_time.time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a TimeSync message using the local time from the device.
|
||||
*/
|
||||
void Send_TimeSync_Device(void)
|
||||
{
|
||||
BACNET_DATE_TIME local_time;
|
||||
|
||||
Device_getCurrentDateTime(&local_time);
|
||||
Send_TimeSync(&local_time.date, &local_time.time);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2008 John Minack
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include "event.h"
|
||||
#include "datalink.h"
|
||||
#include "client.h"
|
||||
#include "device.h"
|
||||
|
||||
/** @file s_uevent.c Send an Unconfirmed Event Notification. */
|
||||
|
||||
/** Sends an Unconfirmed Alarm/Event Notification.
|
||||
* @ingroup EVNOTFCN
|
||||
*
|
||||
* @param buffer [in,out] The buffer to build the message in for sending.
|
||||
* @param data [in] The information about the Event to be sent.
|
||||
* @param dest [in] The destination address information (may be a broadcast).
|
||||
* @return Size of the message sent (bytes), or a negative value on error.
|
||||
*/
|
||||
int Send_UEvent_Notify(
|
||||
uint8_t * buffer,
|
||||
BACNET_EVENT_NOTIFICATION_DATA * data,
|
||||
BACNET_ADDRESS * dest)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
datalink_get_my_address(&my_address);
|
||||
/* encode the NPDU portion of the packet */
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len = npdu_encode_pdu(buffer, dest, &my_address, &npdu_data);
|
||||
/* encode the APDU portion of the packet */
|
||||
len = uevent_notify_encode_apdu(&buffer[pdu_len], data);
|
||||
pdu_len += len;
|
||||
/* send the data */
|
||||
bytes_sent = datalink_send_pdu(dest, &npdu_data, &buffer[0], pdu_len);
|
||||
|
||||
return bytes_sent;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2009 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 <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "dcc.h"
|
||||
#include "ptransfer.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_upt.c Send an Unconfirmed Private Transfer request. */
|
||||
|
||||
int Send_UnconfirmedPrivateTransfer(
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_PRIVATE_TRANSFER_DATA * private_data)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
if (!dcc_communication_enabled())
|
||||
return bytes_sent;
|
||||
|
||||
datalink_get_my_address(&my_address);
|
||||
/* encode the NPDU portion of the packet */
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len =
|
||||
npdu_encode_pdu(&Handler_Transmit_Buffer[0], dest, &my_address,
|
||||
&npdu_data);
|
||||
|
||||
/* encode the APDU portion of the packet */
|
||||
len =
|
||||
uptransfer_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
private_data);
|
||||
pdu_len += len;
|
||||
bytes_sent =
|
||||
datalink_send_pdu(dest, &npdu_data, &Handler_Transmit_Buffer[0],
|
||||
pdu_len);
|
||||
if (bytes_sent <= 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send UnconfirmedPrivateTransfer Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
}
|
||||
|
||||
return bytes_sent;
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "dcc.h"
|
||||
#include "whohas.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_whohas.c Send Who-Has requests. */
|
||||
|
||||
/** Send a Who-Has request for a device which has a named Object.
|
||||
* @ingroup DMDOB
|
||||
* If low_limit and high_limit both are -1, then the device ID range is unlimited.
|
||||
* If low_limit and high_limit have the same non-negative value, then only
|
||||
* that device will respond.
|
||||
* Otherwise, low_limit must be less than high_limit for a range.
|
||||
* @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1
|
||||
* @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1
|
||||
* @param object_name [in] The Name of the desired Object.
|
||||
*/
|
||||
void Send_WhoHas_Name(
|
||||
int32_t low_limit,
|
||||
int32_t high_limit,
|
||||
const char *object_name)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
BACNET_ADDRESS dest;
|
||||
int bytes_sent = 0;
|
||||
BACNET_WHO_HAS_DATA data;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
/* if we are forbidden to send, don't send! */
|
||||
if (!dcc_communication_enabled())
|
||||
return;
|
||||
/* Who-Has is a global broadcast */
|
||||
datalink_get_broadcast_address(&dest);
|
||||
datalink_get_my_address(&my_address);
|
||||
/* encode the NPDU portion of the packet */
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len =
|
||||
npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address,
|
||||
&npdu_data);
|
||||
|
||||
/* encode the APDU portion of the packet */
|
||||
data.low_limit = low_limit;
|
||||
data.high_limit = high_limit;
|
||||
data.is_object_name = true;
|
||||
characterstring_init_ansi(&data.object.name, object_name);
|
||||
len = whohas_encode_apdu(&Handler_Transmit_Buffer[pdu_len], &data);
|
||||
pdu_len += len;
|
||||
/* send the data */
|
||||
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 Who-Has Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Send a Who-Has request for a device which has a specific Object type and ID.
|
||||
* @ingroup DMDOB
|
||||
* If low_limit and high_limit both are -1, then the device ID range is unlimited.
|
||||
* If low_limit and high_limit have the same non-negative value, then only
|
||||
* that device will respond.
|
||||
* Otherwise, low_limit must be less than high_limit for a range.
|
||||
* @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1
|
||||
* @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1
|
||||
* @param object_type [in] The BACNET_OBJECT_TYPE of the desired Object.
|
||||
* @param object_instance [in] The ID of the desired Object.
|
||||
*/
|
||||
void Send_WhoHas_Object(
|
||||
int32_t low_limit,
|
||||
int32_t high_limit,
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
uint32_t object_instance)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
BACNET_ADDRESS dest;
|
||||
int bytes_sent = 0;
|
||||
BACNET_WHO_HAS_DATA data;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
/* if we are forbidden to send, don't send! */
|
||||
if (!dcc_communication_enabled())
|
||||
return;
|
||||
/* Who-Has is a global broadcast */
|
||||
datalink_get_broadcast_address(&dest);
|
||||
datalink_get_my_address(&my_address);
|
||||
/* encode the NPDU portion of the packet */
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len =
|
||||
npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest, &my_address,
|
||||
&npdu_data);
|
||||
|
||||
/* encode the APDU portion of the packet */
|
||||
data.low_limit = low_limit;
|
||||
data.high_limit = high_limit;
|
||||
data.is_object_name = false;
|
||||
data.object.identifier.type = object_type;
|
||||
data.object.identifier.instance = object_instance;
|
||||
len = whohas_encode_apdu(&Handler_Transmit_Buffer[pdu_len], &data);
|
||||
pdu_len += 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 Who-Has Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "dcc.h"
|
||||
#include "whois.h"
|
||||
#include "bacenum.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_whois.c Send a Who-Is request. */
|
||||
|
||||
/** Send a Who-Is request to a remote network for a specific device, a range,
|
||||
* or any device.
|
||||
* If low_limit and high_limit both are -1, then the range is unlimited.
|
||||
* If low_limit and high_limit have the same non-negative value, then only
|
||||
* that device will respond.
|
||||
* Otherwise, low_limit must be less than high_limit.
|
||||
* @param target_address [in] BACnet address of target router
|
||||
* @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1
|
||||
* @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1
|
||||
*/
|
||||
void Send_WhoIs_To_Network(
|
||||
BACNET_ADDRESS * target_address,
|
||||
int32_t low_limit,
|
||||
int32_t high_limit)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
|
||||
datalink_get_my_address(&my_address);
|
||||
/* encode the NPDU portion of the packet */
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
|
||||
pdu_len =
|
||||
npdu_encode_pdu(&Handler_Transmit_Buffer[0], target_address,
|
||||
&my_address, &npdu_data);
|
||||
/* encode the APDU portion of the packet */
|
||||
len =
|
||||
whois_encode_apdu(&Handler_Transmit_Buffer[pdu_len], low_limit,
|
||||
high_limit);
|
||||
pdu_len += len;
|
||||
bytes_sent =
|
||||
datalink_send_pdu(target_address, &npdu_data,
|
||||
&Handler_Transmit_Buffer[0], pdu_len);
|
||||
#if PRINT_ENABLED
|
||||
if (bytes_sent <= 0)
|
||||
fprintf(stderr, "Failed to Send Who-Is Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Send a global Who-Is request for a specific device, a range, or any device.
|
||||
* If low_limit and high_limit both are -1, then the range is unlimited.
|
||||
* If low_limit and high_limit have the same non-negative value, then only
|
||||
* that device will respond.
|
||||
* Otherwise, low_limit must be less than high_limit.
|
||||
* @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1
|
||||
* @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1
|
||||
*/
|
||||
void Send_WhoIs_Global(
|
||||
int32_t low_limit,
|
||||
int32_t high_limit)
|
||||
{
|
||||
BACNET_ADDRESS dest;
|
||||
|
||||
if (!dcc_communication_enabled())
|
||||
return;
|
||||
|
||||
/* Who-Is is a global broadcast */
|
||||
datalink_get_broadcast_address(&dest);
|
||||
|
||||
Send_WhoIs_To_Network(&dest, low_limit, high_limit);
|
||||
}
|
||||
|
||||
/** Send a local Who-Is request for a specific device, a range, or any device.
|
||||
* If low_limit and high_limit both are -1, then the range is unlimited.
|
||||
* If low_limit and high_limit have the same non-negative value, then only
|
||||
* that device will respond.
|
||||
* Otherwise, low_limit must be less than high_limit.
|
||||
* @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1
|
||||
* @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1
|
||||
*/
|
||||
void Send_WhoIs_Local(
|
||||
int32_t low_limit,
|
||||
int32_t high_limit)
|
||||
{
|
||||
BACNET_ADDRESS dest;
|
||||
char temp[6];
|
||||
int loop;
|
||||
|
||||
if (!dcc_communication_enabled())
|
||||
return;
|
||||
|
||||
/* Who-Is is a global broadcast */
|
||||
datalink_get_broadcast_address(&dest);
|
||||
/* encode the NPDU portion of the packet */
|
||||
|
||||
/* I added this to make it a local broadcast */
|
||||
dest.net = 0;
|
||||
|
||||
/* Not sure why this happens but values are backwards so they need to be reversed */
|
||||
|
||||
temp[0] = dest.mac[3];
|
||||
temp[1] = dest.mac[2];
|
||||
temp[2] = dest.mac[1];
|
||||
temp[3] = dest.mac[0];
|
||||
temp[4] = dest.mac[5];
|
||||
temp[5] = dest.mac[4];
|
||||
|
||||
|
||||
for (loop = 0; loop < 6; loop++) {
|
||||
dest.mac[loop] = temp[loop];
|
||||
}
|
||||
|
||||
Send_WhoIs_To_Network(&dest, low_limit, high_limit);
|
||||
}
|
||||
|
||||
/** Send a Who-Is request to a remote network for a specific device, a range,
|
||||
* or any device.
|
||||
* @ingroup DMDDB
|
||||
* If low_limit and high_limit both are -1, then the range is unlimited.
|
||||
* If low_limit and high_limit have the same non-negative value, then only
|
||||
* that device will respond.
|
||||
* Otherwise, low_limit must be less than high_limit.
|
||||
* @param target_address [in] BACnet address of target router
|
||||
* @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1
|
||||
* @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1
|
||||
*/
|
||||
void Send_WhoIs_Remote(
|
||||
BACNET_ADDRESS * target_address,
|
||||
int32_t low_limit,
|
||||
int32_t high_limit)
|
||||
{
|
||||
if (!dcc_communication_enabled())
|
||||
return;
|
||||
|
||||
Send_WhoIs_To_Network(target_address, low_limit, high_limit);
|
||||
}
|
||||
|
||||
/** Send a global Who-Is request for a specific device, a range, or any device.
|
||||
* @ingroup DMDDB
|
||||
* This was the original Who-Is broadcast but the code was moved to the more
|
||||
* descriptive Send_WhoIs_Global when Send_WhoIs_Local and Send_WhoIsRemote was
|
||||
* added.
|
||||
* If low_limit and high_limit both are -1, then the range is unlimited.
|
||||
* If low_limit and high_limit have the same non-negative value, then only
|
||||
* that device will respond.
|
||||
* Otherwise, low_limit must be less than high_limit.
|
||||
* @param low_limit [in] Device Instance Low Range, 0 - 4,194,303 or -1
|
||||
* @param high_limit [in] Device Instance High Range, 0 - 4,194,303 or -1
|
||||
*/
|
||||
void Send_WhoIs(
|
||||
int32_t low_limit,
|
||||
int32_t high_limit)
|
||||
{
|
||||
Send_WhoIs_Global(low_limit, high_limit);
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "txbuf.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "dcc.h"
|
||||
#include "whois.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "txbuf.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_wp.c Send a Write Property request. */
|
||||
|
||||
/** returns the invoke ID for confirmed request, or zero on failure */
|
||||
uint8_t Send_Write_Property_Request_Data(
|
||||
uint32_t device_id,
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_ID object_property,
|
||||
uint8_t * application_data,
|
||||
int application_data_len,
|
||||
uint8_t priority,
|
||||
uint32_t array_index)
|
||||
{
|
||||
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_WRITE_PROPERTY_DATA data;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
|
||||
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 */
|
||||
data.object_type = object_type;
|
||||
data.object_instance = object_instance;
|
||||
data.object_property = object_property;
|
||||
data.array_index = array_index;
|
||||
data.application_data_len = application_data_len;
|
||||
memcpy(&data.application_data[0], &application_data[0],
|
||||
application_data_len);
|
||||
data.priority = priority;
|
||||
len =
|
||||
wp_encode_apdu(&Handler_Transmit_Buffer[pdu_len], invoke_id,
|
||||
&data);
|
||||
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 WriteProperty Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send WriteProperty Request "
|
||||
"(exceeds destination maximum APDU)!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
|
||||
|
||||
/** Sends a Write Property request.
|
||||
* @ingroup DSWP
|
||||
*
|
||||
* @param device_id [in] ID of the destination device
|
||||
* @param object_type [in] Type of the object whose property is to be written.
|
||||
* @param object_instance [in] Instance # of the object to be written.
|
||||
* @param object_property [in] Property to be written.
|
||||
* @param object_value [in] The value to be written to the property.
|
||||
* @param priority [in] Write priority of 1 (highest) to 16 (lowest)
|
||||
* @param array_index [in] Optional: if the Property is an array,
|
||||
* - 0 for the array size
|
||||
* - 1 to n for individual array members
|
||||
* - BACNET_ARRAY_ALL (~0) for the array value to be ignored (not sent)
|
||||
* @return invoke id of outgoing message, or 0 on failure.
|
||||
*/
|
||||
uint8_t Send_Write_Property_Request(
|
||||
uint32_t device_id,
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_ID object_property,
|
||||
BACNET_APPLICATION_DATA_VALUE * object_value,
|
||||
uint8_t priority,
|
||||
uint32_t array_index)
|
||||
{
|
||||
uint8_t application_data[MAX_APDU] = { 0 };
|
||||
int apdu_len = 0, len = 0;
|
||||
|
||||
while (object_value) {
|
||||
#if PRINT_ENABLED_DEBUG
|
||||
fprintf(stderr, "WriteProperty service: " "%s tag=%d\n",
|
||||
(object_value->context_specific ? "context" : "application"),
|
||||
(int) (object_value->
|
||||
context_specific ? object_value->context_tag : object_value->
|
||||
tag));
|
||||
#endif
|
||||
len = bacapp_encode_data(&application_data[apdu_len], object_value);
|
||||
if ((len + apdu_len) < MAX_APDU) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
object_value = object_value->next;
|
||||
}
|
||||
|
||||
return Send_Write_Property_Request_Data(device_id, object_type,
|
||||
object_instance, object_property, &application_data[0], apdu_len,
|
||||
priority, array_index);
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Daniel Blazevic <daniel.blazevic@gmail.com>
|
||||
* @date 2013
|
||||
* @brief Send Write Property Multiple request
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* Copyright (C) 2013 Daniel Blazevic <daniel.blazevic@gmail.com>
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "address.h"
|
||||
#include "tsm.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "dcc.h"
|
||||
#include "wpm.h"
|
||||
/* some demo stuff needed */
|
||||
#include "handlers.h"
|
||||
#include "sbuf.h"
|
||||
#include "client.h"
|
||||
|
||||
/** @file s_wpm.c Send Write Property Multiple request. */
|
||||
|
||||
/** Sends a Write Property Multiple request.
|
||||
* @param pdu [out] Buffer to build the outgoing message into
|
||||
* @param max_pdu [in] Length of the pdu buffer.
|
||||
* @param device_id [in] ID of the destination device
|
||||
* @param write_access_data [in] Ptr to structure with the linked list of
|
||||
* objects and properties to be written.
|
||||
* @return invoke id of outgoing message, or 0 if device is not bound or no tsm available
|
||||
*/
|
||||
uint8_t Send_Write_Property_Multiple_Request(
|
||||
uint8_t * pdu,
|
||||
size_t max_pdu,
|
||||
uint32_t device_id,
|
||||
BACNET_WRITE_ACCESS_DATA * write_access_data)
|
||||
{
|
||||
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;
|
||||
|
||||
/* 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(&pdu[0], &dest, &my_address,&npdu_data);
|
||||
/* encode the APDU portion of the packet */
|
||||
len =
|
||||
wpm_encode_apdu(&pdu[pdu_len], max_pdu - pdu_len,
|
||||
invoke_id, write_access_data);
|
||||
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, &pdu[0], (uint16_t) pdu_len);
|
||||
bytes_sent =
|
||||
datalink_send_pdu(&dest, &npdu_data,
|
||||
&pdu[0], pdu_len);
|
||||
#if PRINT_ENABLED
|
||||
if (bytes_sent <= 0) {
|
||||
fprintf(stderr,
|
||||
"Failed to Send WritePropertyMultiple Request (%s)!\n",
|
||||
strerror(errno));
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr,
|
||||
"Failed to Send WritePropertyMultiple Request "
|
||||
"(exceeds destination maximum APDU)!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "config.h"
|
||||
#include "datalink.h"
|
||||
|
||||
/** @file txbuf.c Declare the global Transmit Buffer for handler functions. */
|
||||
|
||||
uint8_t Handler_Transmit_Buffer[MAX_PDU] = { 0 };
|
||||
@@ -0,0 +1,39 @@
|
||||
#Makefile to build BACnet Application for the Linux Port
|
||||
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
|
||||
TARGET = baciam
|
||||
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
SRCS = main.c \
|
||||
../object/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 -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map
|
||||
|
||||
include: .depend
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "bactext.h"
|
||||
#include "iam.h"
|
||||
#include "address.h"
|
||||
#include "config.h"
|
||||
#include "bacdef.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "version.h"
|
||||
/* some demo stuff needed */
|
||||
#include "filename.h"
|
||||
#include "handlers.h"
|
||||
#include "client.h"
|
||||
#include "txbuf.h"
|
||||
#include "dlenv.h"
|
||||
|
||||
/* parsed command line parameters */
|
||||
static uint32_t Target_Device_ID = BACNET_MAX_INSTANCE;
|
||||
static uint16_t Target_Vendor_ID = BACNET_VENDOR_ID;
|
||||
static unsigned int Target_Max_APDU = MAX_APDU;
|
||||
static int Target_Segmentation = SEGMENTATION_NONE;
|
||||
/* flag for signalling errors */
|
||||
static bool Error_Detected = false;
|
||||
|
||||
void MyAbortHandler(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id,
|
||||
uint8_t abort_reason,
|
||||
bool server)
|
||||
{
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
(void) server;
|
||||
printf("BACnet Abort: %s\n", bactext_abort_reason_name(abort_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
void MyRejectHandler(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id,
|
||||
uint8_t reject_reason)
|
||||
{
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
printf("BACnet Reject: %s\n", bactext_reject_reason_name(reject_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
static void Init_Service_Handlers(
|
||||
void)
|
||||
{
|
||||
Device_Init(NULL);
|
||||
/* we need to handle who-is
|
||||
to support dynamic device binding to us */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
|
||||
/* set the handler for all the services we don't implement
|
||||
It is required to send the proper reject message... */
|
||||
apdu_set_unrecognized_service_handler_handler
|
||||
(handler_unrecognized_service);
|
||||
/* we must implement read property - it's required! */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
|
||||
handler_read_property);
|
||||
/* handle the reply (request) coming back */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_add);
|
||||
/* handle any errors coming back */
|
||||
apdu_set_abort_handler(MyAbortHandler);
|
||||
apdu_set_reject_handler(MyRejectHandler);
|
||||
}
|
||||
|
||||
static void print_usage(char *filename)
|
||||
{
|
||||
printf("Usage: %s [device-instance vendor-id max-apdu segmentation]\n",
|
||||
filename);
|
||||
printf(" [--dnet][--dadr][--mac]\n");
|
||||
printf(" [--version][--help]\n");
|
||||
}
|
||||
|
||||
static void print_help(char *filename)
|
||||
{
|
||||
printf("Send BACnet I-Am message for a device.\n");
|
||||
printf("--mac A\n"
|
||||
"Optional BACnet mac address."
|
||||
"Valid ranges are from 00 to FF (hex) for MS/TP or ARCNET,\n"
|
||||
"or an IP string with optional port number like 10.1.2.3:47808\n"
|
||||
"or an Ethernet MAC in hex like 00:21:70:7e:32:bb\n"
|
||||
"\n"
|
||||
"--dnet N\n"
|
||||
"Optional BACnet network number N for directed requests.\n"
|
||||
"Valid range is from 0 to 65535 where 0 is the local connection\n"
|
||||
"and 65535 is network broadcast.\n"
|
||||
"\n"
|
||||
"--dadr A\n"
|
||||
"Optional BACnet mac address on the destination BACnet network number.\n"
|
||||
"Valid ranges are from 00 to FF (hex) for MS/TP or ARCNET,\n"
|
||||
"or an IP string with optional port number like 10.1.2.3:47808\n"
|
||||
"or an Ethernet MAC in hex like 00:21:70:7e:32:bb\n"
|
||||
"\n");
|
||||
printf("device-instance:\n"
|
||||
" BACnet device-ID 0..4194303\n"
|
||||
"vendor-id:\n"
|
||||
" Vendor Identifier 0..65535\n"
|
||||
"max-apdu:\n"
|
||||
" Maximum APDU size 50..65535\n"
|
||||
"segmentation:\n"
|
||||
" BACnet Segmentation 0=both, 1=transmit, 2=receive, 3=none\n"
|
||||
"To send an I-Am message for instance=1234 vendor-id=260 max-apdu=480\n"
|
||||
"%s 1234 260 480\n",
|
||||
filename);
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char *argv[])
|
||||
{
|
||||
long dnet = -1;
|
||||
BACNET_MAC_ADDRESS mac = { 0 };
|
||||
BACNET_MAC_ADDRESS adr = { 0 };
|
||||
BACNET_ADDRESS dest = { 0 };
|
||||
bool specific_address = false;
|
||||
int argi = 0;
|
||||
unsigned int target_args = 0;
|
||||
char *filename = NULL;
|
||||
|
||||
filename = filename_remove_path(argv[0]);
|
||||
for (argi = 1; argi < argc; argi++) {
|
||||
if (strcmp(argv[argi], "--help") == 0) {
|
||||
print_usage(filename);
|
||||
print_help(filename);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(argv[argi], "--version") == 0) {
|
||||
printf("%s %s\n", filename, BACNET_VERSION_TEXT);
|
||||
printf("Copyright (C) 2016 by Steve Karg and others.\n"
|
||||
"This is free software; see the source for copying conditions.\n"
|
||||
"There is NO warranty; not even for MERCHANTABILITY or\n"
|
||||
"FITNESS FOR A PARTICULAR PURPOSE.\n");
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(argv[argi], "--mac") == 0) {
|
||||
if (++argi < argc) {
|
||||
if (address_mac_from_ascii(&mac, argv[argi])) {
|
||||
specific_address = true;
|
||||
}
|
||||
}
|
||||
} else if (strcmp(argv[argi], "--dnet") == 0) {
|
||||
if (++argi < argc) {
|
||||
dnet = strtol(argv[argi], NULL, 0);
|
||||
if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) {
|
||||
specific_address = true;
|
||||
}
|
||||
}
|
||||
} else if (strcmp(argv[argi], "--dadr") == 0) {
|
||||
if (++argi < argc) {
|
||||
if (address_mac_from_ascii(&adr, argv[argi])) {
|
||||
specific_address = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (target_args == 0) {
|
||||
Target_Device_ID = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else if (target_args == 1) {
|
||||
Target_Vendor_ID = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else if (target_args == 2) {
|
||||
Target_Max_APDU = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else if (target_args == 3) {
|
||||
Target_Segmentation = strtol(argv[argi], NULL, 0);
|
||||
target_args++;
|
||||
} else {
|
||||
print_usage(filename);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
address_init();
|
||||
if (specific_address) {
|
||||
if (adr.len && mac.len) {
|
||||
memcpy(&dest.mac[0], &mac.adr[0], mac.len);
|
||||
dest.mac_len = mac.len;
|
||||
memcpy(&dest.adr[0], &adr.adr[0], adr.len);
|
||||
dest.len = adr.len;
|
||||
if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) {
|
||||
dest.net = dnet;
|
||||
} else {
|
||||
dest.net = BACNET_BROADCAST_NETWORK;
|
||||
}
|
||||
} else if (mac.len) {
|
||||
memcpy(&dest.mac[0], &mac.adr[0], mac.len);
|
||||
dest.mac_len = mac.len;
|
||||
dest.len = 0;
|
||||
if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) {
|
||||
dest.net = dnet;
|
||||
} else {
|
||||
dest.net = 0;
|
||||
}
|
||||
} else {
|
||||
if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) {
|
||||
dest.net = dnet;
|
||||
} else {
|
||||
dest.net = BACNET_BROADCAST_NETWORK;
|
||||
}
|
||||
dest.mac_len = 0;
|
||||
dest.len = 0;
|
||||
}
|
||||
}
|
||||
/* setup my info */
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
Init_Service_Handlers();
|
||||
address_init();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
/* send the request */
|
||||
Send_I_Am_To_Network(&dest,
|
||||
Target_Device_ID,
|
||||
Target_Max_APDU,
|
||||
Target_Segmentation,
|
||||
Target_Vendor_ID);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
#
|
||||
# Simple makefile to build an executable for Win32
|
||||
#
|
||||
# This makefile assumes Borland bcc32 development environment
|
||||
# on Windows NT/9x/2000/XP
|
||||
#
|
||||
|
||||
!ifndef BORLAND_DIR
|
||||
BORLAND_DIR_Not_Defined:
|
||||
@echo .
|
||||
@echo You must define environment variable BORLAND_DIR to compile.
|
||||
!endif
|
||||
|
||||
# target
|
||||
PRODUCT = baciam
|
||||
PRODUCT_EXE = $(PRODUCT).exe
|
||||
|
||||
# tools
|
||||
CC = $(BORLAND_DIR)\bin\bcc32
|
||||
MAKE=$(BORLAND_DIR)\bin\make.exe
|
||||
#LINK = $(BORLAND_DIR)\bin\tlink32
|
||||
LINK = $(BORLAND_DIR)\bin\ilink32
|
||||
|
||||
BACNET_LIB_DIR = ..\..\lib
|
||||
BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib
|
||||
|
||||
# directories
|
||||
BACNET_PORT = ..\..\ports\win32
|
||||
BACNET_INCLUDE = ..\..\include
|
||||
BACNET_OBJECT = ..\object
|
||||
BACNET_HANDLER = ..\handler
|
||||
INCLUDES = \
|
||||
-I$(BACNET_INCLUDE) \
|
||||
-I$(BACNET_PORT) \
|
||||
-I$(BACNET_OBJECT) \
|
||||
-I$(BACNET_HANDLER) \
|
||||
-I$(BORLAND_DIR)\include
|
||||
|
||||
#
|
||||
BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL
|
||||
#BACDL_DEFINE=-DBACDL_MSTP=1
|
||||
BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1
|
||||
DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE)
|
||||
|
||||
SRCS = main.c \
|
||||
$(BACNET_OBJECT)\device-client.c
|
||||
|
||||
OBJS = $(SRCS:.c=.obj)
|
||||
|
||||
#
|
||||
# Compiler definitions
|
||||
#
|
||||
BCC_CFG = bcc32.cfg
|
||||
|
||||
#
|
||||
# Include directories
|
||||
#
|
||||
CFLAGS = $(INCLUDES) $(DEFINES)
|
||||
|
||||
#
|
||||
# Libraries
|
||||
#
|
||||
C_LIB_DIR = $(BORLAND_DIR)\lib
|
||||
|
||||
LIBS = $(BACNET_LIB) \
|
||||
$(C_LIB_DIR)\IMPORT32.lib \
|
||||
$(C_LIB_DIR)\CW32MT.lib \
|
||||
|
||||
#
|
||||
# Main target
|
||||
#
|
||||
# This should be the first one in the makefile
|
||||
|
||||
all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE)
|
||||
del $(BCC_CFG)
|
||||
|
||||
install: $(PRODUCT_EXE)
|
||||
copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE)
|
||||
|
||||
# Linker specific: the link below is for BCC linker/compiler. If you link
|
||||
# with a different linker - please change accordingly.
|
||||
#
|
||||
|
||||
# need a temp response file (@&&| ... |) because command line is too long
|
||||
# $** lists each dependency
|
||||
# $< target name
|
||||
# $* target name without extension
|
||||
$(PRODUCT_EXE) : $(OBJS)
|
||||
@echo Running Linker for $(PRODUCT_EXE)
|
||||
$(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&|
|
||||
$(BORLAND_DIR)\lib\c0x32.obj $**
|
||||
$<
|
||||
$*.map
|
||||
$(LIBS)
|
||||
|
|
||||
|
||||
#
|
||||
# Utilities
|
||||
|
||||
clean :
|
||||
del $(OBJS)
|
||||
del $(PRODUCT_EXE)
|
||||
del $(PRODUCT).map
|
||||
del $(PRODUCT).ilc
|
||||
del $(PRODUCT).ild
|
||||
del $(PRODUCT).ilf
|
||||
del $(PRODUCT).ils
|
||||
del $(PRODUCT).tds
|
||||
del $(BCC_CFG)
|
||||
|
||||
#
|
||||
# Generic rules
|
||||
#
|
||||
.SUFFIXES: .cpp .c .sbr .obj
|
||||
|
||||
#
|
||||
# cc generic rule
|
||||
#
|
||||
.c.obj:
|
||||
$(CC) +$(BCC_CFG) -o$@ $<
|
||||
|
||||
# Compiler configuration file
|
||||
$(BCC_CFG) :
|
||||
Copy &&|
|
||||
$(CFLAGS)
|
||||
-c
|
||||
-y #include line numbers in OBJ's
|
||||
-v #include debug info
|
||||
-w+ #turn on all warnings
|
||||
-Od #disable all optimizations
|
||||
#-a4 #32 bit data alignment
|
||||
#-M # generate link map
|
||||
#-ls # linker options
|
||||
#-WM- #not multithread
|
||||
-WM #multithread
|
||||
-w-aus # ignore warning assigned a value that is never used
|
||||
-w-sig # ignore warning conversion may lose sig digits
|
||||
| $@
|
||||
|
||||
# EOF: makefile
|
||||
@@ -0,0 +1,39 @@
|
||||
#Makefile to build BACnet Application for the Linux Port
|
||||
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
|
||||
TARGET = baciamr
|
||||
|
||||
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 -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map
|
||||
|
||||
include: .depend
|
||||
@@ -0,0 +1,179 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "bactext.h"
|
||||
#include "iam.h"
|
||||
#include "address.h"
|
||||
#include "config.h"
|
||||
#include "bacdef.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "version.h"
|
||||
/* some demo stuff needed */
|
||||
#include "filename.h"
|
||||
#include "handlers.h"
|
||||
#include "client.h"
|
||||
#include "txbuf.h"
|
||||
#include "dlenv.h"
|
||||
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void MyRejectHandler(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id,
|
||||
uint8_t reject_reason)
|
||||
{
|
||||
/* FIXME: verify src and invoke id */
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
printf("BACnet Reject: %s\n", bactext_reject_reason_name(reject_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
static void Init_Service_Handlers(
|
||||
void)
|
||||
{
|
||||
Device_Init(NULL);
|
||||
/* we need to handle who-is
|
||||
to support dynamic device binding to us */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
|
||||
/* set the handler for all the services we don't implement
|
||||
It is required to send the proper reject message... */
|
||||
apdu_set_unrecognized_service_handler_handler
|
||||
(handler_unrecognized_service);
|
||||
/* we must implement read property - it's required! */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
|
||||
handler_read_property);
|
||||
/* handle the reply (request) coming back */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_add);
|
||||
/* handle any errors coming back */
|
||||
apdu_set_abort_handler(MyAbortHandler);
|
||||
apdu_set_reject_handler(MyRejectHandler);
|
||||
}
|
||||
|
||||
static void print_usage(char *filename)
|
||||
{
|
||||
printf("Usage: %s DNET [DNET] [DNET] [...]\n", filename);
|
||||
printf(" [--version][--help]\n");
|
||||
}
|
||||
|
||||
static void print_help(char *filename)
|
||||
{
|
||||
printf("Send BACnet I-Am-Router-To-Network message for \n"
|
||||
"one or more networks.\n" "\nDNET:\n"
|
||||
"BACnet destination network number 0-65534\n"
|
||||
"To send a I-Am-Router-To-Network message for DNET 86:\n"
|
||||
"%s 86\n"
|
||||
"To send a I-Am-Router-To-Network message for multiple DNETs\n"
|
||||
"use the following command:\n" "%s 86 42 24 14\n",
|
||||
filename, filename);
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char *argv[])
|
||||
{
|
||||
unsigned arg_count = 0;
|
||||
int argi = 0;
|
||||
char *filename = NULL;
|
||||
|
||||
filename = filename_remove_path(argv[0]);
|
||||
for (argi = 1; argi < argc; argi++) {
|
||||
if (strcmp(argv[argi], "--help") == 0) {
|
||||
print_usage(filename);
|
||||
print_help(filename);
|
||||
exit(0);
|
||||
}
|
||||
if (strcmp(argv[argi], "--version") == 0) {
|
||||
printf("%s %s\n", filename, BACNET_VERSION_TEXT);
|
||||
printf("Copyright (C) 2014 by Steve Karg and others.\n"
|
||||
"This is free software; see the source for copying conditions.\n"
|
||||
"There is NO warranty; not even for MERCHANTABILITY or\n"
|
||||
"FITNESS FOR A PARTICULAR PURPOSE.\n");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
if (argc < 2) {
|
||||
print_usage(filename);
|
||||
return 0;
|
||||
}
|
||||
/* decode the command line parameters */
|
||||
if (argc > 1) {
|
||||
for (arg_count = 1; arg_count < argc; arg_count++) {
|
||||
if (arg_count > MAX_ROUTER_DNETS) {
|
||||
fprintf(stderr, "Limited to %u DNETS. Sorry!\n",
|
||||
MAX_ROUTER_DNETS);
|
||||
break;
|
||||
}
|
||||
Target_Router_Networks[arg_count - 1] =
|
||||
strtol(argv[arg_count], NULL, 0);
|
||||
/* mark the end of list */
|
||||
Target_Router_Networks[arg_count] = -1;
|
||||
/* invalid DNET? */
|
||||
if (Target_Router_Networks[arg_count - 1] >= 65535) {
|
||||
fprintf(stderr, "DNET=%u - it must be less than %u\n",
|
||||
Target_Router_Networks[arg_count - 1], 65535);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* setup my info */
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
Init_Service_Handlers();
|
||||
address_init();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
/* send the request */
|
||||
Send_I_Am_Router_To_Network(Target_Router_Networks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
#
|
||||
# Simple makefile to build an executable for Win32
|
||||
#
|
||||
# This makefile assumes Borland bcc32 development environment
|
||||
# on Windows NT/9x/2000/XP
|
||||
#
|
||||
|
||||
!ifndef BORLAND_DIR
|
||||
BORLAND_DIR_Not_Defined:
|
||||
@echo .
|
||||
@echo You must define environment variable BORLAND_DIR to compile.
|
||||
!endif
|
||||
|
||||
# target
|
||||
PRODUCT = baciamr
|
||||
PRODUCT_EXE = $(PRODUCT).exe
|
||||
|
||||
# tools
|
||||
CC = $(BORLAND_DIR)\bin\bcc32
|
||||
MAKE=$(BORLAND_DIR)\bin\make.exe
|
||||
#LINK = $(BORLAND_DIR)\bin\tlink32
|
||||
LINK = $(BORLAND_DIR)\bin\ilink32
|
||||
|
||||
BACNET_LIB_DIR = ..\..\lib
|
||||
BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib
|
||||
|
||||
# directories
|
||||
BACNET_PORT = ..\..\ports\win32
|
||||
BACNET_INCLUDE = ..\..\include
|
||||
BACNET_OBJECT = ..\object
|
||||
BACNET_HANDLER = ..\handler
|
||||
INCLUDES = \
|
||||
-I$(BACNET_INCLUDE) \
|
||||
-I$(BACNET_PORT) \
|
||||
-I$(BACNET_OBJECT) \
|
||||
-I$(BACNET_HANDLER) \
|
||||
-I$(BORLAND_DIR)\include
|
||||
|
||||
#
|
||||
BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL
|
||||
#BACDL_DEFINE=-DBACDL_MSTP=1
|
||||
BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1
|
||||
DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE)
|
||||
|
||||
SRCS = main.c \
|
||||
$(BACNET_OBJECT)\device-client.c
|
||||
|
||||
OBJS = $(SRCS:.c=.obj)
|
||||
|
||||
#
|
||||
# Compiler definitions
|
||||
#
|
||||
BCC_CFG = bcc32.cfg
|
||||
|
||||
#
|
||||
# Include directories
|
||||
#
|
||||
CFLAGS = $(INCLUDES) $(DEFINES)
|
||||
|
||||
#
|
||||
# Libraries
|
||||
#
|
||||
C_LIB_DIR = $(BORLAND_DIR)\lib
|
||||
|
||||
LIBS = $(BACNET_LIB) \
|
||||
$(C_LIB_DIR)\IMPORT32.lib \
|
||||
$(C_LIB_DIR)\CW32MT.lib \
|
||||
|
||||
#
|
||||
# Main target
|
||||
#
|
||||
# This should be the first one in the makefile
|
||||
|
||||
all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE)
|
||||
del $(BCC_CFG)
|
||||
|
||||
install: $(PRODUCT_EXE)
|
||||
copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE)
|
||||
|
||||
# Linker specific: the link below is for BCC linker/compiler. If you link
|
||||
# with a different linker - please change accordingly.
|
||||
#
|
||||
|
||||
# need a temp response file (@&&| ... |) because command line is too long
|
||||
# $** lists each dependency
|
||||
# $< target name
|
||||
# $* target name without extension
|
||||
$(PRODUCT_EXE) : $(OBJS)
|
||||
@echo Running Linker for $(PRODUCT_EXE)
|
||||
$(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&|
|
||||
$(BORLAND_DIR)\lib\c0x32.obj $**
|
||||
$<
|
||||
$*.map
|
||||
$(LIBS)
|
||||
|
|
||||
|
||||
#
|
||||
# Utilities
|
||||
|
||||
clean :
|
||||
del $(OBJS)
|
||||
del $(PRODUCT_EXE)
|
||||
del $(PRODUCT).map
|
||||
del $(PRODUCT).ilc
|
||||
del $(PRODUCT).ild
|
||||
del $(PRODUCT).ilf
|
||||
del $(PRODUCT).ils
|
||||
del $(PRODUCT).tds
|
||||
del $(BCC_CFG)
|
||||
|
||||
#
|
||||
# Generic rules
|
||||
#
|
||||
.SUFFIXES: .cpp .c .sbr .obj
|
||||
|
||||
#
|
||||
# cc generic rule
|
||||
#
|
||||
.c.obj:
|
||||
$(CC) +$(BCC_CFG) -o$@ $<
|
||||
|
||||
# Compiler configuration file
|
||||
$(BCC_CFG) :
|
||||
Copy &&|
|
||||
$(CFLAGS)
|
||||
-c
|
||||
-y #include line numbers in OBJ's
|
||||
-v #include debug info
|
||||
-w+ #turn on all warnings
|
||||
-Od #disable all optimizations
|
||||
#-a4 #32 bit data alignment
|
||||
#-M # generate link map
|
||||
#-ls # linker options
|
||||
#-WM- #not multithread
|
||||
-WM #multithread
|
||||
-w-aus # ignore warning assigned a value that is never used
|
||||
-w-sig # ignore warning conversion may lose sig digits
|
||||
| $@
|
||||
|
||||
# EOF: makefile
|
||||
@@ -0,0 +1,39 @@
|
||||
#Makefile to build BACnet Application for the Linux Port
|
||||
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
|
||||
TARGET = bacinitr
|
||||
|
||||
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 -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map
|
||||
|
||||
include: .depend
|
||||
@@ -0,0 +1,393 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 "bactext.h"
|
||||
#include "iam.h"
|
||||
#include "address.h"
|
||||
#include "config.h"
|
||||
#include "bacdef.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "device.h"
|
||||
#include "datalink.h"
|
||||
#include "version.h"
|
||||
/* some demo stuff needed */
|
||||
#ifndef DEBUG_ENABLED
|
||||
#define DEBUG_ENABLED 0
|
||||
#endif
|
||||
#include "debug.h"
|
||||
#include "filename.h"
|
||||
#include "handlers.h"
|
||||
#include "client.h"
|
||||
#include "txbuf.h"
|
||||
#include "dlenv.h"
|
||||
|
||||
/* buffer used for receive */
|
||||
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
|
||||
|
||||
/* target address */
|
||||
static BACNET_ADDRESS Target_Router_Address;
|
||||
/* static BACNET_ROUTER_PORT *Target_Router_Port_List; */
|
||||
|
||||
#define VIRTUAL_DNET 2709 /* your choice of number here */
|
||||
/** The list of DNETs that our router can reach.
|
||||
* Only one entry since we don't support downstream routers.
|
||||
*/
|
||||
int DNET_list[2] = {
|
||||
VIRTUAL_DNET, -1 /* Need -1 terminator */
|
||||
};
|
||||
|
||||
static bool Error_Detected = false;
|
||||
|
||||
static void MyAbortHandler(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id,
|
||||
uint8_t abort_reason,
|
||||
bool server)
|
||||
{
|
||||
/* FIXME: verify src and invoke id */
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
(void) server;
|
||||
printf("BACnet Abort: %s\n", bactext_abort_reason_name(abort_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
static void MyRejectHandler(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t invoke_id,
|
||||
uint8_t reject_reason)
|
||||
{
|
||||
/* FIXME: verify src and invoke id */
|
||||
(void) src;
|
||||
(void) invoke_id;
|
||||
printf("BACnet Reject: %s\n", bactext_reject_reason_name(reject_reason));
|
||||
Error_Detected = true;
|
||||
}
|
||||
|
||||
static void My_Router_Handler(
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_NPDU_DATA * npdu_data,
|
||||
uint8_t * npdu, /* PDU data */
|
||||
uint16_t npdu_len)
|
||||
{
|
||||
uint16_t npdu_offset = 0;
|
||||
uint16_t dnet = 0;
|
||||
uint16_t len = 0;
|
||||
uint16_t j = 0;
|
||||
unsigned port_mappings = 0;
|
||||
unsigned port_id = 0;
|
||||
unsigned port_info_len = 0;
|
||||
|
||||
switch (npdu_data->network_message_type) {
|
||||
case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK:
|
||||
break;
|
||||
case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK:
|
||||
break;
|
||||
case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK:
|
||||
break;
|
||||
case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK:
|
||||
break;
|
||||
case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK:
|
||||
break;
|
||||
case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK:
|
||||
break;
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE:
|
||||
break;
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
|
||||
printf("Initialize-Routing-Table-Ack from ");
|
||||
for (j = 0; j < MAX_MAC_LEN; j++) {
|
||||
if (j < src->mac_len) {
|
||||
printf("%02X", src->mac[j]);
|
||||
}
|
||||
}
|
||||
if (npdu_len >= 1) {
|
||||
port_mappings = npdu[npdu_offset];
|
||||
printf("\nPort Mappings: %u\n", port_mappings);
|
||||
npdu_offset++;
|
||||
npdu_len--;
|
||||
}
|
||||
while (npdu_len) {
|
||||
if (npdu_len >= 2) {
|
||||
len = decode_unsigned16(&npdu[npdu_offset], &dnet);
|
||||
printf("DNET=%hu, ", dnet);
|
||||
npdu_offset += len;
|
||||
npdu_len -= len;
|
||||
}
|
||||
if (npdu_len >= 1) {
|
||||
port_id = npdu[npdu_offset];
|
||||
printf("Port ID=%u, ", port_id);
|
||||
npdu_offset++;
|
||||
npdu_len--;
|
||||
}
|
||||
if (npdu_len >= 1) {
|
||||
port_info_len = npdu[npdu_offset];
|
||||
printf("Port Info Length=%u, ", port_info_len);
|
||||
npdu_offset++;
|
||||
npdu_len--;
|
||||
} else {
|
||||
port_info_len = 0;
|
||||
}
|
||||
if (port_info_len) {
|
||||
printf("Port Info=\"");
|
||||
while (port_info_len) {
|
||||
if (npdu_len >= 1) {
|
||||
printf("%02X", npdu[npdu_offset]);
|
||||
npdu_offset++;
|
||||
npdu_len--;
|
||||
}
|
||||
}
|
||||
printf("\"");
|
||||
}
|
||||
if (npdu_len) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
break;
|
||||
case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK:
|
||||
break;
|
||||
case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void My_NPDU_Handler(
|
||||
BACNET_ADDRESS * src, /* source address */
|
||||
uint8_t * pdu, /* PDU data */
|
||||
uint16_t pdu_len)
|
||||
{ /* length PDU */
|
||||
int apdu_offset = 0;
|
||||
BACNET_ADDRESS dest = { 0 };
|
||||
BACNET_NPDU_DATA npdu_data = { 0 };
|
||||
|
||||
apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data);
|
||||
if (npdu_data.network_layer_message) {
|
||||
if (apdu_offset <= pdu_len) {
|
||||
My_Router_Handler(src, &npdu_data, &pdu[apdu_offset],
|
||||
(uint16_t) (pdu_len - apdu_offset));
|
||||
}
|
||||
} else if ((apdu_offset > 0) && (apdu_offset <= pdu_len)) {
|
||||
if ((npdu_data.protocol_version == BACNET_PROTOCOL_VERSION) &&
|
||||
((dest.net == 0) || (dest.net == BACNET_BROADCAST_NETWORK))) {
|
||||
/* only handle the version that we know how to handle */
|
||||
/* and we are not a router, so ignore messages with
|
||||
routing information cause they are not for us */
|
||||
apdu_handler(src, &pdu[apdu_offset],
|
||||
(uint16_t) (pdu_len - apdu_offset));
|
||||
} else {
|
||||
if (dest.net) {
|
||||
debug_printf("NPDU: DNET=%d. Discarded!\n", dest.net);
|
||||
} else {
|
||||
debug_printf("NPDU: BACnet Protocol Version=%d. Discarded!\n",
|
||||
npdu_data.protocol_version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void Init_Service_Handlers(
|
||||
void)
|
||||
{
|
||||
Device_Init(NULL);
|
||||
/* we need to handle who-is
|
||||
to support dynamic device binding to us */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
|
||||
/* set the handler for all the services we don't implement
|
||||
It is required to send the proper reject message... */
|
||||
apdu_set_unrecognized_service_handler_handler
|
||||
(handler_unrecognized_service);
|
||||
/* we must implement read property - it's required! */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
|
||||
handler_read_property);
|
||||
/* handle the reply (request) coming back */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_add);
|
||||
/* handle any errors coming back */
|
||||
apdu_set_abort_handler(MyAbortHandler);
|
||||
apdu_set_reject_handler(MyRejectHandler);
|
||||
}
|
||||
|
||||
static void print_usage(char *filename)
|
||||
{
|
||||
printf("Usage: %s address [DNET ID Len Info]\n", filename);
|
||||
printf(" [--version][--help]\n");
|
||||
}
|
||||
|
||||
static void print_help(char *filename)
|
||||
{
|
||||
printf("Send BACnet Initialize-Routing-Table message to a network\n"
|
||||
"and wait for responses. Displays their network information.\n"
|
||||
"\n" "address:\n"
|
||||
"MAC address in xx:xx:xx:xx:xx:xx format or IP x.x.x.x:port\n"
|
||||
"DNET ID Len Info:\n" "Port-info data:\n" " DNET:\n"
|
||||
" Destination network number 0-65534\n" " ID:\n"
|
||||
" Port Identifier number 0-255\n" " Info:\n"
|
||||
" Octet string of data, up to 255 octets\n"
|
||||
"To query the complete routing table, do not include any port-info.\n"
|
||||
"To query using Initialize-Routing-Table message to 192.168.0.18:\n"
|
||||
"%s 192.168.0.18:47808\n", filename);
|
||||
}
|
||||
|
||||
static void address_parse(
|
||||
BACNET_ADDRESS * dst,
|
||||
int argc,
|
||||
char *argv[])
|
||||
{
|
||||
unsigned mac[6];
|
||||
unsigned port;
|
||||
int count = 0;
|
||||
int index = 0;
|
||||
|
||||
if (argc > 0) {
|
||||
count =
|
||||
sscanf(argv[0], "%u.%u.%u.%u:%u", &mac[0], &mac[1], &mac[2],
|
||||
&mac[3], &port);
|
||||
if (count == 5) {
|
||||
dst->mac_len = 6;
|
||||
for (index = 0; index < 4; index++) {
|
||||
dst->mac[index] = mac[index];
|
||||
}
|
||||
encode_unsigned16(&dst->mac[4], port);
|
||||
} else {
|
||||
count =
|
||||
sscanf(argv[0], "%x:%x:%x:%x:%x:%x", &mac[0], &mac[1], &mac[2],
|
||||
&mac[3], &mac[4], &mac[5]);
|
||||
dst->mac_len = count;
|
||||
for (index = 0; index < MAX_MAC_LEN; index++) {
|
||||
if (index < count) {
|
||||
dst->mac[index] = mac[index];
|
||||
} else {
|
||||
dst->mac[index] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dst->net = 0;
|
||||
dst->len = 0;
|
||||
for (index = 0; index < MAX_MAC_LEN; index++) {
|
||||
dst->adr[index] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char *argv[])
|
||||
{
|
||||
BACNET_ADDRESS src = {
|
||||
0
|
||||
}; /* address where message came from */
|
||||
uint16_t pdu_len = 0;
|
||||
unsigned timeout = 100; /* milliseconds */
|
||||
time_t total_seconds = 0;
|
||||
time_t elapsed_seconds = 0;
|
||||
time_t last_seconds = 0;
|
||||
time_t current_seconds = 0;
|
||||
time_t timeout_seconds = 0;
|
||||
int argi = 0;
|
||||
char *filename = NULL;
|
||||
|
||||
filename = filename_remove_path(argv[0]);
|
||||
for (argi = 1; argi < argc; argi++) {
|
||||
if (strcmp(argv[argi], "--help") == 0) {
|
||||
print_usage(filename);
|
||||
print_help(filename);
|
||||
exit(0);
|
||||
}
|
||||
if (strcmp(argv[argi], "--version") == 0) {
|
||||
printf("%s %s\n", filename, BACNET_VERSION_TEXT);
|
||||
printf("Copyright (C) 2014 by Steve Karg and others.\n"
|
||||
"This is free software; see the source for copying conditions.\n"
|
||||
"There is NO warranty; not even for MERCHANTABILITY or\n"
|
||||
"FITNESS FOR A PARTICULAR PURPOSE.\n");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
if (argc < 2) {
|
||||
print_usage(filename);
|
||||
return 0;
|
||||
}
|
||||
if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) {
|
||||
return 0;
|
||||
}
|
||||
/* decode the command line parameters */
|
||||
address_parse(&Target_Router_Address, argc - 1, &argv[1]);
|
||||
if (argc > 2) {
|
||||
/* FIXME: add port info parse */
|
||||
/* BACNET_ROUTER_PORT *router_port_list
|
||||
Target_Router_Port_List
|
||||
ports_parse(&router_port[0], argc-2, &argv[2]);
|
||||
Target_Router_Port_List = router_port[0];
|
||||
*/
|
||||
}
|
||||
/* setup my info */
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
Init_Service_Handlers();
|
||||
address_init();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
/* configure the timeout values */
|
||||
last_seconds = time(NULL);
|
||||
timeout_seconds = apdu_timeout() / 1000;
|
||||
/* send the request */
|
||||
Send_Initialize_Routing_Table(&Target_Router_Address, DNET_list);
|
||||
/* loop forever */
|
||||
for (;;) {
|
||||
/* increment timer - exit if timed out */
|
||||
current_seconds = time(NULL);
|
||||
/* returns 0 bytes on timeout */
|
||||
pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
|
||||
/* process */
|
||||
if (pdu_len) {
|
||||
My_NPDU_Handler(&src, &Rx_Buf[0], pdu_len);
|
||||
}
|
||||
if (Error_Detected)
|
||||
break;
|
||||
/* increment timer - exit if timed out */
|
||||
elapsed_seconds = current_seconds - last_seconds;
|
||||
if (elapsed_seconds) {
|
||||
#if defined(BACDL_BIP) && BBMD_ENABLED
|
||||
bvlc_maintenance_timer(elapsed_seconds);
|
||||
#endif
|
||||
}
|
||||
total_seconds += elapsed_seconds;
|
||||
if (total_seconds > timeout_seconds)
|
||||
break;
|
||||
/* keep track of time for next check */
|
||||
last_seconds = current_seconds;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
#
|
||||
# Simple makefile to build an executable for Win32
|
||||
#
|
||||
# This makefile assumes Borland bcc32 development environment
|
||||
# on Windows NT/9x/2000/XP
|
||||
#
|
||||
|
||||
!ifndef BORLAND_DIR
|
||||
BORLAND_DIR_Not_Defined:
|
||||
@echo .
|
||||
@echo You must define environment variable BORLAND_DIR to compile.
|
||||
!endif
|
||||
|
||||
# target
|
||||
PRODUCT = bacinitr
|
||||
PRODUCT_EXE = $(PRODUCT).exe
|
||||
|
||||
# tools
|
||||
CC = $(BORLAND_DIR)\bin\bcc32
|
||||
MAKE=$(BORLAND_DIR)\bin\make.exe
|
||||
#LINK = $(BORLAND_DIR)\bin\tlink32
|
||||
LINK = $(BORLAND_DIR)\bin\ilink32
|
||||
|
||||
BACNET_LIB_DIR = ..\..\lib
|
||||
BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib
|
||||
|
||||
# directories
|
||||
BACNET_PORT = ..\..\ports\win32
|
||||
BACNET_INCLUDE = ..\..\include
|
||||
BACNET_OBJECT = ..\object
|
||||
BACNET_HANDLER = ..\handler
|
||||
INCLUDES = \
|
||||
-I$(BACNET_INCLUDE) \
|
||||
-I$(BACNET_PORT) \
|
||||
-I$(BACNET_OBJECT) \
|
||||
-I$(BACNET_HANDLER) \
|
||||
-I$(BORLAND_DIR)\include
|
||||
|
||||
#
|
||||
BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL
|
||||
#BACDL_DEFINE=-DBACDL_MSTP=1
|
||||
BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1
|
||||
DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE)
|
||||
|
||||
SRCS = main.c \
|
||||
$(BACNET_OBJECT)\device-client.c
|
||||
|
||||
OBJS = $(SRCS:.c=.obj)
|
||||
|
||||
#
|
||||
# Compiler definitions
|
||||
#
|
||||
BCC_CFG = bcc32.cfg
|
||||
|
||||
#
|
||||
# Include directories
|
||||
#
|
||||
CFLAGS = $(INCLUDES) $(DEFINES)
|
||||
|
||||
#
|
||||
# Libraries
|
||||
#
|
||||
C_LIB_DIR = $(BORLAND_DIR)\lib
|
||||
|
||||
LIBS = $(BACNET_LIB) \
|
||||
$(C_LIB_DIR)\IMPORT32.lib \
|
||||
$(C_LIB_DIR)\CW32MT.lib \
|
||||
|
||||
#
|
||||
# Main target
|
||||
#
|
||||
# This should be the first one in the makefile
|
||||
|
||||
all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE)
|
||||
del $(BCC_CFG)
|
||||
|
||||
install: $(PRODUCT_EXE)
|
||||
copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE)
|
||||
|
||||
# Linker specific: the link below is for BCC linker/compiler. If you link
|
||||
# with a different linker - please change accordingly.
|
||||
#
|
||||
|
||||
# need a temp response file (@&&| ... |) because command line is too long
|
||||
# $** lists each dependency
|
||||
# $< target name
|
||||
# $* target name without extension
|
||||
$(PRODUCT_EXE) : $(OBJS)
|
||||
@echo Running Linker for $(PRODUCT_EXE)
|
||||
$(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&|
|
||||
$(BORLAND_DIR)\lib\c0x32.obj $**
|
||||
$<
|
||||
$*.map
|
||||
$(LIBS)
|
||||
|
|
||||
|
||||
#
|
||||
# Utilities
|
||||
|
||||
clean :
|
||||
del $(OBJS)
|
||||
del $(PRODUCT_EXE)
|
||||
del $(PRODUCT).map
|
||||
del $(PRODUCT).ilc
|
||||
del $(PRODUCT).ild
|
||||
del $(PRODUCT).ilf
|
||||
del $(PRODUCT).ils
|
||||
del $(PRODUCT).tds
|
||||
del $(BCC_CFG)
|
||||
|
||||
#
|
||||
# Generic rules
|
||||
#
|
||||
.SUFFIXES: .cpp .c .sbr .obj
|
||||
|
||||
#
|
||||
# cc generic rule
|
||||
#
|
||||
.c.obj:
|
||||
$(CC) +$(BCC_CFG) -o$@ $<
|
||||
|
||||
# Compiler configuration file
|
||||
$(BCC_CFG) :
|
||||
Copy &&|
|
||||
$(CFLAGS)
|
||||
-c
|
||||
-y #include line numbers in OBJ's
|
||||
-v #include debug info
|
||||
-w+ #turn on all warnings
|
||||
-Od #disable all optimizations
|
||||
#-a4 #32 bit data alignment
|
||||
#-M # generate link map
|
||||
#-ls # linker options
|
||||
#-WM- #not multithread
|
||||
-WM #multithread
|
||||
-w-aus # ignore warning assigned a value that is never used
|
||||
-w-sig # ignore warning conversion may lose sig digits
|
||||
| $@
|
||||
|
||||
# EOF: makefile
|
||||
@@ -0,0 +1,48 @@
|
||||
#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 = mstpcap
|
||||
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
# This demo seems to be a little unique
|
||||
DEFINES = $(BACNET_DEFINES) -DBACDL_MSTP
|
||||
BACNET_SOURCE_DIR = ../../src
|
||||
|
||||
SRCS = main.c \
|
||||
${BACNET_PORT_DIR}/rs485.c \
|
||||
${BACNET_PORT_DIR}/timer.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}/bacdcode.c \
|
||||
${BACNET_SOURCE_DIR}/iam.c \
|
||||
${BACNET_SOURCE_DIR}/crc.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
|
||||
+1251
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,139 @@
|
||||
#
|
||||
# Simple makefile to build an executable for Win32
|
||||
#
|
||||
# This makefile assumes Borland bcc32 development environment
|
||||
# on Windows NT/9x/2000/XP
|
||||
#
|
||||
|
||||
!ifndef BORLAND_DIR
|
||||
BORLAND_DIR_Not_Defined:
|
||||
@echo .
|
||||
@echo You must define environment variable BORLAND_DIR to compile.
|
||||
!endif
|
||||
|
||||
# target
|
||||
PRODUCT = mstpcap
|
||||
PRODUCT_EXE = $(PRODUCT).exe
|
||||
|
||||
# tools
|
||||
CC = $(BORLAND_DIR)\bin\bcc32
|
||||
MAKE=$(BORLAND_DIR)\bin\make.exe
|
||||
#LINK = $(BORLAND_DIR)\bin\tlink32
|
||||
LINK = $(BORLAND_DIR)\bin\ilink32
|
||||
|
||||
BACNET_LIB_DIR = ..\..\lib
|
||||
BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib
|
||||
|
||||
# directories
|
||||
BACNET_PORT = ..\..\ports\win32
|
||||
BACNET_INCLUDE = ..\..\include
|
||||
BACNET_OBJECT = ..\object
|
||||
BACNET_HANDLER = ..\handler
|
||||
INCLUDES = \
|
||||
-I$(BACNET_INCLUDE) \
|
||||
-I$(BACNET_PORT) \
|
||||
-I$(BACNET_OBJECT) \
|
||||
-I$(BACNET_HANDLER) \
|
||||
-I$(BORLAND_DIR)\include
|
||||
|
||||
#
|
||||
BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL
|
||||
#BACDL_DEFINE=-DBACDL_MSTP=1
|
||||
BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1
|
||||
DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE)
|
||||
|
||||
SRCS = main.c \
|
||||
|
||||
OBJS = $(SRCS:.c=.obj)
|
||||
|
||||
#
|
||||
# Compiler definitions
|
||||
#
|
||||
BCC_CFG = bcc32.cfg
|
||||
|
||||
#
|
||||
# Include directories
|
||||
#
|
||||
CFLAGS = $(INCLUDES) $(DEFINES)
|
||||
|
||||
#
|
||||
# Libraries
|
||||
#
|
||||
C_LIB_DIR = $(BORLAND_DIR)\lib
|
||||
|
||||
LIBS = $(BACNET_LIB) \
|
||||
$(C_LIB_DIR)\IMPORT32.lib \
|
||||
$(C_LIB_DIR)\CW32MT.lib \
|
||||
|
||||
#
|
||||
# Main target
|
||||
#
|
||||
# This should be the first one in the makefile
|
||||
|
||||
all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE)
|
||||
del $(BCC_CFG)
|
||||
|
||||
install: $(PRODUCT_EXE)
|
||||
copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE)
|
||||
|
||||
# Linker specific: the link below is for BCC linker/compiler. If you link
|
||||
# with a different linker - please change accordingly.
|
||||
#
|
||||
|
||||
# need a temp response file (@&&| ... |) because command line is too long
|
||||
# $** lists each dependency
|
||||
# $< target name
|
||||
# $* target name without extension
|
||||
$(PRODUCT_EXE) : $(OBJS)
|
||||
@echo Running Linker for $(PRODUCT_EXE)
|
||||
$(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&|
|
||||
$(BORLAND_DIR)\lib\c0x32.obj $**
|
||||
$<
|
||||
$*.map
|
||||
$(LIBS)
|
||||
|
|
||||
|
||||
#
|
||||
# Utilities
|
||||
|
||||
clean :
|
||||
del $(OBJS)
|
||||
del $(PRODUCT_EXE)
|
||||
del $(PRODUCT).map
|
||||
del $(PRODUCT).ilc
|
||||
del $(PRODUCT).ild
|
||||
del $(PRODUCT).ilf
|
||||
del $(PRODUCT).ils
|
||||
del $(PRODUCT).tds
|
||||
del $(BCC_CFG)
|
||||
|
||||
#
|
||||
# Generic rules
|
||||
#
|
||||
.SUFFIXES: .cpp .c .sbr .obj
|
||||
|
||||
#
|
||||
# cc generic rule
|
||||
#
|
||||
.c.obj:
|
||||
$(CC) +$(BCC_CFG) -o$@ $<
|
||||
|
||||
# Compiler configuration file
|
||||
$(BCC_CFG) :
|
||||
Copy &&|
|
||||
$(CFLAGS)
|
||||
-c
|
||||
-y #include line numbers in OBJ's
|
||||
-v #include debug info
|
||||
-w+ #turn on all warnings
|
||||
-Od #disable all optimizations
|
||||
#-a4 #32 bit data alignment
|
||||
#-M # generate link map
|
||||
#-ls # linker options
|
||||
#-WM- #not multithread
|
||||
-WM #multithread
|
||||
-w-aus # ignore warning assigned a value that is never used
|
||||
-w-sig # ignore warning conversion may lose sig digits
|
||||
| $@
|
||||
|
||||
# EOF: makefile
|
||||
@@ -0,0 +1,211 @@
|
||||
BACnet MS/TP Capture Tool
|
||||
|
||||
This tool captures BACnet MS/TP packets on an RS485 serial interface,
|
||||
and saves the packets to a file in Wireshark PCAP format for
|
||||
the BACnet MS/TP dissector to read. The filename has a date and time
|
||||
code in it, and will contain up to 65535 packets. A new file
|
||||
will be created at each 65535 packet interval. The tool can
|
||||
be stopped by using Control-C. The tool can also pipe its output
|
||||
to Wireshark to be monitored in real-time.
|
||||
|
||||
Here is a sample of the tool running (use CTRL-C to quit):
|
||||
D:\code\bacnet-stack>bin\mstpcap.exe com54 38400
|
||||
Adjusted interface name to \\.\COM54
|
||||
mstpcap: Using \\.\COM54 for capture at 38400 bps.
|
||||
mstpcap: saving capture to mstp_20110413134119.cap
|
||||
1156 packets
|
||||
==== MS/TP Frame Counts ====
|
||||
MAC Device Tokens PFM RPFM DER Postpd DNER TestReq TestRsp
|
||||
0 - 188 4 0 0 0 0 0 0
|
||||
2 - 189 0 0 0 0 0 0 0
|
||||
3 - 189 9 0 0 0 0 0 0
|
||||
7 - 189 60 0 0 0 0 0 0
|
||||
35 - 188 140 0 0 0 0 0 0
|
||||
Node Count: 5
|
||||
|
||||
==== MS/TP Usage and Timing Maximums ====
|
||||
MAC MaxMstr Retries Npoll Self Treply Tusage Trpfm Tder Tpostpd
|
||||
0 1 0 52 0 11 24 0 0 0
|
||||
2 0 0 0 0 23 0 0 0 0
|
||||
3 6 0 50 0 5 100 0 0 0
|
||||
7 34 0 52 0 5 34 0 0 0
|
||||
35 127 0 50 0 6 63 0 0 0
|
||||
Node Count: 5
|
||||
Invalid Frame Count: 0
|
||||
|
||||
The files that are captured can also be scanned to give some statistics:
|
||||
D:\code\bacnet-stack>bin\mstpcap.exe --scan mstp_20110413134119.cap
|
||||
Scanning mstp_20110413134119.cap
|
||||
1156 packets
|
||||
==== MS/TP Frame Counts ====
|
||||
MAC Device Tokens PFM RPFM DER Postpd DNER TestReq TestRsp
|
||||
0 - 188 4 0 0 0 0 0 0
|
||||
2 - 189 0 0 0 0 0 0 0
|
||||
3 - 189 9 0 0 0 0 0 0
|
||||
7 - 189 60 0 0 0 0 0 0
|
||||
35 - 188 140 0 0 0 0 0 0
|
||||
Node Count: 5
|
||||
|
||||
==== MS/TP Usage and Timing Maximums ====
|
||||
MAC MaxMstr Retries Npoll Self Treply Tusage Trpfm Tder Tpostpd
|
||||
0 1 0 52 0 11 24 0 0 0
|
||||
2 0 0 0 0 23 0 0 0 0
|
||||
3 6 0 50 0 5 100 0 0 0
|
||||
7 34 0 52 0 5 34 0 0 0
|
||||
35 127 0 50 0 6 63 0 0 0
|
||||
Node Count: 5
|
||||
Invalid Frame Count: 0
|
||||
|
||||
The BACnet MS/TP capture tool also includes statistics which are
|
||||
listed for any MAC addresses found passing a token,
|
||||
or any MAC address replying to a DER message.
|
||||
The statistics are emitted when Control-C is pressed, or when
|
||||
65535 packets are captured and the new file is created.
|
||||
The statistics are cleared when the new file is created.
|
||||
The statistics can be emitted from a file using the "--scan" option.
|
||||
|
||||
The MS/TP Frame counts use the following abbreviations:
|
||||
|
||||
Device = Device ID when an I-Am is seen in a capture (trigger with Who-Is).
|
||||
|
||||
Tokens = number of Token frames sent from this MAC address.
|
||||
|
||||
PFM = number of Poll-For-Master frames sent from this MAC address.
|
||||
|
||||
RPFM = number of Reply-To-Poll-For-Master frames sent from this MAC address.
|
||||
|
||||
DER = number of Data-Expecting-Reply frames sent from this MAC address.
|
||||
|
||||
Postpd = number of Reply-Postponed frames sent from this MAC address.
|
||||
|
||||
DNER = number of Data-Not-Expecting-Reply frames sent from this MAC address.
|
||||
|
||||
TestReq = number of Test-Request frames sent from this MAC address.
|
||||
|
||||
TestRsp = number of Test-Response frames sent from this MAC address.
|
||||
|
||||
The MS/TP Usage and Timing Maximums use the following abbreviations:
|
||||
|
||||
MaxMstr = highest destination MAC address during PFM
|
||||
|
||||
Retries = number of second tokens sent to this MAC address.
|
||||
|
||||
Npoll = number of Tokens between Poll-For-Master
|
||||
|
||||
Self/TT = number of Tokens sent to self (Addendum 135-2008v) and
|
||||
number of tardy tokens sent late.
|
||||
|
||||
Treply = maximum number of milliseconds it took to reply with
|
||||
a token after receiving a token. Treply is required to be less
|
||||
than 25ms (but the mstpcap tool may not have that good of
|
||||
resolution on Windows).
|
||||
|
||||
Tusage = the maximum number of milliseconds the
|
||||
device waits for a ReplyToPollForMaster or Token retry.
|
||||
Tusage is required to be between 20ms and 100ms.
|
||||
|
||||
Trpfm = maximum number of milliseconds to respond to PFM with RPFM. It is
|
||||
required to be less than 25ms.
|
||||
|
||||
Tder = maximum number of milliseconds that a device takes to
|
||||
respond to a DataExpectingReply request. Tder is required to be less
|
||||
than 250ms.
|
||||
|
||||
Tpostpd = maximum number of milliseconds to respond to
|
||||
DataExpectingReply request with ReplyPostponed. Tpostpd is
|
||||
required to be less than 250ms.
|
||||
|
||||
==== FTDI chip RS-485 converter 76800 baud tricks ====
|
||||
|
||||
If you are using FTDI chip in your RS485 converter, you can
|
||||
alias an existing baud rate on Windows in the FTDIPORT.INF file
|
||||
in order to acheive non-standard 76800 bps:
|
||||
HKR,,"ConfigData",1,11,00,3F,3F,27,C0,00,00,27,00,00,00,C4,09,00,00,E2,04,00,00,71,02,00,00,38,41,00,00,9C,80,00,00,4E,C0,00,00,34,00,00,00,1A,00,00,00,0D,00,00,00,06,40,00,00,03,80,00,00,00,00,00,00,D0,80,00,00
|
||||
|
||||
replace the 10,27,00,00 => divisor = 10000, rate = 300 bps alias
|
||||
|
||||
hex values actual
|
||||
----------- ---------
|
||||
27,C0,00,00 - 76923 bps => divisor=39.125
|
||||
27,00,00,00 - 76677 bps => divisor=39.000
|
||||
|
||||
Windows XP (from koby3101)
|
||||
1) Plug in and locate your USB/RSS85 in Device Manager under ports. Right click
|
||||
on it and select Properties. Click Details tab and from the drop down select
|
||||
Device Instance Id.
|
||||
|
||||
2) Click Start, Run and then type regedit.
|
||||
Follow this path in the registry
|
||||
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Enum\FTDIBUS
|
||||
|
||||
Locate the folder that has the same name as what you found earlier Device Instance
|
||||
Id in step 1. Click on 0000 folder and then Device Parameters. On the right
|
||||
side you will see ConfigData. Right click and select Modify Binary Data.
|
||||
Locate the 10 27 which in my case were in 5th and 6th position and replace with
|
||||
27 C0.
|
||||
|
||||
This will make the RS485 go to 76800 baud (76923 baud) baud when you ask it
|
||||
to be 300 baud.
|
||||
|
||||
So to capture at 76800 baud type: mstpcap.exe COM2 300
|
||||
|
||||
Linux (used with Debian Lenny and Fedora 15)
|
||||
http://www.connecttech.com/KnowledgeDatabase/kdb309.htm
|
||||
As root:
|
||||
Change USB so I can use it later as normal user:
|
||||
# chmod 777 /dev/ttyUSB0 -
|
||||
Print current info about the device:
|
||||
# setserial /dev/ttyUSB0 –a
|
||||
/dev/ttyUSB0, Line 0, UART: unknown, Port: 0x0000, IRQ: 0
|
||||
Baud_base: 24000000, close_delay: 0, divisor: 0
|
||||
closing_wait: infinte
|
||||
Flags: spd_normal low_latency
|
||||
|
||||
Make custom speed:
|
||||
# setserial /dev/ttyUSB0 spd_cust
|
||||
24000000/312 gives 76923 baudrate:
|
||||
# setserial /dev/ttyUSB0 divisor 312
|
||||
Print to make sure changes got applied:
|
||||
# setserial /dev/ttyUSB0 –a
|
||||
/dev/ttyUSB0, Line 0, UART: unknown, Port: 0x0000, IRQ: 0
|
||||
Baud_base: 24000000, close_delay: 0, divisor: 312
|
||||
closing_wait: infinte
|
||||
Flags: spd_cust low_latency
|
||||
|
||||
Now as normal user running the mstpcap which uses the default 38400 baud it
|
||||
will actually capture at 76800 baud. (76923)
|
||||
|
||||
Just navigate (cd bin) to bin folder in the project and type:
|
||||
|
||||
$ ./mstpcap
|
||||
|
||||
==== Named Pipe direct to Wireshark ====
|
||||
|
||||
Use the named pipe option to send the capture output directly to Wireshark.
|
||||
On Windows, use \\.\pipe\wireshark as the name, and set that name as the
|
||||
interface name in Wireshark. On Linux, the named pipe name can be just about
|
||||
any file name, such as /tmp/wireshark. See:
|
||||
http://wiki.wireshark.org/CaptureSetup/Pipes
|
||||
|
||||
==== EXTCAP direct from Wireshark ====
|
||||
|
||||
To use extcap, run Wireshark and go to the About-dialog.
|
||||
Find a tab located there named "Folders".
|
||||
Locate the extcap search path.
|
||||
Copy the mstpcap.exe to that folder, which may not exist.
|
||||
Restart Wireshark, and look for "BACnet MS/TP on COMx" interfaces.
|
||||
Configure the interface to change baud rate.
|
||||
Capture directly from the interface.
|
||||
|
||||
==== Developer Info about Wireshark ExtCap ====
|
||||
|
||||
BACnet MS/TP uses a DLT (data link type) requested from and
|
||||
approved by libPCAP development team, which is 165. Wireshark
|
||||
includes a dissector that decodes the MS/TP packets when they
|
||||
use the DLT for MS/TP. Other DLTs exist for other types, and
|
||||
there are also DLTs set aside as USER DLTs.
|
||||
|
||||
See the following URLs for information about ExtCap:
|
||||
https://github.com/wireshark/wireshark/blob/master/doc/README.extcap
|
||||
https://github.com/wireshark/wireshark/blob/master/doc/extcap_example.py
|
||||
https://wiki.wireshark.org/Development/Extcap
|
||||
@@ -0,0 +1,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 = mstpcrc
|
||||
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
# This demo seems to be a little unique
|
||||
DEFINES = $(BACNET_DEFINES)
|
||||
BACNET_SOURCE_DIR = ../../src
|
||||
|
||||
#libraries used
|
||||
LIBRARIES=-lgcc,-lm
|
||||
|
||||
#build for release (default) or debug
|
||||
DEBUGGING =
|
||||
OPTIMIZATION = -Os
|
||||
ifeq (${BUILD},debug)
|
||||
OPTIMIZATION = -O0
|
||||
DEBUGGING = -g
|
||||
endif
|
||||
|
||||
# search order for included libraries
|
||||
INCLUDES = -I$(BACNET_INCLUDE_DIR)
|
||||
|
||||
SRCS = main.c \
|
||||
${BACNET_PORT_DIR}/timer.c \
|
||||
${BACNET_SOURCE_DIR}/crc.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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user