Enhanced the WriteProperty service demo to allow multiple tagged data elements and to allow context data.

This commit is contained in:
skarg
2007-02-20 20:56:08 +00:00
parent 2fed6fcf3b
commit b3d15b1f37
10 changed files with 760 additions and 472 deletions
+283 -103
View File
@@ -110,6 +110,77 @@ int bacapp_encode_application_data(uint8_t * apdu,
return apdu_len;
}
/* decode the data and store it into value.
Return the number of octets consumed. */
int bacapp_decode_data(uint8_t * apdu,
uint8_t tag_data_type,
uint32_t len_value_type,
BACNET_APPLICATION_DATA_VALUE * value)
{
int len = 0;
int object_type = 0;
uint32_t instance = 0;
if (apdu && value) {
switch (tag_data_type) {
case BACNET_APPLICATION_TAG_NULL:
/* nothing else to do */
break;
case BACNET_APPLICATION_TAG_BOOLEAN:
value->type.Boolean = decode_boolean(len_value_type);
break;
case BACNET_APPLICATION_TAG_UNSIGNED_INT:
len = decode_unsigned(&apdu[0],
len_value_type, &value->type.Unsigned_Int);
break;
case BACNET_APPLICATION_TAG_SIGNED_INT:
len = decode_signed(&apdu[0],
len_value_type, &value->type.Signed_Int);
break;
case BACNET_APPLICATION_TAG_REAL:
len = decode_real(&apdu[0], &(value->type.Real));
break;
#if 0
case BACNET_APPLICATION_TAG_DOUBLE:
len = decode_double(&apdu[0], &(value->type.Double));
break;
#endif
case BACNET_APPLICATION_TAG_ENUMERATED:
len = decode_enumerated(&apdu[0],
len_value_type, &value->type.Enumerated);
break;
case BACNET_APPLICATION_TAG_DATE:
len = decode_date(&apdu[0], &value->type.Date);
break;
case BACNET_APPLICATION_TAG_TIME:
len = decode_bacnet_time(&apdu[0], &value->type.Time);
break;
case BACNET_APPLICATION_TAG_OBJECT_ID:
len = decode_object_id(&apdu[0],
&object_type, &instance);
value->type.Object_Id.type = object_type;
value->type.Object_Id.instance = instance;
break;
case BACNET_APPLICATION_TAG_OCTET_STRING:
len = decode_octet_string(&apdu[0],
len_value_type, &value->type.Octet_String);
break;
case BACNET_APPLICATION_TAG_CHARACTER_STRING:
len = decode_character_string(&apdu[0],
len_value_type, &value->type.Character_String);
break;
case BACNET_APPLICATION_TAG_BIT_STRING:
len = decode_bitstring(&apdu[0],
len_value_type, &value->type.Bit_String);
break;
default:
break;
}
}
return len;
}
int bacapp_decode_application_data(uint8_t * apdu,
int max_apdu_len, BACNET_APPLICATION_DATA_VALUE * value)
{
@@ -117,132 +188,239 @@ int bacapp_decode_application_data(uint8_t * apdu,
int tag_len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
/* FIXME: use max_apdu_len! */
(void) max_apdu_len;
if (apdu && value && !decode_is_context_specific(apdu)) {
value->context_specific = false;
tag_len = decode_tag_number_and_value(&apdu[0],
&tag_number, &len_value_type);
if (tag_len) {
len += tag_len;
value->tag = tag_number;
len += bacapp_decode_data(&apdu[len],
tag_number,
len_value_type,
value);
}
value->next = NULL;
}
return len;
}
int bacapp_encode_context_data_value(uint8_t * apdu, uint8_t context_tag_number,
BACNET_APPLICATION_DATA_VALUE * value)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (value && apdu) {
switch (value->tag) {
case BACNET_APPLICATION_TAG_NULL:
apdu_len = encode_context_null(&apdu[0], context_tag_number);
break;
case BACNET_APPLICATION_TAG_BOOLEAN:
apdu_len = encode_context_boolean(&apdu[0], context_tag_number,
value->type.Boolean);
break;
case BACNET_APPLICATION_TAG_UNSIGNED_INT:
apdu_len = encode_context_unsigned(&apdu[0], context_tag_number,
value->type.Unsigned_Int);
break;
case BACNET_APPLICATION_TAG_SIGNED_INT:
apdu_len = encode_context_signed(&apdu[0], context_tag_number,
value->type.Signed_Int);
break;
case BACNET_APPLICATION_TAG_REAL:
apdu_len = encode_context_real(&apdu[0], context_tag_number,
value->type.Real);
break;
case BACNET_APPLICATION_TAG_ENUMERATED:
apdu_len = encode_context_enumerated(&apdu[0], context_tag_number,
value->type.Enumerated);
break;
case BACNET_APPLICATION_TAG_DATE:
apdu_len = encode_context_date(&apdu[0], context_tag_number,
&value->type.Date);
break;
case BACNET_APPLICATION_TAG_TIME:
apdu_len = encode_context_time(&apdu[0], context_tag_number,
&value->type.Time);
break;
case BACNET_APPLICATION_TAG_OBJECT_ID:
apdu_len = encode_context_object_id(&apdu[0], context_tag_number,
value->type.Object_Id.type,
value->type.Object_Id.instance);
break;
case BACNET_APPLICATION_TAG_OCTET_STRING:
apdu_len = encode_context_octet_string(&apdu[0], context_tag_number,
&value->type.Octet_String);
break;
case BACNET_APPLICATION_TAG_CHARACTER_STRING:
apdu_len = encode_context_character_string(&apdu[0], context_tag_number,
&value->type.Character_String);
break;
case BACNET_APPLICATION_TAG_BIT_STRING:
apdu_len = encode_context_bitstring(&apdu[0], context_tag_number,
&value->type.Bit_String);
break;
#if 0
case BACNET_APPLICATION_TAG_DOUBLE:
/* FIXME: double is not implemented yet.*/
apdu_len = encode_context_double(&apdu[0], context_tag_number,
value->type.Double);
break;
#endif
default:
break;
}
}
return apdu_len;
}
/* returns the fixed tag type for certain context tagged properties */
BACNET_APPLICATION_TAG bacapp_context_tag_type(
BACNET_PROPERTY_ID property,
uint8_t tag_number)
{
BACNET_APPLICATION_TAG tag = MAX_BACNET_APPLICATION_TAG;
switch (property) {
case PROP_REQUESTED_SHED_LEVEL:
switch (tag_number) {
case 0:
case 1:
tag = BACNET_APPLICATION_TAG_UNSIGNED_INT;
break;
case 2:
tag = BACNET_APPLICATION_TAG_REAL;
break;
default:
break;
}
break;
case PROP_ACTION:
switch (tag_number) {
case 0:
case 1:
tag = BACNET_APPLICATION_TAG_OBJECT_ID;
break;
case 2:
tag = BACNET_APPLICATION_TAG_ENUMERATED;
break;
case 3:
case 5:
case 6:
tag = BACNET_APPLICATION_TAG_UNSIGNED_INT;
break;
case 7:
case 8:
tag = BACNET_APPLICATION_TAG_BOOLEAN;
break;
case 4: /* propertyValue: abstract syntax */
default:
break;
}
break;
case PROP_EXCEPTION_SCHEDULE:
switch (tag_number) {
case 1:
tag = BACNET_APPLICATION_TAG_OBJECT_ID;
break;
case 3:
tag = BACNET_APPLICATION_TAG_UNSIGNED_INT;
break;
case 0: /* calendarEntry: abstract syntax + context */
case 2: /* list of BACnetTimeValue: abstract syntax */
default:
break;
}
break;
default:
break;
}
return tag;
}
int bacapp_encode_context_data(uint8_t * apdu,
BACNET_APPLICATION_DATA_VALUE * value,
BACNET_PROPERTY_ID property)
{
int apdu_len = 0;
BACNET_APPLICATION_TAG tag_data_type;
if (value && apdu) {
tag_data_type = bacapp_context_tag_type(property, value->context_tag);
if (tag_data_type < MAX_BACNET_APPLICATION_TAG) {
apdu_len = bacapp_encode_context_data_value(&apdu[0],
value->context_tag, value);
} else {
/* FIXME: what now? */
apdu_len = 0;
}
value->next = NULL;
}
return apdu_len;
}
int bacapp_decode_context_data(uint8_t * apdu,
int max_apdu_len, BACNET_APPLICATION_DATA_VALUE * value,
BACNET_PROPERTY_ID property)
{
int apdu_len = 0, len = 0;
int tag_len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
int object_type = 0;
uint32_t instance = 0;
/* FIXME: use max_apdu_len! */
(void) max_apdu_len;
if (apdu && !decode_is_context_specific(apdu)) {
if (apdu && value && decode_is_context_specific(apdu)) {
value->context_specific = true;
tag_len = decode_tag_number_and_value(&apdu[0],
&tag_number, &len_value_type);
if (tag_len) {
len += tag_len;
value->tag = tag_number;
if (tag_number == BACNET_APPLICATION_TAG_NULL) {
/* nothing else to do */
} else if (tag_number == BACNET_APPLICATION_TAG_BOOLEAN)
value->type.Boolean = decode_boolean(len_value_type);
else if (tag_number == BACNET_APPLICATION_TAG_UNSIGNED_INT)
len += decode_unsigned(&apdu[len],
len_value_type, &value->type.Unsigned_Int);
else if (tag_number == BACNET_APPLICATION_TAG_SIGNED_INT)
len += decode_signed(&apdu[len],
len_value_type, &value->type.Signed_Int);
else if (tag_number == BACNET_APPLICATION_TAG_REAL)
len += decode_real(&apdu[len], &(value->type.Real));
#if 0
else if (tag_number == BACNET_APPLICATION_TAG_DOUBLE)
len += decode_double(&apdu[len], &(value->type.Double));
#endif
else if (tag_number == BACNET_APPLICATION_TAG_OCTET_STRING)
len += decode_octet_string(&apdu[len],
len_value_type, &value->type.Octet_String);
else if (tag_number == BACNET_APPLICATION_TAG_CHARACTER_STRING)
len += decode_character_string(&apdu[len],
len_value_type, &value->type.Character_String);
else if (tag_number == BACNET_APPLICATION_TAG_BIT_STRING)
len += decode_bitstring(&apdu[len],
len_value_type, &value->type.Bit_String);
else if (tag_number == BACNET_APPLICATION_TAG_ENUMERATED)
len += decode_enumerated(&apdu[len],
len_value_type, &value->type.Enumerated);
else if (tag_number == BACNET_APPLICATION_TAG_DATE)
len += decode_date(&apdu[len], &value->type.Date);
else if (tag_number == BACNET_APPLICATION_TAG_TIME)
len += decode_bacnet_time(&apdu[len], &value->type.Time);
else if (tag_number == BACNET_APPLICATION_TAG_OBJECT_ID) {
len += decode_object_id(&apdu[len],
&object_type, &instance);
value->type.Object_Id.type = object_type;
value->type.Object_Id.instance = instance;
apdu_len = tag_len;
value->context_tag = tag_number;
value->tag = bacapp_context_tag_type(property, tag_number);
if (value->tag < MAX_BACNET_APPLICATION_TAG) {
len = bacapp_decode_data(&apdu[apdu_len],
value->tag,
len_value_type,
value);
apdu_len += len;
} else {
/* FIXME: what now? */
apdu_len = 0;
}
}
value->next = NULL;
}
return len;
return apdu_len;
}
int bacapp_encode_context_data(uint8_t * apdu,
BACNET_APPLICATION_DATA_VALUE * value, BACNET_PROPERTY_ID property)
int bacapp_encode_data(uint8_t * apdu,
BACNET_APPLICATION_DATA_VALUE * value)
{
int apdu_len = 0; /* total length of the apdu, return value */
if (value && apdu) {
switch (property) {
case PROP_REQUESTED_SHED_LEVEL:
switch (value->tag) {
case 0:
case 1:
apdu_len = encode_tagged_unsigned(&apdu[0],
value->type.Unsigned_Int);
break;
case 2:
apdu_len = encode_tagged_real(&apdu[0], value->type.Real);
break;
default:
apdu_len = 0;
break;
}
break;
default:
break;
if (value->context_specific) {
apdu_len = bacapp_encode_context_data_value(&apdu[0],
value->context_tag, value);
} else {
apdu_len = bacapp_encode_application_data(&apdu[0], value);
}
}
return apdu_len;
}
int bacapp_decode_context_data(uint8_t * apdu,
int max_apdu_len, BACNET_APPLICATION_DATA_VALUE * value,
BACNET_PROPERTY_ID property)
{
int len = 0;
int tag_len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
/* FIXME: use max_apdu_len! */
(void) max_apdu_len;
if (apdu && decode_is_context_specific(apdu)) {
tag_len = decode_tag_number_and_value(&apdu[0],
&tag_number, &len_value_type);
if (tag_len) {
len += tag_len;
value->tag = tag_number;
switch (property) {
case PROP_REQUESTED_SHED_LEVEL:
switch (tag_number) {
case 0:
case 1:
len += decode_unsigned(&apdu[len],
len_value_type, &(value->type.Unsigned_Int));
break;
case 2:
len += decode_real(&apdu[len], &(value->type.Real));
break;
default:
len = 0;
break;
}
break;
default:
break;
}
}
}
return len;
}
bool bacapp_copy(BACNET_APPLICATION_DATA_VALUE * dest_value,
BACNET_APPLICATION_DATA_VALUE * src_value)
@@ -299,6 +477,7 @@ bool bacapp_copy(BACNET_APPLICATION_DATA_VALUE * dest_value,
status = false;
break;
}
dest_value->next = src_value->next;
}
return status;
@@ -574,6 +753,7 @@ bool bacapp_parse_application_data(BACNET_APPLICATION_TAG tag_number,
} else
status = false;
}
value->next = NULL;
}
return status;
+16 -2
View File
@@ -41,8 +41,11 @@
#include "bacstr.h"
#include "datetime.h"
struct BACnet_Application_Data_Value;
typedef struct BACnet_Application_Data_Value {
uint8_t tag; /* application or context-specific number */
bool context_specific; /* true if context specific data */
uint8_t context_tag; /* only used for context specific data */
uint8_t tag; /* application tag data type */
union {
/* NULL - not needed as it is encoded in the tag alone */
bool Boolean;
@@ -58,11 +61,15 @@ typedef struct BACnet_Application_Data_Value {
BACNET_TIME Time;
BACNET_OBJECT_ID Object_Id;
} type;
/* simple linked list if needed */
struct BACnet_Application_Data_Value *next;
} BACNET_APPLICATION_DATA_VALUE;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
int bacapp_encode_data(uint8_t * apdu,
BACNET_APPLICATION_DATA_VALUE * value);
int bacapp_decode_application_data(uint8_t * apdu,
int max_apdu_len, BACNET_APPLICATION_DATA_VALUE * value);
@@ -78,6 +85,14 @@ extern "C" {
BACNET_APPLICATION_DATA_VALUE * value,
BACNET_PROPERTY_ID property);
int bacapp_encode_context_data_value(uint8_t * apdu,
uint8_t context_tag_number,
BACNET_APPLICATION_DATA_VALUE * value);
BACNET_APPLICATION_TAG bacapp_context_tag_type(
BACNET_PROPERTY_ID property,
uint8_t tag_number);
bool bacapp_copy(BACNET_APPLICATION_DATA_VALUE * dest_value,
BACNET_APPLICATION_DATA_VALUE * src_value);
@@ -99,7 +114,6 @@ extern "C" {
#ifdef BACAPP_PRINT_ENABLED
bool bacapp_parse_application_data(BACNET_APPLICATION_TAG tag_number,
const char *argv, BACNET_APPLICATION_DATA_VALUE * value);
bool bacapp_print_value(FILE * stream,
BACNET_APPLICATION_DATA_VALUE * value,
BACNET_PROPERTY_ID property);
+38 -7
View File
@@ -734,14 +734,11 @@ int encode_tagged_boolean(uint8_t * apdu, bool boolean_value)
int encode_context_boolean(uint8_t * apdu, int tag_number,
bool boolean_value)
{
int len = 1; /* return value */
int len = 0; /* return value */
apdu[1] = boolean_value ? 1 : 0;
/* we only reserved 1 byte for encoding the tag - check the limits */
if (tag_number <= 14)
len += encode_tag(&apdu[0], (uint8_t) tag_number, true, 1);
else
len = 0;
len = encode_tag(&apdu[0], (uint8_t) tag_number, true, 1);
apdu[len] = boolean_value ? 1 : 0;
len++;
return len;
}
@@ -777,6 +774,11 @@ int encode_tagged_null(uint8_t * apdu)
return encode_tag(&apdu[0], BACNET_APPLICATION_TAG_NULL, false, 0);
}
int encode_context_null(uint8_t * apdu, int tag_number)
{
return encode_tag(&apdu[0], tag_number, true, 0);
}
static uint8_t byte_reverse_bits(uint8_t in_byte)
{
uint8_t out_byte = 0;
@@ -872,6 +874,20 @@ int encode_tagged_bitstring(uint8_t * apdu, BACNET_BIT_STRING * bit_string)
return len;
}
int encode_context_bitstring(uint8_t * apdu, int tag_number,
BACNET_BIT_STRING * bit_string)
{
int len = 0;
int bit_string_encoded_length = 1; /* 1 for the bits remaining octet */
/* bit string may use more than 1 octet for the tag, so find out how many */
bit_string_encoded_length += bitstring_bytes_used(bit_string);
len = encode_tag(&apdu[0], tag_number, true, bit_string_encoded_length);
len += encode_bitstring(&apdu[len], bit_string);
return len;
}
/* from clause 20.2.6 Encoding of a Real Number Value */
/* returns the number of apdu bytes consumed */
int decode_real(uint8_t * apdu, float *real_value)
@@ -1429,7 +1445,22 @@ int encode_tagged_time(uint8_t * apdu, BACNET_TIME * btime)
len += encode_tag(&apdu[0], BACNET_APPLICATION_TAG_TIME, false, len);
return len;
}
int encode_context_time(uint8_t * apdu, int tag_number,
BACNET_TIME * btime)
{
int len = 0; /* return value */
/* assumes that the tag only consumes 1 octet */
len = encode_bacnet_time(&apdu[1], btime);
/* we only reserved 1 byte for encoding the tag - check the limits */
if ((tag_number <= 14) && (len <= 4))
len += encode_tag(&apdu[0], (uint8_t) tag_number, true, len);
else
len = 0;
return len;
}
/* from clause 20.2.13 Encoding of a Time Value */
+5
View File
@@ -72,6 +72,7 @@ extern "C" {
/* from clause 20.2.2 Encoding of a Null Value */
int encode_tagged_null(uint8_t * apdu);
int encode_context_null(uint8_t * apdu, int tag_number);
/* from clause 20.2.3 Encoding of a Boolean Value */
int encode_tagged_boolean(uint8_t * apdu, bool boolean_value);
@@ -88,6 +89,8 @@ extern "C" {
int encode_bitstring(uint8_t * apdu, BACNET_BIT_STRING * bit_string);
int encode_tagged_bitstring(uint8_t * apdu,
BACNET_BIT_STRING * bit_string);
int encode_context_bitstring(uint8_t * apdu, int tag_number,
BACNET_BIT_STRING * bit_string);
/* from clause 20.2.6 Encoding of a Real Number Value */
/* and 20.2.1 General Rules for Encoding BACnet Tags */
@@ -168,6 +171,8 @@ extern "C" {
int encode_bacnet_time(uint8_t * apdu, BACNET_TIME * btime);
int encode_tagged_time(uint8_t * apdu, BACNET_TIME * btime);
int decode_bacnet_time(uint8_t * apdu, BACNET_TIME * btime);
int encode_context_time(uint8_t * apdu, int tag_number,
BACNET_TIME * btime);
/* BACnet Date */
/* year = years since 1900 */
+1 -1
View File
@@ -115,7 +115,7 @@ static void days_since_epoch_into_ymd(uint32_t days,
year++;
}
while (days >= month_days(year, month)) {
while (days >= (uint32_t)month_days(year, month)) {
days -= month_days(year, month);
month++;
}
+1 -1
View File
@@ -64,7 +64,7 @@ extern "C" {
BACNET_PROPERTY_ID object_property,
BACNET_APPLICATION_DATA_VALUE * object_value,
uint8_t priority, int32_t array_index);
/* returns the invoke ID for confirmed request, or 0 if failed */
uint8_t Send_Reinitialize_Device_Request(uint32_t device_id,
BACNET_REINITIALIZED_STATE state, char *password);
+39 -5
View File
@@ -25,6 +25,7 @@
#include <stddef.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include "config.h"
#include "config.h"
#include "txbuf.h"
@@ -43,11 +44,12 @@
#include "txbuf.h"
/* returns the invoke ID for confirmed request, or zero on failure */
uint8_t Send_Write_Property_Request(uint32_t device_id, /* destination device */
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,
BACNET_APPLICATION_DATA_VALUE * object_value,
uint8_t *application_data,
int application_data_len,
uint8_t priority, int32_t array_index)
{
BACNET_ADDRESS dest;
@@ -80,9 +82,8 @@ uint8_t Send_Write_Property_Request(uint32_t device_id, /* destination device */
data.object_instance = object_instance;
data.object_property = object_property;
data.array_index = array_index;
data.application_data_len =
bacapp_encode_application_data(&data.application_data[0],
object_value);
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);
@@ -117,3 +118,36 @@ uint8_t Send_Write_Property_Request(uint32_t device_id, /* destination device */
return invoke_id;
}
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, int32_t array_index)
{
uint8_t application_data[MAX_APDU] = {0};
int apdu_len = 0, len = 0;
while (object_value) {
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);
}
+1 -2
View File
@@ -20,7 +20,7 @@ CFLAGS = -Wall -g $(INCLUDES) $(DEFINES)
TARGET = bacwp
SRCS = writeprop.c \
SRCS = main.c \
$(BACNET_PORT)/bip-init.c \
$(BACNET_ROOT)/bip.c \
$(BACNET_HANDLER)/txbuf.c \
@@ -51,7 +51,6 @@ SRCS = writeprop.c \
$(BACNET_ROOT)/bactext.c \
$(BACNET_ROOT)/datetime.c \
$(BACNET_ROOT)/indtext.c \
$(BACNET_ROOT)/bigend.c \
$(BACNET_ROOT)/whois.c \
$(BACNET_ROOT)/iam.c \
$(BACNET_ROOT)/tsm.c \
@@ -1,349 +1,375 @@
/**************************************************************************
*
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
/* command line tool that sends a BACnet service, and displays the reply */
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h> /* for time */
#include <string.h>
#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"
/* some demo stuff needed */
#include "filename.h"
#include "handlers.h"
#include "client.h"
#include "txbuf.h"
/* buffer used for receive */
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
/* global variables used in this file */
static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE;
static uint32_t Target_Object_Instance = BACNET_MAX_INSTANCE;
static BACNET_OBJECT_TYPE Target_Object_Type = OBJECT_ANALOG_INPUT;
static BACNET_PROPERTY_ID Target_Object_Property = PROP_ACKED_TRANSITIONS;
/* array index value or BACNET_ARRAY_ALL */
static int32_t Target_Object_Property_Index = BACNET_ARRAY_ALL;
static BACNET_APPLICATION_TAG Target_Object_Property_Tag =
BACNET_APPLICATION_TAG_NULL;
static BACNET_APPLICATION_DATA_VALUE Target_Object_Property_Value = { 0 };
/* 0 if not set, 1..16 if set */
static uint8_t Target_Object_Property_Priority = 0;
static BACNET_ADDRESS Target_Address;
static bool Error_Detected = false;
static void MyErrorHandler(BACNET_ADDRESS * src,
uint8_t invoke_id,
BACNET_ERROR_CLASS error_class, BACNET_ERROR_CODE error_code)
{
/* FIXME: verify src and invoke id */
(void) src;
(void) invoke_id;
printf("\r\nBACnet Error!\r\n");
printf("Error Class: %s\r\n", bactext_error_class_name(error_class));
printf("Error Code: %s\r\n", 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("\r\nBACnet Abort!\r\n");
printf("Abort Reason: %s\r\n",
bactext_abort_reason_name(abort_reason));
Error_Detected = true;
}
void MyRejectHandler(BACNET_ADDRESS * src,
uint8_t invoke_id, uint8_t reject_reason)
{
/* FIXME: verify src and invoke id */
(void) src;
(void) invoke_id;
printf("\r\nBACnet Reject!\r\n");
printf("Reject Reason: %s\r\n",
bactext_reject_reason_name(reject_reason));
Error_Detected = true;
}
void MyWritePropertySimpleAckHandler(BACNET_ADDRESS * src,
uint8_t invoke_id)
{
(void) src;
(void) invoke_id;
printf("\r\nWriteProperty Acknowledged!\r\n");
}
static void Init_Service_Handlers(void)
{
/* we need to handle who-is
to support dynamic device binding to us */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS,
handler_who_is);
/* handle i-am to support binding to other devices */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM,
handler_i_am_bind);
/* set the handler for all the services we don't implement
It is required to send the proper reject message... */
apdu_set_unrecognized_service_handler_handler
(handler_unrecognized_service);
/* we must implement read property - it's required! */
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
handler_read_property);
/* handle the ack coming back */
apdu_set_confirmed_simple_ack_handler(SERVICE_CONFIRMED_WRITE_PROPERTY,
MyWritePropertySimpleAckHandler);
/* handle any errors coming back */
apdu_set_error_handler(SERVICE_CONFIRMED_WRITE_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;
uint8_t invoke_id = 0;
bool found = false;
char *value_string = NULL;
bool status = false;
if (argc < 7) {
/* note: priority 16 and 0 should produce the same end results... */
printf("Usage: %s device-instance object-type object-instance "
"property tag value [priority] [index]\r\n",
filename_remove_path(argv[0]));
if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) {
printf("device-instance:\r\n"
"BACnet Device Object Instance number that you are trying to\r\n"
"communicate to. This number will be used to try and bind with\r\n"
"the device using Who-Is and I-Am services. For example, if you were\r\n"
"writing to Device Object 123, the device-instance would be 123.\r\n"
"\r\n"
"object-type:\r\n"
"The object type is the integer value of the enumeration\r\n"
"BACNET_OBJECT_TYPE in bacenum.h. It is the object that you are\r\n"
"writing to. For example if you were writing to Analog Output 2, \r\n"
"the object-type would be 1.\r\n"
"\r\n"
"object-instance:\r\n"
"This is the object instance number of the object that you are \r\n"
"writing to. For example, if you were writing to Analog Output 2, \r\n"
"the object-instance would be 2.\r\n"
"\r\n"
"property:\r\n"
"The property is an integer value of the enumeration \r\n"
"BACNET_PROPERTY_ID in bacenum.h. It is the property you are \r\n"
"writing to. For example, if you were writing to the Present Value\r\n"
"property, you would use 85 as the property.\r\n"
"\r\n"
"tag:\r\n"
"Tag is the integer value of the enumeration BACNET_APPLICATION_TAG \r\n"
"in bacenum.h. It is the data type of the value that you are\r\n"
"writing. For example, if you were writing a REAL value, you would \r\n"
"use a tag of 4."
"\r\n"
"value:\r\n"
"The value is an ASCII representation of some type of data that you\r\n"
"are writing. It is encoded using the tag information provided. For\r\n"
"example, if you were writing a REAL value of 100.0, you would use \r\n"
"100.0 as the value.\r\n"
"\r\n"
"[priority]:\r\n"
"This optional parameter is used for setting the priority of the\r\n"
"write. If no priority is given, none is sent, and the BACnet \r\n"
"standard requires that the value is written at the lowest \r\n"
"priority (16) if the object property supports priorities.\r\n"
"\r\n"
"[index]\r\n"
"This optional integer parameter is the index number of an array.\r\n"
"If the property is an array, individual elements can be written\r\n"
"to if supported.\r\n"
"\r\n"
"Here is a brief overview of BACnet property and tags:\r\n"
"Certain properties are expected to be written with certain \r\n"
"application tags, so you probably need to know which ones to use\r\n"
"with each property of each object. It is almost safe to say that\r\n"
"given a property and an object and a table, the tag could be looked\r\n"
"up automatically. There may be a few exceptions to this, such as\r\n"
"the Any property type in the schedule object and the Present Value\r\n"
"accepting REAL, BOOLEAN, NULL, etc. Perhaps it would be simpler for\r\n"
"the demo to use this kind of table - but I also wanted to be able\r\n"
"to do negative testing by passing the wrong tag and have the server\r\n"
"return a reject message.\r\n"
"\r\n"
"Example:\r\n"
"If you want send a 100 to the Present-Value in the Analog Output\r\n"
"at priority 16, you could send the following command:\r\n"
"%s 123 1 0 85 4 100\r\n"
"You could also send a relinquish command:\r\n"
"%s 123 1 0 85 0 0\r\n",
filename_remove_path(argv[0]),
filename_remove_path(argv[0]));
}
return 0;
}
/* decode the command line parameters */
Target_Device_Object_Instance = strtol(argv[1], NULL, 0);
Target_Object_Type = strtol(argv[2], NULL, 0);
Target_Object_Instance = strtol(argv[3], NULL, 0);
Target_Object_Property = strtol(argv[4], NULL, 0);
Target_Object_Property_Tag = strtol(argv[5], NULL, 0);
value_string = argv[6];
/* optional priority */
if (argc > 7)
Target_Object_Property_Priority = strtol(argv[7], NULL, 0);
/* optional index */
if (argc > 8)
Target_Object_Property_Index = strtol(argv[8], NULL, 0);
if (Target_Device_Object_Instance > BACNET_MAX_INSTANCE) {
fprintf(stderr, "device-instance=%u - it must be less than %u\r\n",
Target_Device_Object_Instance, BACNET_MAX_INSTANCE + 1);
return 1;
}
if (Target_Object_Type > MAX_BACNET_OBJECT_TYPE) {
fprintf(stderr, "object-type=%u - it must be less than %u\r\n",
Target_Object_Type, MAX_BACNET_OBJECT_TYPE + 1);
return 1;
}
if (Target_Object_Instance > BACNET_MAX_INSTANCE) {
fprintf(stderr, "object-instance=%u - it must be less than %u\r\n",
Target_Object_Instance, BACNET_MAX_INSTANCE + 1);
return 1;
}
if (Target_Object_Property > MAX_BACNET_PROPERTY_ID) {
fprintf(stderr, "object-type=%u - it must be less than %u\r\n",
Target_Object_Property, MAX_BACNET_PROPERTY_ID + 1);
return 1;
}
if (Target_Object_Property_Tag >= MAX_BACNET_APPLICATION_TAG) {
fprintf(stderr, "tag=%u - it must be less than %u\r\n",
Target_Object_Property_Tag, MAX_BACNET_APPLICATION_TAG);
return 1;
}
status = bacapp_parse_application_data(Target_Object_Property_Tag,
value_string, &Target_Object_Property_Value);
if (!status) {
/* FIXME: show the expected entry format for the tag */
fprintf(stderr, "unable to parse the tag value\r\n");
return 1;
}
/* setup my info */
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
address_init();
Init_Service_Handlers();
/* configure standard BACnet/IP port */
bip_set_interface("eth0"); /* for linux */
bip_set_port(0xBAC0);
if (!bip_init())
return 1;
/* configure the timeout values */
last_seconds = time(NULL);
timeout_seconds = (Device_APDU_Timeout() / 1000) *
Device_Number_Of_APDU_Retries();
/* try to bind with the device */
Send_WhoIs(Target_Device_Object_Instance,
Target_Device_Object_Instance);
/* loop forever */
for (;;) {
/* increment timer - exit if timed out */
current_seconds = time(NULL);
/* returns 0 bytes on timeout */
pdu_len = bip_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
/* process */
if (pdu_len) {
npdu_handler(&src, &Rx_Buf[0], pdu_len);
}
/* at least one second has passed */
if (current_seconds != last_seconds)
tsm_timer_milliseconds(((current_seconds -
last_seconds) * 1000));
if (Error_Detected)
break;
/* wait until the device is bound, or timeout and quit */
found = address_bind_request(Target_Device_Object_Instance,
&max_apdu, &Target_Address);
if (found) {
if (invoke_id == 0) {
invoke_id =
Send_Write_Property_Request
(Target_Device_Object_Instance, Target_Object_Type,
Target_Object_Instance, Target_Object_Property,
&Target_Object_Property_Value,
Target_Object_Property_Priority,
Target_Object_Property_Index);
} else if (tsm_invoke_id_free(invoke_id))
break;
else if (tsm_invoke_id_failed(invoke_id)) {
fprintf(stderr, "\rError: TSM Timeout!\r\n");
tsm_free_invoke_id(invoke_id);
/* try again or abort? */
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");
break;
}
}
/* keep track of time for next check */
last_seconds = current_seconds;
}
return 0;
}
/**************************************************************************
*
* Copyright (C) 2006-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.
*
*********************************************************************/
/* command line tool that sends a BACnet service, and displays the response */
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h> /* for time */
#include <string.h>
#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"
/* some demo stuff needed */
#include "filename.h"
#include "handlers.h"
#include "client.h"
#include "txbuf.h"
/* buffer used for receive */
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
/* global variables used in this file */
static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE;
static uint32_t Target_Object_Instance = BACNET_MAX_INSTANCE;
static BACNET_OBJECT_TYPE Target_Object_Type = OBJECT_ANALOG_INPUT;
static BACNET_PROPERTY_ID Target_Object_Property = PROP_ACKED_TRANSITIONS;
/* array index value or BACNET_ARRAY_ALL */
static int32_t Target_Object_Property_Index = BACNET_ARRAY_ALL;
#define MAX_PROPERTY_VALUES 16
static BACNET_APPLICATION_DATA_VALUE Target_Object_Property_Value[MAX_PROPERTY_VALUES];
/* 0 if not set, 1..16 if set */
static uint8_t Target_Object_Property_Priority = 0;
static BACNET_ADDRESS Target_Address;
static bool Error_Detected = false;
static void MyErrorHandler(BACNET_ADDRESS * src,
uint8_t invoke_id,
BACNET_ERROR_CLASS error_class, BACNET_ERROR_CODE error_code)
{
/* FIXME: verify src and invoke id */
(void) src;
(void) invoke_id;
printf("\r\nBACnet Error!\r\n");
printf("Error Class: %s\r\n", bactext_error_class_name(error_class));
printf("Error Code: %s\r\n", 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("\r\nBACnet Abort!\r\n");
printf("Abort Reason: %s\r\n",
bactext_abort_reason_name(abort_reason));
Error_Detected = true;
}
void MyRejectHandler(BACNET_ADDRESS * src,
uint8_t invoke_id, uint8_t reject_reason)
{
/* FIXME: verify src and invoke id */
(void) src;
(void) invoke_id;
printf("\r\nBACnet Reject!\r\n");
printf("Reject Reason: %s\r\n",
bactext_reject_reason_name(reject_reason));
Error_Detected = true;
}
void MyWritePropertySimpleAckHandler(BACNET_ADDRESS * src,
uint8_t invoke_id)
{
(void) src;
(void) invoke_id;
printf("\r\nWriteProperty Acknowledged!\r\n");
}
static void Init_Service_Handlers(void)
{
/* we need to handle who-is
to support dynamic device binding to us */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS,
handler_who_is);
/* handle i-am to support binding to other devices */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM,
handler_i_am_bind);
/* set the handler for all the services we don't implement
It is required to send the proper reject message... */
apdu_set_unrecognized_service_handler_handler
(handler_unrecognized_service);
/* we must implement read property - it's required! */
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
handler_read_property);
/* handle the ack coming back */
apdu_set_confirmed_simple_ack_handler(SERVICE_CONFIRMED_WRITE_PROPERTY,
MyWritePropertySimpleAckHandler);
/* handle any errors coming back */
apdu_set_error_handler(SERVICE_CONFIRMED_WRITE_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;
uint8_t invoke_id = 0;
bool found = false;
char *value_string = NULL;
bool status = false;
int args_remaining = 0, tag_value_arg = 0, i = 0;
BACNET_APPLICATION_TAG property_tag;
uint8_t context_tag = 0;
bool context_tagged = false;
if (argc < 9) {
/* note: priority 16 and 0 should produce the same end results... */
printf("Usage: %s device-instance object-type object-instance "
"property priority index tag value [tag value...]\r\n",
filename_remove_path(argv[0]));
if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) {
printf("device-instance:\r\n"
"BACnet Device Object Instance number that you are trying to\r\n"
"communicate to. This number will be used to try and bind with\r\n"
"the device using Who-Is and I-Am services. For example, if you were\r\n"
"writing to Device Object 123, the device-instance would be 123.\r\n"
"\r\n"
"object-type:\r\n"
"The object type is the integer value of the enumeration\r\n"
"BACNET_OBJECT_TYPE in bacenum.h. It is the object that you are\r\n"
"writing to. For example if you were writing to Analog Output 2, \r\n"
"the object-type would be 1.\r\n"
"\r\n"
"object-instance:\r\n"
"This is the object instance number of the object that you are \r\n"
"writing to. For example, if you were writing to Analog Output 2, \r\n"
"the object-instance would be 2.\r\n"
"\r\n"
"property:\r\n"
"The property is an integer value of the enumeration \r\n"
"BACNET_PROPERTY_ID in bacenum.h. It is the property you are \r\n"
"writing to. For example, if you were writing to the Present Value\r\n"
"property, you would use 85 as the property.\r\n"
"\r\n"
"priority:\r\n"
"This parameter is used for setting the priority of the\r\n"
"write. If Priority 0 is given, no priority is sent. The BACnet \r\n"
"standard states that the value is written at the lowest \r\n"
"priority (16) if the object property supports priorities\r\n"
"when no priority is sent.\r\n"
"\r\n"
"index\r\n"
"This integer parameter is the index number of an array.\r\n"
"If the property is an array, individual elements can be written\r\n"
"to if supported. If this parameter is -1, the index is ignored.\r\n"
"\r\n"
"tag:\r\n"
"Tag is the integer value of the enumeration BACNET_APPLICATION_TAG \r\n"
"in bacenum.h. It is the data type of the value that you are\r\n"
"writing. For example, if you were writing a REAL value, you would \r\n"
"use a tag of 4.\r\n"
"Context tags are created using two tags in a row. The context tag\r\n"
"is preceded by a C. Ctag tag. C2 4 creates a context 2 tagged REAL.\r\n"
"\r\n"
"value:\r\n"
"The value is an ASCII representation of some type of data that you\r\n"
"are writing. It is encoded using the tag information provided. For\r\n"
"example, if you were writing a REAL value of 100.0, you would use \r\n"
"100.0 as the value.\r\n"
"\r\n"
"Here is a brief overview of BACnet property and tags:\r\n"
"Certain properties are expected to be written with certain \r\n"
"application tags, so you probably need to know which ones to use\r\n"
"with each property of each object. It is almost safe to say that\r\n"
"given a property and an object and a table, the tag could be looked\r\n"
"up automatically. There may be a few exceptions to this, such as\r\n"
"the Any property type in the schedule object and the Present Value\r\n"
"accepting REAL, BOOLEAN, NULL, etc. Perhaps it would be simpler for\r\n"
"the demo to use this kind of table - but I also wanted to be able\r\n"
"to do negative testing by passing the wrong tag and have the server\r\n"
"return a reject message.\r\n"
"\r\n"
"Example:\r\n"
"If you want send a 100 to the Present-Value in the Analog Output\r\n"
"at priority 16, you could send the following command:\r\n"
"%s 123 1 0 85 4 100\r\n"
"You could also send a relinquish command:\r\n"
"%s 123 1 0 85 0 0\r\n",
filename_remove_path(argv[0]),
filename_remove_path(argv[0]));
}
return 0;
}
/* decode the command line parameters */
Target_Device_Object_Instance = strtol(argv[1], NULL, 0);
Target_Object_Type = strtol(argv[2], NULL, 0);
Target_Object_Instance = strtol(argv[3], NULL, 0);
Target_Object_Property = strtol(argv[4], NULL, 0);
Target_Object_Property_Priority = strtol(argv[5], NULL, 0);
Target_Object_Property_Index = strtol(argv[6], NULL, 0);
if (Target_Object_Property_Index == -1)
Target_Object_Property_Index = BACNET_ARRAY_ALL;
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 + 1);
return 1;
}
if (Target_Object_Type > MAX_BACNET_OBJECT_TYPE) {
fprintf(stderr, "object-type=%u - it must be less than %u\r\n",
Target_Object_Type, MAX_BACNET_OBJECT_TYPE + 1);
return 1;
}
if (Target_Object_Instance > BACNET_MAX_INSTANCE) {
fprintf(stderr, "object-instance=%u - it must be less than %u\r\n",
Target_Object_Instance, BACNET_MAX_INSTANCE + 1);
return 1;
}
if (Target_Object_Property > MAX_BACNET_PROPERTY_ID) {
fprintf(stderr, "object-type=%u - it must be less than %u\r\n",
Target_Object_Property, MAX_BACNET_PROPERTY_ID + 1);
return 1;
}
args_remaining = (argc - 7);
for (i = 0; i < MAX_PROPERTY_VALUES; i++) {
tag_value_arg = 7+(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);
value_string = argv[tag_value_arg+1];
args_remaining -= 2;
/* printf("tag[%d]=%u value[%d]=%s\r\n",
i, property_tag, i, value_string); */
if (property_tag >= MAX_BACNET_APPLICATION_TAG) {
fprintf(stderr, "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, "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;
}
/* setup my info */
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
address_init();
Init_Service_Handlers();
/* configure standard BACnet/IP port */
bip_set_interface("eth0"); /* for linux */
bip_set_port(0xBAC0);
if (!bip_init())
return 1;
/* configure the timeout values */
last_seconds = time(NULL);
timeout_seconds = (Device_APDU_Timeout() / 1000) *
Device_Number_Of_APDU_Retries();
/* try to bind with the device */
Send_WhoIs(Target_Device_Object_Instance,
Target_Device_Object_Instance);
/* loop forever */
for (;;) {
/* increment timer - exit if timed out */
current_seconds = time(NULL);
/* returns 0 bytes on timeout */
pdu_len = bip_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
/* process */
if (pdu_len) {
npdu_handler(&src, &Rx_Buf[0], pdu_len);
}
/* at least one second has passed */
if (current_seconds != last_seconds)
tsm_timer_milliseconds(((current_seconds -
last_seconds) * 1000));
if (Error_Detected)
break;
/* wait until the device is bound, or timeout and quit */
found = address_bind_request(Target_Device_Object_Instance,
&max_apdu, &Target_Address);
if (found) {
if (invoke_id == 0) {
invoke_id =
Send_Write_Property_Request(
Target_Device_Object_Instance, Target_Object_Type,
Target_Object_Instance, Target_Object_Property,
&Target_Object_Property_Value[0],
Target_Object_Property_Priority,
Target_Object_Property_Index);
} else if (tsm_invoke_id_free(invoke_id))
break;
else if (tsm_invoke_id_failed(invoke_id)) {
fprintf(stderr, "\rError: TSM Timeout!\r\n");
tsm_free_invoke_id(invoke_id);
/* try again or abort? */
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");
break;
}
}
/* keep track of time for next check */
last_seconds = current_seconds;
}
return 0;
}
+1 -2
View File
@@ -17,7 +17,7 @@ PRODUCT_EXE = $(PRODUCT).exe
# Choose the Data Link Layer to Enable
DEFINES = -DBACDL_BIP=1;TSM_ENABLED=1;BIG_ENDIAN=0;PRINT_ENABLED=1
SRCS = writeprop.c \
SRCS = main.c \
..\..\ports\win32\bip-init.c \
..\..\filename.c \
..\..\bip.c \
@@ -34,7 +34,6 @@ SRCS = writeprop.c \
..\..\bactext.c \
..\..\datetime.c \
..\..\indtext.c \
..\..\bigend.c \
..\..\whois.c \
..\..\iam.c \
..\..\rp.c \