diff --git a/bacnet-stack/bacapp.c b/bacnet-stack/bacapp.c index 7cf44fbf..d0625f9e 100644 --- a/bacnet-stack/bacapp.c +++ b/bacnet-stack/bacapp.c @@ -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; diff --git a/bacnet-stack/bacapp.h b/bacnet-stack/bacapp.h index 4c007ec3..54023082 100644 --- a/bacnet-stack/bacapp.h +++ b/bacnet-stack/bacapp.h @@ -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); diff --git a/bacnet-stack/bacdcode.c b/bacnet-stack/bacdcode.c index a8ccbf8b..9f68c312 100644 --- a/bacnet-stack/bacdcode.c +++ b/bacnet-stack/bacdcode.c @@ -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 */ diff --git a/bacnet-stack/bacdcode.h b/bacnet-stack/bacdcode.h index 1bd36150..585adc62 100644 --- a/bacnet-stack/bacdcode.h +++ b/bacnet-stack/bacdcode.h @@ -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 */ diff --git a/bacnet-stack/datetime.c b/bacnet-stack/datetime.c index d0e161df..05425dc2 100644 --- a/bacnet-stack/datetime.c +++ b/bacnet-stack/datetime.c @@ -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++; } diff --git a/bacnet-stack/demo/handler/client.h b/bacnet-stack/demo/handler/client.h index 459efee2..c69ee04e 100644 --- a/bacnet-stack/demo/handler/client.h +++ b/bacnet-stack/demo/handler/client.h @@ -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); diff --git a/bacnet-stack/demo/handler/s_wp.c b/bacnet-stack/demo/handler/s_wp.c index 461c7ade..9928c91c 100644 --- a/bacnet-stack/demo/handler/s_wp.c +++ b/bacnet-stack/demo/handler/s_wp.c @@ -25,6 +25,7 @@ #include #include #include +#include #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); +} \ No newline at end of file diff --git a/bacnet-stack/demo/writeprop/Makefile b/bacnet-stack/demo/writeprop/Makefile index deb042a3..d69edd8f 100644 --- a/bacnet-stack/demo/writeprop/Makefile +++ b/bacnet-stack/demo/writeprop/Makefile @@ -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 \ diff --git a/bacnet-stack/demo/writeprop/writeprop.c b/bacnet-stack/demo/writeprop/main.c similarity index 77% rename from bacnet-stack/demo/writeprop/writeprop.c rename to bacnet-stack/demo/writeprop/main.c index 09241376..78b29acf 100644 --- a/bacnet-stack/demo/writeprop/writeprop.c +++ b/bacnet-stack/demo/writeprop/main.c @@ -1,349 +1,375 @@ -/************************************************************************** -* -* Copyright (C) 2006 Steve Karg -* -* 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 -#include -#include -#include -#include /* for time */ -#include -#include -#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 +* +* 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 +#include +#include +#include +#include /* for time */ +#include +#include +#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; +} diff --git a/bacnet-stack/demo/writeprop/makefile.b32 b/bacnet-stack/demo/writeprop/makefile.b32 index 3ee7bf1d..02270b36 100644 --- a/bacnet-stack/demo/writeprop/makefile.b32 +++ b/bacnet-stack/demo/writeprop/makefile.b32 @@ -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 \