diff --git a/bacnet-stack/demo/readprop/Makefile b/bacnet-stack/demo/readprop/Makefile index 3077c344..e15a87b4 100644 --- a/bacnet-stack/demo/readprop/Makefile +++ b/bacnet-stack/demo/readprop/Makefile @@ -2,7 +2,7 @@ # Compiler to use CC = gcc # Executable file name -TARGET = bacrpm +TARGET = bacrp # Configure the BACnet Datalink Layer #BACDL_DEFINE = -DBACDL_ETHERNET=1 diff --git a/bacnet-stack/demo/readprop/main.c b/bacnet-stack/demo/readprop/main.c index 72017fed..ef82b2a4 100644 --- a/bacnet-stack/demo/readprop/main.c +++ b/bacnet-stack/demo/readprop/main.c @@ -1,5 +1,5 @@ /************************************************************************* -* Copyright (C) 2008 Steve Karg +* 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 @@ -47,7 +47,6 @@ #include "datalink.h" #include "whois.h" /* some demo stuff needed */ -#include "rpm.h" #include "filename.h" #include "handlers.h" #include "client.h" @@ -58,7 +57,10 @@ static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; /* global variables used in this file */ static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; -static BACNET_READ_ACCESS_DATA *Read_Access_Data; +static uint32_t Target_Object_Instance = BACNET_MAX_INSTANCE; +static BACNET_OBJECT_TYPE Target_Object_Type = OBJECT_ANALOG_INPUT; +static BACNET_PROPERTY_ID Target_Object_Property = PROP_ACKED_TRANSITIONS; +static int32_t Target_Object_Index = BACNET_ARRAY_ALL; static BACNET_ADDRESS Target_Address; static bool Error_Detected = false; @@ -122,8 +124,8 @@ static void Init_Service_Handlers( apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, handler_read_property); /* handle the data coming back from confirmed requests */ - apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, - handler_read_property_multiple_ack); + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property_ack); /* handle any errors coming back */ apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler); apdu_set_abort_handler(MyAbortHandler); @@ -216,49 +218,22 @@ static void Init_DataLink( #endif } -void cleanup(void) -{ - BACNET_READ_ACCESS_DATA *rpm_object; - BACNET_READ_ACCESS_DATA *old_rpm_object; - BACNET_PROPERTY_REFERENCE *rpm_property; - BACNET_PROPERTY_REFERENCE *old_rpm_property; - - rpm_object = Read_Access_Data; - old_rpm_object = rpm_object; - while (rpm_object) { - rpm_property = rpm_object->listOfProperties; - while (rpm_property) { - old_rpm_property = rpm_property; - rpm_property = rpm_property->next; - free(old_rpm_property); - } - old_rpm_object = rpm_object; - rpm_object = rpm_object->next; - free(old_rpm_object); - } -} - int main(int argc, char *argv[]) { BACNET_ADDRESS src = { 0}; /* address where message came from */ uint16_t pdu_len = 0; unsigned timeout = 100; /* milliseconds */ unsigned max_apdu = 0; - int args_remaining = 0, tag_value_arg = 0, arg_sets = 0; time_t elapsed_seconds = 0; time_t last_seconds = 0; time_t current_seconds = 0; time_t timeout_seconds = 0; uint8_t invoke_id = 0; bool found = false; - uint8_t buffer[MAX_PDU] = {0}; - BACNET_READ_ACCESS_DATA *rpm_object; - BACNET_PROPERTY_REFERENCE *rpm_property; - if (argc < 5) { printf("Usage: %s device-instance object-type object-instance " - "property index [object-type ...]\r\n", filename_remove_path(argv[0])); + "property [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\r\n" @@ -286,94 +261,44 @@ int main(int argc, char *argv[]) { "be read. If this parameter is missing and the property\r\n" "is an array, the entire array will be read.\r\n" "\r\nExample:\r\n" - "If you want read the ALL property in\r\n" - "Device object 123, you would use the following command:\r\n" - "%s 123 8 123 8 -1\r\n" - "If you want read the OPTIONAL property in\r\n" - "Device object 123, you would use the following command:\r\n" - "%s 123 8 123 80 -1\r\n" - "If you want read the REQUIRED property in\r\n" - "Device object 123, you would use the following command:\r\n" - "%s 123 8 123 105 -1\r\n", - filename_remove_path(argv[0]), - filename_remove_path(argv[0]), + "If you want read the Present-Value of Analog Output 101\r\n" + "in Device 123, you could send the following command:\r\n" + "%s 123 1 101 85\r\n" + "If you want read the Priority-Array of Analog Output 101\r\n" + "in Device 123, you could send the following command:\r\n" + "%s 123 1 101 87\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); + if (argc > 5) + Target_Object_Index = strtol(argv[5], NULL, 0); if (Target_Device_Object_Instance >= BACNET_MAX_INSTANCE) { fprintf(stderr, "device-instance=%u - it must be less than %u\r\n", Target_Device_Object_Instance, BACNET_MAX_INSTANCE); return 1; } - atexit(cleanup); - Read_Access_Data = calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); - rpm_object = Read_Access_Data; - args_remaining = (argc - 2); - arg_sets = 0; - while (rpm_object) { - tag_value_arg = 2 + (arg_sets * 4); - rpm_object->object_type = - strtol(argv[tag_value_arg], NULL, 0); - tag_value_arg++; - args_remaining--; - if (args_remaining <= 0) { - fprintf(stderr, "Error: not enough object property quads.\r\n"); - return 1; - } - if (rpm_object->object_type > MAX_BACNET_OBJECT_TYPE) { - fprintf(stderr, "object-type=%u - it must be less than %u\r\n", - rpm_object->object_type, MAX_BACNET_OBJECT_TYPE + 1); - return 1; - } - rpm_object->object_instance = - strtol(argv[tag_value_arg], NULL, 0); - tag_value_arg++; - args_remaining--; - if (args_remaining <= 0) { - fprintf(stderr, "Error: not enough object property quads.\r\n"); - return 1; - } - if (rpm_object->object_instance > BACNET_MAX_INSTANCE) { - fprintf(stderr, "object-instance=%u - it must be less than %u\r\n", - rpm_object->object_instance, BACNET_MAX_INSTANCE + 1); - return 1; - } - rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE)); - rpm_object->listOfProperties = rpm_property; - if (rpm_property) { - rpm_property->propertyIdentifier = - strtol(argv[tag_value_arg], NULL, 0); - /* used up another arg */ - tag_value_arg++; - args_remaining--; - if (args_remaining <= 0) { - fprintf(stderr, "Error: not enough object property quads.\r\n"); - return 1; - } - if (rpm_property->propertyIdentifier > MAX_BACNET_PROPERTY_ID) { - fprintf(stderr, "property=%u - it must be less than %u\r\n", - rpm_property->propertyIdentifier, MAX_BACNET_PROPERTY_ID + 1); - return 1; - } - rpm_property->propertyArrayIndex = - strtol(argv[tag_value_arg], NULL, 0); - /* note: we are only loading one property for now */ - rpm_property->next = NULL; - /* used up another arg */ - tag_value_arg++; - args_remaining--; - } - if (args_remaining) { - arg_sets++; - rpm_object->next = calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); - rpm_object = rpm_object->next; - } else { - break; - } + 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, "property=%u - it must be less than %u\r\n", + Target_Object_Property, MAX_BACNET_PROPERTY_ID + 1); + return 1; + } + /* setup my info */ Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); address_init(); @@ -413,11 +338,10 @@ int main(int argc, char *argv[]) { &Target_Address); if (found) { if (invoke_id == 0) { - invoke_id = Send_Read_Property_Multiple_Request( - &buffer[0], - sizeof(buffer), - Target_Device_Object_Instance, - Read_Access_Data); + invoke_id = + Send_Read_Property_Request(Target_Device_Object_Instance, + Target_Object_Type, Target_Object_Instance, + Target_Object_Property, Target_Object_Index); } else if (tsm_invoke_id_free(invoke_id)) break; else if (tsm_invoke_id_failed(invoke_id)) { diff --git a/bacnet-stack/demo/readprop/makefile.b32 b/bacnet-stack/demo/readprop/makefile.b32 index 888309e2..b0893dc4 100644 --- a/bacnet-stack/demo/readprop/makefile.b32 +++ b/bacnet-stack/demo/readprop/makefile.b32 @@ -11,7 +11,7 @@ BORLAND_DIR_Not_Defined: @echo You must define environment variable BORLAND_DIR to compile. !endif -PRODUCT = bacrpm +PRODUCT = bacrp PRODUCT_EXE = $(PRODUCT).exe # tools @@ -26,7 +26,7 @@ BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib # directories BACNET_PORT = ..\..\ports\win32 BACNET_INCLUDE = ..\..\include -INCLUDES = \ +INCLUDES = \ -I$(BACNET_INCLUDE) \ -I$(BACNET_PORT) \ -I$(BORLAND_DIR)\include @@ -65,7 +65,7 @@ LIBS = $(BACNET_LIB) \ # # This should be the first one in the makefile -all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) del $(BCC_CFG) install: $(PRODUCT_EXE) diff --git a/bacnet-stack/demo/readpropm/Makefile b/bacnet-stack/demo/readpropm/Makefile new file mode 100644 index 00000000..3077c344 --- /dev/null +++ b/bacnet-stack/demo/readpropm/Makefile @@ -0,0 +1,72 @@ +#Makefile to build BACnet Application for the Linux Port +# Compiler to use +CC = gcc +# Executable file name +TARGET = bacrpm + +# Configure the BACnet Datalink Layer +#BACDL_DEFINE = -DBACDL_ETHERNET=1 +#BACDL_DEFINE = -DBACDL_ARCNET=1 +#BACDL_DEFINE = -DBACDL_MSTP=1 +BACDL_DEFINE = -DBACDL_BIP=1 +BACNET_DEFINES = -DBACFILE=1 -DPRINT_ENABLED=1 -DBACAPP_ALL +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +# Directories +BACNET_PORT = linux +BACNET_PORT_DIR = ../../ports/${BACNET_PORT} +BACNET_INCLUDE = ../../include +# BACnet Library +BACNET_LIB_DIR = ../../lib +BACNET_LIB_NAME = bacnet +BACNET_LIB_TARGET = $(BACNET_LIB_DIR)/lib$(BACNET_LIB_NAME).a +# Compiler Setup +INCLUDES = -I$(BACNET_INCLUDE) -I$(BACNET_PORT_DIR) +ifeq (${BACNET_PORT},linux) +PFLAGS = -pthread +TARGET_BIN = ${TARGET} +LIBRARIES=-lc,-lgcc,-lm,-L=$(BACNET_LIB_DIR),-l$(BACNET_LIB_NAME) +endif +ifeq (${BACNET_PORT},win32) +TARGET_BIN = ${TARGET}.exe +LIBRARY1=-L=$(BACNET_LIB_DIR),-l$(BACNET_LIB_NAME) +LIBRARY2=-lws2_32,-lgcc,-lm,-liphlpapi +LIBRARIES=$(LIBRARY1),$(LIBRARY2) +endif +#build for release (default) or debug +DEBUGGING = +OPTIMIZATION = -Os +ifeq (${BUILD},debug) +OPTIMIZATION = -O0 +DEBUGGING = -g +endif +# put all the flags together +CFLAGS = -Wall $(DEBUGGING) $(OPTIMIZATION) $(INCLUDES) $(DEFINES) +LFLAGS = -Wl,-Map=$(TARGET).map,$(LIBRARIES) + +SRCS = main.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + size ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; make clean ; make ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} + +include: .depend diff --git a/bacnet-stack/demo/readprop/bacrpm.cbp b/bacnet-stack/demo/readpropm/bacrpm.cbp similarity index 100% rename from bacnet-stack/demo/readprop/bacrpm.cbp rename to bacnet-stack/demo/readpropm/bacrpm.cbp diff --git a/bacnet-stack/demo/readpropm/main.c b/bacnet-stack/demo/readpropm/main.c new file mode 100644 index 00000000..72017fed --- /dev/null +++ b/bacnet-stack/demo/readpropm/main.c @@ -0,0 +1,446 @@ +/************************************************************************* +* Copyright (C) 2008 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 +#include /* for time */ + +#define PRINT_ENABLED 1 + +#include "bacdef.h" +#include "config.h" +#include "bactext.h" +#include "bacerror.h" +#include "iam.h" +#include "arf.h" +#include "tsm.h" +#include "address.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "whois.h" +/* some demo stuff needed */ +#include "rpm.h" +#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 BACNET_READ_ACCESS_DATA *Read_Access_Data; + +static BACNET_ADDRESS Target_Address; +static bool Error_Detected = false; + +static void MyErrorHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Error: %s: %s\r\n", + bactext_error_class_name((int) error_class), + bactext_error_code_name((int) error_code)); + Error_Detected = true; +} + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + (void) server; + printf("BACnet Abort: %s\r\n", + bactext_abort_reason_name((int) abort_reason)); + Error_Detected = true; +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + /* FIXME: verify src and invoke id */ + (void) src; + (void) invoke_id; + printf("BACnet Reject: %s\r\n", + bactext_reject_reason_name((int) reject_reason)); + Error_Detected = true; +} + +static void Init_Service_Handlers( + void) +{ + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* handle i-am to support binding to other devices */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle the data coming back from confirmed requests */ + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, + handler_read_property_multiple_ack); + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +static void Init_DataLink( + void) +{ + char *pEnv = NULL; +#if defined(BACDL_BIP) && BBMD_ENABLED + long bbmd_port = 0xBAC0; + long bbmd_address = 0; + long bbmd_timetolive_seconds = 60000; +#endif + +#if defined(BACDL_ALL) + pEnv = getenv("BACNET_DATALINK"); + if (pEnv) { + datalink_set(pEnv)); + } else { + datalink_set(NULL); + } +#endif + +#if defined(BACDL_BIP) + pEnv = getenv("BACNET_IP_PORT"); + if (pEnv) { + bip_set_port(strtol(pEnv, NULL, 0)); + } else { + bip_set_port(0xBAC0); + } +#elif defined(BACDL_MSTP) + pEnv = getenv("BACNET_MAX_INFO_FRAMES"); + if (pEnv) { + dlmstp_set_max_info_frames(strtol(pEnv, NULL, 0)); + } else { + dlmstp_set_max_info_frames(1); + } + pEnv = getenv("BACNET_MAX_MASTER"); + if (pEnv) { + dlmstp_set_max_master(strtol(pEnv, NULL, 0)); + } else { + dlmstp_set_max_master(127); + } + pEnv = getenv("BACNET_MSTP_BAUD"); + if (pEnv) { + RS485_Set_Baud_Rate(strtol(pEnv, NULL, 0)); + } else { + RS485_Set_Baud_Rate(38400); + } + pEnv = getenv("BACNET_MSTP_MAC"); + if (pEnv) { + dlmstp_set_mac_address(strtol(pEnv, NULL, 0)); + } else { + dlmstp_set_mac_address(127); + } +#endif + if (!datalink_init(getenv("BACNET_IFACE"))) { + exit(1); + } +#if defined(BACDL_BIP) && BBMD_ENABLED + pEnv = getenv("BACNET_BBMD_PORT"); + if (pEnv) { + bbmd_port = strtol(pEnv, NULL, 0); + if (bbmd_port > 0xFFFF) { + bbmd_port = 0xBAC0; + } + } + pEnv = getenv("BACNET_BBMD_TIMETOLIVE"); + if (pEnv) { + bbmd_timetolive_seconds = strtol(pEnv, NULL, 0); + if (bbmd_timetolive_seconds > 0xFFFF) { + bbmd_timetolive_seconds = 0xFFFF; + } + } + pEnv = getenv("BACNET_BBMD_ADDRESS"); + if (pEnv) { + bbmd_address = bip_getaddrbyname(pEnv); + if (bbmd_address) { + struct in_addr addr; + addr.s_addr = bbmd_address; + printf + ("ReadProperty: Registering with BBMD at %s:%ld for %ld seconds\n", + inet_ntoa(addr), bbmd_port, bbmd_timetolive_seconds); + bvlc_register_with_bbmd(bbmd_address, bbmd_port, + bbmd_timetolive_seconds); + } + } +#endif +} + +void cleanup(void) +{ + BACNET_READ_ACCESS_DATA *rpm_object; + BACNET_READ_ACCESS_DATA *old_rpm_object; + BACNET_PROPERTY_REFERENCE *rpm_property; + BACNET_PROPERTY_REFERENCE *old_rpm_property; + + rpm_object = Read_Access_Data; + old_rpm_object = rpm_object; + while (rpm_object) { + rpm_property = rpm_object->listOfProperties; + while (rpm_property) { + old_rpm_property = rpm_property; + rpm_property = rpm_property->next; + free(old_rpm_property); + } + old_rpm_object = rpm_object; + rpm_object = rpm_object->next; + free(old_rpm_object); + } +} + +int main(int argc, char *argv[]) { + BACNET_ADDRESS src = { + 0}; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + unsigned max_apdu = 0; + int args_remaining = 0, tag_value_arg = 0, arg_sets = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + uint8_t invoke_id = 0; + bool found = false; + uint8_t buffer[MAX_PDU] = {0}; + BACNET_READ_ACCESS_DATA *rpm_object; + BACNET_PROPERTY_REFERENCE *rpm_property; + + + if (argc < 5) { + printf("Usage: %s device-instance object-type object-instance " + "property index [object-type ...]\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\r\n" + "trying to communicate to. This number will be used\r\n" + "to try and bind with the device using Who-Is and\r\n" + "I-Am services. For example, if you were reading\r\n" + "Device Object 123, the device-instance would be 123.\r\n" + "\r\nobject-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\r\n" + "that you are reading. For example if you were\r\n" + "reading Analog Output 2, the object-type would be 1.\r\n" + "\r\nobject-instance:\r\n" + "This is the object instance number of the object that\r\n" + "you are reading. For example, if you were reading\r\n" + "Analog Output 2, the object-instance would be 2.\r\n" + "\r\nproperty:\r\n" + "The property is an integer value of the enumeration\r\n" + "BACNET_PROPERTY_ID in bacenum.h. It is the property\r\n" + "you are reading. For example, if you were reading the\r\n" + "Present Value property, use 85 as the property.\r\n" + "\r\nindex:\r\n" + "This integer parameter is the index number of an array.\r\n" + "If the property is an array, individual elements can\r\n" + "be read. If this parameter is missing and the property\r\n" + "is an array, the entire array will be read.\r\n" + "\r\nExample:\r\n" + "If you want read the ALL property in\r\n" + "Device object 123, you would use the following command:\r\n" + "%s 123 8 123 8 -1\r\n" + "If you want read the OPTIONAL property in\r\n" + "Device object 123, you would use the following command:\r\n" + "%s 123 8 123 80 -1\r\n" + "If you want read the REQUIRED property in\r\n" + "Device object 123, you would use the following command:\r\n" + "%s 123 8 123 105 -1\r\n", + filename_remove_path(argv[0]), + 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); + if (Target_Device_Object_Instance >= BACNET_MAX_INSTANCE) { + fprintf(stderr, "device-instance=%u - it must be less than %u\r\n", + Target_Device_Object_Instance, BACNET_MAX_INSTANCE); + return 1; + } + atexit(cleanup); + Read_Access_Data = calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); + rpm_object = Read_Access_Data; + args_remaining = (argc - 2); + arg_sets = 0; + while (rpm_object) { + tag_value_arg = 2 + (arg_sets * 4); + rpm_object->object_type = + strtol(argv[tag_value_arg], NULL, 0); + tag_value_arg++; + args_remaining--; + if (args_remaining <= 0) { + fprintf(stderr, "Error: not enough object property quads.\r\n"); + return 1; + } + if (rpm_object->object_type > MAX_BACNET_OBJECT_TYPE) { + fprintf(stderr, "object-type=%u - it must be less than %u\r\n", + rpm_object->object_type, MAX_BACNET_OBJECT_TYPE + 1); + return 1; + } + rpm_object->object_instance = + strtol(argv[tag_value_arg], NULL, 0); + tag_value_arg++; + args_remaining--; + if (args_remaining <= 0) { + fprintf(stderr, "Error: not enough object property quads.\r\n"); + return 1; + } + if (rpm_object->object_instance > BACNET_MAX_INSTANCE) { + fprintf(stderr, "object-instance=%u - it must be less than %u\r\n", + rpm_object->object_instance, BACNET_MAX_INSTANCE + 1); + return 1; + } + rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE)); + rpm_object->listOfProperties = rpm_property; + if (rpm_property) { + rpm_property->propertyIdentifier = + strtol(argv[tag_value_arg], NULL, 0); + /* used up another arg */ + tag_value_arg++; + args_remaining--; + if (args_remaining <= 0) { + fprintf(stderr, "Error: not enough object property quads.\r\n"); + return 1; + } + if (rpm_property->propertyIdentifier > MAX_BACNET_PROPERTY_ID) { + fprintf(stderr, "property=%u - it must be less than %u\r\n", + rpm_property->propertyIdentifier, MAX_BACNET_PROPERTY_ID + 1); + return 1; + } + rpm_property->propertyArrayIndex = + strtol(argv[tag_value_arg], NULL, 0); + /* note: we are only loading one property for now */ + rpm_property->next = NULL; + /* used up another arg */ + tag_value_arg++; + args_remaining--; + } + if (args_remaining) { + arg_sets++; + rpm_object->next = calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); + rpm_object = rpm_object->next; + } else { + break; + } + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + address_init(); + Init_Service_Handlers(); + Init_DataLink(); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); + /* try to bind with the device */ + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + if (!found) { + Send_WhoIs(Target_Device_Object_Instance, + Target_Device_Object_Instance); + } + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + /* at least one second has passed */ + if (current_seconds != last_seconds) + tsm_timer_milliseconds(((current_seconds - last_seconds) * 1000)); + if (Error_Detected) + break; + /* wait until the device is bound, or timeout and quit */ + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + if (found) { + if (invoke_id == 0) { + invoke_id = Send_Read_Property_Multiple_Request( + &buffer[0], + sizeof(buffer), + Target_Device_Object_Instance, + Read_Access_Data); + } 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); + Error_Detected = true; + /* try again or abort? */ + break; + } + } else { + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) { + printf("\rError: APDU Timeout!\r\n"); + Error_Detected = true; + break; + } + } + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + if (Error_Detected) + return 1; + return 0; +} diff --git a/bacnet-stack/demo/readpropm/makefile.b32 b/bacnet-stack/demo/readpropm/makefile.b32 new file mode 100644 index 00000000..888309e2 --- /dev/null +++ b/bacnet-stack/demo/readpropm/makefile.b32 @@ -0,0 +1,134 @@ +# +# Simple makefile to build an executable for Win32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = bacrpm +PRODUCT_EXE = $(PRODUCT).exe + +# tools +CC = $(BORLAND_DIR)\bin\bcc32 +MAKE=$(BORLAND_DIR)\bin\make.exe +#LINK = $(BORLAND_DIR)\bin\tlink32 +LINK = $(BORLAND_DIR)\bin\ilink32 + +BACNET_LIB_DIR = ..\..\lib +BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib + +# directories +BACNET_PORT = ..\..\ports\win32 +BACNET_INCLUDE = ..\..\include +INCLUDES = \ + -I$(BACNET_INCLUDE) \ + -I$(BACNET_PORT) \ + -I$(BORLAND_DIR)\include + +# +BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL +#BACDL_DEFINE=-DBACDL_MSTP=1 +BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1 +DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE) + +SRCS = main.c + +OBJS = $(SRCS:.c=.obj) + +# +# Compiler definitions +# +BCC_CFG = bcc32.cfg + +# +# Include directories +# +CFLAGS = $(INCLUDES) $(DEFINES) + +# +# Libraries +# +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBS = $(BACNET_LIB) \ + $(C_LIB_DIR)\IMPORT32.lib \ + $(C_LIB_DIR)\CW32MT.lib \ + +# +# Main target +# +# This should be the first one in the makefile + +all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE) + del $(BCC_CFG) + +install: $(PRODUCT_EXE) + copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE) + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&| ... |) because command line is too long +# $** lists each dependency +# $< target name +# $* target name without extension +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&| + $(BORLAND_DIR)\lib\c0x32.obj $** + $< + $*.map + $(LIBS) +| + +# +# Utilities + +clean : + del $(OBJS) + del $(PRODUCT_EXE) + del $(PRODUCT).map + del $(PRODUCT).ilc + del $(PRODUCT).ild + del $(PRODUCT).ilf + del $(PRODUCT).ils + del $(PRODUCT).tds + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) +$(BCC_CFG) -o$@ $< + +# Compiler configuration file +$(BCC_CFG) : + Copy &&| +$(CFLAGS) +-c +-y #include line numbers in OBJ's +-v #include debug info +-w+ #turn on all warnings +-Od #disable all optimizations +#-a4 #32 bit data alignment +#-M # generate link map +#-ls # linker options +#-WM- #not multithread +-WM #multithread +-w-aus # ignore warning assigned a value that is never used +-w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile