Added Added SubscribeCOV demo application (bacscov) for testing (and fixed subversion mess up that I did by copying a directory).

This commit is contained in:
skarg
2011-09-14 20:47:00 +00:00
parent bebb97b469
commit e9142bfc54
6 changed files with 724 additions and 116 deletions
+43
View File
@@ -0,0 +1,43 @@
#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 = bacscov
TARGET_BIN = ${TARGET}$(TARGET_EXT)
SRCS = main.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
+339
View File
@@ -0,0 +1,339 @@
/*************************************************************************
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
/* command line tool that sends a BACnet service, and displays the reply */
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h> /* for time */
#include <ctype.h> /* for toupper */
#define PRINT_ENABLED 1
#include "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"
/* some demo stuff needed */
#include "filename.h"
#include "handlers.h"
#include "client.h"
#include "txbuf.h"
#include "dlenv.h"
/* All included BACnet objects */
static object_functions_t Object_Table[] = {
{DEVICE_OBJ_FUNCTIONS},
{MAX_BACNET_OBJECT_TYPE, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL}
};
/* buffer used for receive */
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
/* converted command line arguments */
static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE;
/* Process identifier for matching replies */
static uint32_t Target_Device_Process_Identifier = 0;
/* buffer for service parameters */
static uint8_t Service_Parameters[MAX_APDU];
/* the invoke id is needed to filter incoming messages */
static uint8_t Request_Invoke_ID = 0;
static BACNET_ADDRESS Target_Address;
static bool Error_Detected = false;
static void MyErrorHandler(
BACNET_ADDRESS * src,
uint8_t invoke_id,
BACNET_ERROR_CLASS error_class,
BACNET_ERROR_CODE error_code)
{
if (address_match(&Target_Address, src) &&
(invoke_id == Request_Invoke_ID)) {
printf("BACnet Error: %s: %s\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;
}
}
static void Init_Service_Handlers(
void)
{
Device_Init(&Object_Table[0]);
/* we need to handle who-is
to support dynamic device binding to us */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
/* handle i-am to support binding to other devices */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind);
/* set the handler for all the services we don't implement
It is required to send the proper reject message... */
apdu_set_unrecognized_service_handler_handler
(handler_unrecognized_service);
/* we must implement read property - it's required! */
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
handler_read_property);
/* handle the data coming back from COV subscriptions */
apdu_set_confirmed_handler(SERVICE_CONFIRMED_COV_NOTIFICATION,
handler_ccov_notification);
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_COV_NOTIFICATION,
handler_ucov_notification);
/* handle any errors coming back */
apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler);
apdu_set_abort_handler(MyAbortHandler);
apdu_set_reject_handler(MyRejectHandler);
}
int main(
int argc,
char *argv[])
{
BACNET_ADDRESS src = {
0
}; /* address where message came from */
uint16_t pdu_len = 0;
unsigned timeout = 100; /* milliseconds */
unsigned max_apdu = 0;
time_t elapsed_seconds = 0;
time_t last_seconds = 0;
time_t current_seconds = 0;
time_t timeout_seconds = 0;
time_t delta_seconds = 0;
bool found = false;
char *filename = NULL;
BACNET_SUBSCRIBE_COV_DATA cov_data = {0};
bool print_usage_terse = false;
bool print_usage_verbose = false;
if (argc < 5) {
print_usage_terse = true;
}
if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) {
print_usage_terse = true;
print_usage_verbose = true;
}
if (print_usage_terse) {
filename = filename_remove_path(argv[0]);
/* note: priority 16 and 0 should produce the same end results... */
printf("Usage: %s device-id object-type object-instance "
"process-id lifetime [issueConfirmedNotifications]\r\n",
filename);
if (!print_usage_verbose) {
return 0;
}
}
if (print_usage_verbose) {
printf("\r\n"
"device-id:\r\n"
"The subscriber BACnet Device Object Instance number.\r\n"
"\r\n"
"object-type:\r\n"
"The monitored object type is the integer value of the\r\n"
"enumeration BACNET_OBJECT_TYPE in bacenum.h. For example,\r\n"
"if you were monitoring Analog Output 2, the object-type\r\n"
"would be 1.\r\n"
"\r\n"
"object-instance:\r\n"
"The monitored object instance number.\r\n"
"\r\n"
"process-id:\r\n"
"Process Identifier for this COV subscription.\r\n"
"\r\n"
"lifetime:\r\n"
"Optional subscription lifetime is conveyed in seconds.\r\n"
"\r\n"
"issueConfirmedNotifications:\r\n"
"Optional flag to subscribe using Confirmed notifications.\r\n"
"Use the word \'confirmed\'.\r\n"
"\r\n"
"If both the \'issueConfirmedNotifications\' and\r\n"
"\'lifetime\' parameters are absent, then this shall\r\n"
"indicate a cancellation request.\r\n"
"\r\n"
"Example:\r\n"
"If you want subscribe to Device 123 Analog Input 9 object\r\n"
"using confirmed COV notifications for 5 minutes,\r\n"
"you could send the following command:\r\n"
"%s 123 0 9 1 600 confirmed\r\n"
"To send the same COV subscription request for unconfirmed\r\n"
"notifications, you could send the following command:\r\n"
"%s 123 0 9 1 600\r\n",
filename, filename);
return 0;
}
/* decode the command line parameters */
Target_Device_Object_Instance = strtol(argv[1], NULL, 0);
cov_data.monitoredObjectIdentifier.type = strtol(argv[2], NULL, 0);
cov_data.monitoredObjectIdentifier.instance = strtol(argv[3], NULL, 0);
cov_data.subscriberProcessIdentifier =
Target_Device_Process_Identifier = strtol(argv[4], NULL, 0);
if (argc > 5) {
cov_data.lifetime = strtol(argv[5], NULL, 0);
cov_data.issueConfirmedNotifications = false;
} else {
cov_data.cancellationRequest = true;
}
if (argc > 6) {
if (strcmp(argv[6],"confirmed") == 0) {
cov_data.issueConfirmedNotifications = true;
}
}
if (Target_Device_Object_Instance >= BACNET_MAX_INSTANCE) {
fprintf(stderr, "device-instance=%u - it must be less than %u\r\n",
Target_Device_Object_Instance, BACNET_MAX_INSTANCE);
return 1;
}
if (cov_data.monitoredObjectIdentifier.type >= MAX_BACNET_OBJECT_TYPE) {
fprintf(stderr, "object-type=%u - it must be less than %u\r\n",
cov_data.monitoredObjectIdentifier.type, MAX_BACNET_OBJECT_TYPE);
return 1;
}
if (cov_data.monitoredObjectIdentifier.instance > BACNET_MAX_INSTANCE) {
fprintf(stderr, "object-instance=%u - it must be less than %u\r\n",
cov_data.monitoredObjectIdentifier.instance,
BACNET_MAX_INSTANCE + 1);
return 1;
}
/* setup my info */
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
address_init();
Init_Service_Handlers();
dlenv_init();
/* configure the timeout values */
last_seconds = time(NULL);
timeout_seconds = (apdu_timeout() / 1000) * apdu_retries();
if (timeout_seconds < cov_data.lifetime) {
timeout_seconds = cov_data.lifetime;
}
/* 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) {
/* increment timer - exit if timed out */
delta_seconds = current_seconds - last_seconds;
elapsed_seconds += delta_seconds;
tsm_timer_milliseconds((delta_seconds * 1000));
/* keep track of time for next check */
last_seconds = current_seconds;
}
if (Error_Detected)
break;
/* wait until the device is bound, or timeout and quit */
if (!found) {
found =
address_bind_request(Target_Device_Object_Instance, &max_apdu,
&Target_Address);
}
if (found) {
if (Request_Invoke_ID == 0) {
Request_Invoke_ID = Send_COV_Subscribe(
Target_Device_Object_Instance,
&cov_data);
printf("Sent SubscribeCOV request. Waiting %u seconds.\r\n",
(unsigned)(timeout_seconds - elapsed_seconds));
} else if (tsm_invoke_id_free(Request_Invoke_ID)) {
/* do nothing - wait for lifetime value to expire */
} 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 {
/* exit if timed out */
if (elapsed_seconds > timeout_seconds) {
Error_Detected = true;
printf("\rError: APDU Timeout!\r\n");
break;
}
}
/* returns 0 bytes on timeout */
pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
/* process */
if (pdu_len) {
npdu_handler(&src, &Rx_Buf[0], pdu_len);
}
if (Error_Detected) {
break;
}
/* COV - so just wait until lifetime value expires */
if (elapsed_seconds > timeout_seconds) {
break;
}
}
if (Error_Detected)
return 1;
return 0;
}
+142
View File
@@ -0,0 +1,142 @@
#
# 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 = bacscov
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
# by default bacupt can handle 64 tag/value pairs
BUILD_DEFINE = -DMAX_PROPERTY_VALUES=64
DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) $(BUILD_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
+1 -1
View File
@@ -10,7 +10,7 @@
# Assumes rm and cp are available # Assumes rm and cp are available
# Executable file name # Executable file name
TARGET = bacscov TARGET = bacupt
TARGET_BIN = ${TARGET}$(TARGET_EXT) TARGET_BIN = ${TARGET}$(TARGET_EXT)
+188 -104
View File
@@ -66,8 +66,14 @@ static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
/* converted command line arguments */ /* converted command line arguments */
static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE;
/* Process identifier for matching replies */ static uint16_t Target_Vendor_Identifier = 260;
static uint32_t Target_Device_Process_Identifier = 0; static uint32_t Target_Service_Number = 0;
/* property value encodings */
#ifndef MAX_PROPERTY_VALUES
#define MAX_PROPERTY_VALUES 64
#endif
static BACNET_APPLICATION_DATA_VALUE
Target_Object_Property_Value[MAX_PROPERTY_VALUES];
/* buffer for service parameters */ /* buffer for service parameters */
static uint8_t Service_Parameters[MAX_APDU]; static uint8_t Service_Parameters[MAX_APDU];
/* the invoke id is needed to filter incoming messages */ /* the invoke id is needed to filter incoming messages */
@@ -118,6 +124,70 @@ void MyRejectHandler(
} }
} }
void MyUnconfirmedPrivateTransferHandler(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src)
{
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;
len = ptransfer_decode_service_request(
service_request, service_len, &private_data);
if (len >= 0) {
printf("PrivateTransfer:vendorID=%u\r\n",
(unsigned)private_data.vendorID);
printf("PrivateTransfer:serviceNumber=%lu\r\n",
(unsigned long)private_data.serviceNumber);
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
}
}
static void Init_Service_Handlers( static void Init_Service_Handlers(
void) void)
{ {
@@ -134,11 +204,9 @@ static void Init_Service_Handlers(
/* we must implement read property - it's required! */ /* we must implement read property - it's required! */
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
handler_read_property); handler_read_property);
/* handle the data coming back from COV subscriptions */ /* handle the data coming back from confirmed requests */
apdu_set_confirmed_handler(SERVICE_CONFIRMED_COV_NOTIFICATION, apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_PRIVATE_TRANSFER,
handler_ccov_notification); MyUnconfirmedPrivateTransferHandler);
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_COV_NOTIFICATION,
handler_ucov_notification);
/* handle any errors coming back */ /* handle any errors coming back */
apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler); apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler);
apdu_set_abort_handler(MyAbortHandler); apdu_set_abort_handler(MyAbortHandler);
@@ -162,97 +230,117 @@ int main(
time_t delta_seconds = 0; time_t delta_seconds = 0;
bool found = false; bool found = false;
char *filename = NULL; char *filename = NULL;
BACNET_SUBSCRIBE_COV_DATA cov_data = {0}; char *value_string = NULL;
bool print_usage_terse = false; bool status = false;
bool print_usage_verbose = false; int args_remaining = 0, tag_value_arg = 0, i = 0;
BACNET_APPLICATION_TAG property_tag;
uint8_t context_tag = 0;
BACNET_PRIVATE_TRANSFER_DATA private_data = {0};
int len = 0;
bool sent_message = false;
if (argc < 5) { if (argc < 6) {
print_usage_terse = true;
}
if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) {
print_usage_terse = true;
print_usage_verbose = true;
}
if (print_usage_terse) {
filename = filename_remove_path(argv[0]); filename = filename_remove_path(argv[0]);
/* note: priority 16 and 0 should produce the same end results... */ printf("Usage: %s device-instance vendor-id service-number "
printf("Usage: %s device-id object-type object-instance " "tag value [tag value...]\r\n", filename);
"process-id lifetime [issueConfirmedNotifications]\r\n", if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) {
filename); printf("device-instance:\r\n"
if (!print_usage_verbose) { "BACnet Device Object Instance number that you are\r\n"
return 0; "trying to communicate to. This number will be used\r\n"
} "to try and bind with the device using Who-Is and\r\n"
} "I-Am services. For example, if you were transferring to\r\n"
if (print_usage_verbose) { "Device Object 123, the device-instance would be 123.\r\n"
printf("\r\n"
"device-id:\r\n"
"The subscriber BACnet Device Object Instance number.\r\n"
"\r\n" "\r\n"
"object-type:\r\n" "vendor_id:\r\n"
"The monitored object type is the integer value of the\r\n" "the unique vendor identification code for the type of\r\n"
"enumeration BACNET_OBJECT_TYPE in bacenum.h. For example,\r\n" "vendor proprietary service to be performed.\r\n"
"if you were monitoring Analog Output 2, the object-type\r\n"
"would be 1.\r\n"
"\r\n" "\r\n"
"object-instance:\r\n" "service-number (Unsigned32):\r\n"
"The monitored object instance number.\r\n" "the desired proprietary service to be performed.\r\n"
"\r\n" "\r\n"
"process-id:\r\n" "tag:\r\n"
"Process Identifier for this COV subscription.\r\n" "Tag is the integer value of the enumeration \r\n"
"BACNET_APPLICATION_TAG in bacenum.h.\r\n"
"It is the data type of the value that you are sending.\r\n"
"For example, if you were transfering a REAL value, you would \r\n"
"use a tag of 4.\r\n"
"Context tags are created using two tags in a row.\r\n"
"The context tag is preceded by a C. Ctag tag.\r\n"
"C2 4 creates a context 2 tagged REAL.\r\n"
"\r\n" "\r\n"
"lifetime:\r\n" "value:\r\n"
"Optional subscription lifetime is conveyed in seconds.\r\n" "The value is an ASCII representation of some type of data\r\n"
"\r\n" "that you are transfering.\r\n"
"issueConfirmedNotifications:\r\n" "It is encoded using the tag information provided.\r\n"
"Optional flag to subscribe using Confirmed notifications.\r\n" "For example, if you were transferring a REAL value of 100.0,\r\n"
"Use the word \'confirmed\'.\r\n" "you would use 100.0 as the value.\r\n"
"\r\n" "If you were transferring an object identifier for Device 123,\r\n"
"If both the \'issueConfirmedNotifications\' and\r\n" "you would use 8:123 as the value.\r\n"
"\'lifetime\' parameters are absent, then this shall\r\n"
"indicate a cancellation request.\r\n"
"\r\n" "\r\n"
"Example:\r\n" "Example:\r\n"
"If you want subscribe to Device 123 Analog Input 9 object\r\n" "If you want to transfer a REAL value of 1.1 to service 23 of \r\n"
"using confirmed COV notifications for 5 minutes,\r\n" "vendor 260 in Device 99, you could send the following command:\r\n"
"you could send the following command:\r\n" "%s 99 260 23 4 1.1\r\n", filename);
"%s 123 0 9 1 600 confirmed\r\n" }
"To send the same COV subscription request for unconfirmed\r\n"
"notifications, you could send the following command:\r\n"
"%s 123 0 9 1 600\r\n",
filename, filename);
return 0; return 0;
} }
/* decode the command line parameters */ /* decode the command line parameters */
Target_Device_Object_Instance = strtol(argv[1], NULL, 0); Target_Device_Object_Instance = strtol(argv[1], NULL, 0);
cov_data.monitoredObjectIdentifier.type = strtol(argv[2], NULL, 0); Target_Vendor_Identifier = strtol(argv[2], NULL, 0);
cov_data.monitoredObjectIdentifier.instance = strtol(argv[3], NULL, 0); Target_Service_Number = strtol(argv[3], NULL, 0);
cov_data.subscriberProcessIdentifier = if (Target_Device_Object_Instance > BACNET_MAX_INSTANCE) {
Target_Device_Process_Identifier = strtol(argv[4], NULL, 0);
if (argc > 5) {
cov_data.lifetime = strtol(argv[5], NULL, 0);
cov_data.issueConfirmedNotifications = false;
} else {
cov_data.cancellationRequest = true;
}
if (argc > 6) {
if (strcmp(argv[6],"confirmed") == 0) {
cov_data.issueConfirmedNotifications = true;
}
}
if (Target_Device_Object_Instance >= BACNET_MAX_INSTANCE) {
fprintf(stderr, "device-instance=%u - it must be less than %u\r\n", fprintf(stderr, "device-instance=%u - it must be less than %u\r\n",
Target_Device_Object_Instance, BACNET_MAX_INSTANCE); Target_Device_Object_Instance, BACNET_MAX_INSTANCE);
return 1; return 1;
} }
if (cov_data.monitoredObjectIdentifier.type >= MAX_BACNET_OBJECT_TYPE) { args_remaining = (argc - (6-2));
fprintf(stderr, "object-type=%u - it must be less than %u\r\n", for (i = 0; i < MAX_PROPERTY_VALUES; i++) {
cov_data.monitoredObjectIdentifier.type, MAX_BACNET_OBJECT_TYPE); tag_value_arg = (6-2) + (i * 2);
/* special case for context tagged values */
if (toupper(argv[tag_value_arg][0]) == 'C') {
context_tag = strtol(&argv[tag_value_arg][1], NULL, 0);
tag_value_arg++;
args_remaining--;
Target_Object_Property_Value[i].context_tag = context_tag;
Target_Object_Property_Value[i].context_specific = true;
} else {
Target_Object_Property_Value[i].context_specific = false;
}
property_tag = strtol(argv[tag_value_arg], NULL, 0);
args_remaining--;
if (args_remaining <= 0) {
fprintf(stderr, "Error: not enough tag-value pairs\r\n");
return 1; return 1;
} }
if (cov_data.monitoredObjectIdentifier.instance > BACNET_MAX_INSTANCE) { value_string = argv[tag_value_arg + 1];
fprintf(stderr, "object-instance=%u - it must be less than %u\r\n", args_remaining--;
cov_data.monitoredObjectIdentifier.instance, /* printf("tag[%d]=%u value[%d]=%s\r\n",
BACNET_MAX_INSTANCE + 1); i, property_tag, i, value_string); */
if (property_tag >= MAX_BACNET_APPLICATION_TAG) {
fprintf(stderr, "Error: tag=%u - it must be less than %u\r\n",
property_tag, MAX_BACNET_APPLICATION_TAG);
return 1;
}
status =
bacapp_parse_application_data(property_tag, value_string,
&Target_Object_Property_Value[i]);
if (!status) {
/* FIXME: show the expected entry format for the tag */
fprintf(stderr, "Error: unable to parse the tag value\r\n");
return 1;
}
Target_Object_Property_Value[i].next = NULL;
if (i > 0) {
Target_Object_Property_Value[i - 1].next =
&Target_Object_Property_Value[i];
}
if (args_remaining <= 0) {
break;
}
}
if (args_remaining > 0) {
fprintf(stderr, "Error: Exceeded %d tag-value pairs.\r\n",
MAX_PROPERTY_VALUES);
return 1; return 1;
} }
/* setup my info */ /* setup my info */
@@ -263,9 +351,6 @@ int main(
/* configure the timeout values */ /* configure the timeout values */
last_seconds = time(NULL); last_seconds = time(NULL);
timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); timeout_seconds = (apdu_timeout() / 1000) * apdu_retries();
if (timeout_seconds < cov_data.lifetime) {
timeout_seconds = cov_data.lifetime;
}
/* try to bind with the device */ /* try to bind with the device */
found = found =
address_bind_request(Target_Device_Object_Instance, &max_apdu, address_bind_request(Target_Device_Object_Instance, &max_apdu,
@@ -283,9 +368,7 @@ int main(
/* increment timer - exit if timed out */ /* increment timer - exit if timed out */
delta_seconds = current_seconds - last_seconds; delta_seconds = current_seconds - last_seconds;
elapsed_seconds += delta_seconds; elapsed_seconds += delta_seconds;
tsm_timer_milliseconds((delta_seconds * 1000)); tsm_timer_milliseconds(((current_seconds - last_seconds) * 1000));
/* keep track of time for next check */
last_seconds = current_seconds;
} }
if (Error_Detected) if (Error_Detected)
break; break;
@@ -295,28 +378,26 @@ int main(
address_bind_request(Target_Device_Object_Instance, &max_apdu, address_bind_request(Target_Device_Object_Instance, &max_apdu,
&Target_Address); &Target_Address);
} }
if (!sent_message) {
if (found) { if (found) {
if (Request_Invoke_ID == 0) { len = bacapp_encode_data(&Service_Parameters[0],
Request_Invoke_ID = Send_COV_Subscribe( &Target_Object_Property_Value[0]);
Target_Device_Object_Instance, private_data.serviceParameters = &Service_Parameters[0];
&cov_data); private_data.serviceParametersLen = len;
printf("Sent SubscribeCOV request. Waiting %u seconds.\r\n", private_data.vendorID = Target_Vendor_Identifier;
private_data.serviceNumber = Target_Service_Number;
Send_UnconfirmedPrivateTransfer(
&Target_Address,
&private_data);
printf("Sent PrivateTransfer. Waiting %u seconds.\r\n",
(unsigned)(timeout_seconds - elapsed_seconds)); (unsigned)(timeout_seconds - elapsed_seconds));
} else if (tsm_invoke_id_free(Request_Invoke_ID)) { sent_message = true;
/* do nothing - wait for lifetime value to expire */ } else {
} else if (tsm_invoke_id_failed(Request_Invoke_ID)) { if (elapsed_seconds > timeout_seconds) {
fprintf(stderr, "\rError: TSM Timeout!\r\n"); printf("\rError: APDU Timeout!\r\n");
tsm_free_invoke_id(Request_Invoke_ID);
Error_Detected = true; Error_Detected = true;
/* try again or abort? */
break; break;
} }
} else {
/* exit if timed out */
if (elapsed_seconds > timeout_seconds) {
Error_Detected = true;
printf("\rError: APDU Timeout!\r\n");
break;
} }
} }
/* returns 0 bytes on timeout */ /* returns 0 bytes on timeout */
@@ -328,11 +409,14 @@ int main(
if (Error_Detected) { if (Error_Detected) {
break; break;
} }
/* COV - so just wait until lifetime value expires */ /* unconfirmed - so just wait until our timeout value */
if (elapsed_seconds > timeout_seconds) { if (elapsed_seconds > timeout_seconds) {
break; break;
} }
/* keep track of time for next check */
last_seconds = current_seconds;
} }
if (Error_Detected) if (Error_Detected)
return 1; return 1;
return 0; return 0;
+1 -1
View File
@@ -11,7 +11,7 @@ BORLAND_DIR_Not_Defined:
@echo You must define environment variable BORLAND_DIR to compile. @echo You must define environment variable BORLAND_DIR to compile.
!endif !endif
PRODUCT = bacscov PRODUCT = bacupt
PRODUCT_EXE = $(PRODUCT).exe PRODUCT_EXE = $(PRODUCT).exe
# tools # tools