From ced9dff0f289e780023bc0d0173e41d2a3ca825e Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Sun, 16 Apr 2023 14:12:11 -0500 Subject: [PATCH] Added AddListElement and RemoveListElement services (#418) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added AddListElement and RemoveListElement services Added list-element codec and unit tests. Added BACnetDestination codec and unit tests. Added RecipientList handling to NotificationClass object. Added AddListElement and RemoveListElement client example app. * Fix defects found by scan-build and CPPCHECK * Update ports errors found during CI builds * Update zephy os test build missing files in CMakeLists --------- Co-authored-by: Ondřej Hruška Co-authored-by: Steve Karg --- .gitignore | 3 + CMakeLists.txt | 14 + Makefile | 8 + apps/Makefile | 10 +- apps/add-list-element/Makefile | 39 + apps/add-list-element/main.c | 480 +++++++++ apps/remove-list-element/Makefile | 39 + apps/remove-list-element/main.c | 480 +++++++++ ports/at91sam7s/Makefile | 1 + ports/at91sam7s/bacnet.ewp | 3 + ports/stm32f10x/Makefile | 1 + ports/stm32f10x/bacnet.ewp | 3 + ports/stm32f4xx/Makefile | 1 + ports/stm32f4xx/bacnet.ewp | 3 + ports/xplained/Makefile | 1 + ports/xplained/bacnet.cproj | 4 + src/bacnet/bacapp.c | 34 +- src/bacnet/bacapp.h | 2 + src/bacnet/bacdest.c | 993 ++++++++++++++++++ src/bacnet/bacdest.h | 104 ++ .../basic/object/client/device-client.c | 9 +- src/bacnet/basic/object/device.c | 161 ++- src/bacnet/basic/object/device.h | 11 + src/bacnet/basic/object/nc.c | 809 +++++++------- src/bacnet/basic/object/nc.h | 190 ++-- src/bacnet/basic/service/h_list_element.c | 211 ++++ src/bacnet/basic/service/h_list_element.h | 43 + src/bacnet/basic/service/s_list_element.c | 282 +++++ src/bacnet/basic/service/s_list_element.h | 75 ++ src/bacnet/basic/services.h | 2 + src/bacnet/list_element.c | 363 +++++++ src/bacnet/list_element.h | 83 ++ test/bacnet/bacapp/CMakeLists.txt | 2 + test/bacnet/bacdest/CMakeLists.txt | 62 ++ test/bacnet/bacdest/src/main.c | 160 +++ test/bacnet/bacdevobjpropref/CMakeLists.txt | 2 + test/bacnet/bactimevalue/CMakeLists.txt | 2 + .../basic/binding/address/CMakeLists.txt | 1 + test/bacnet/basic/object/acc/CMakeLists.txt | 2 + .../object/access_credential/CMakeLists.txt | 2 + .../basic/object/access_door/CMakeLists.txt | 2 + .../basic/object/access_point/CMakeLists.txt | 2 + .../basic/object/access_rights/CMakeLists.txt | 2 + .../basic/object/access_user/CMakeLists.txt | 2 + .../basic/object/access_zone/CMakeLists.txt | 2 + test/bacnet/basic/object/ai/CMakeLists.txt | 2 + test/bacnet/basic/object/ao/CMakeLists.txt | 2 + test/bacnet/basic/object/av/CMakeLists.txt | 2 + .../basic/object/bacfile/CMakeLists.txt | 2 + test/bacnet/basic/object/bi/CMakeLists.txt | 2 + test/bacnet/basic/object/bo/CMakeLists.txt | 2 + test/bacnet/basic/object/bv/CMakeLists.txt | 2 + .../basic/object/color_object/CMakeLists.txt | 2 + .../object/color_temperature/CMakeLists.txt | 2 + .../basic/object/command/CMakeLists.txt | 2 + .../credential_data_input/CMakeLists.txt | 2 + .../bacnet/basic/object/device/CMakeLists.txt | 1 + test/bacnet/basic/object/lc/CMakeLists.txt | 2 + test/bacnet/basic/object/lo/CMakeLists.txt | 2 + test/bacnet/basic/object/lsp/CMakeLists.txt | 2 + .../basic/object/ms-input/CMakeLists.txt | 2 + test/bacnet/basic/object/mso/CMakeLists.txt | 2 + test/bacnet/basic/object/msv/CMakeLists.txt | 2 + test/bacnet/basic/object/nc/CMakeLists.txt | 67 ++ test/bacnet/basic/object/nc/src/main.c | 61 ++ test/bacnet/basic/object/nc/stubs.c | 55 + .../basic/object/netport/CMakeLists.txt | 2 + test/bacnet/basic/object/osv/CMakeLists.txt | 2 + test/bacnet/basic/object/piv/CMakeLists.txt | 2 + .../basic/object/schedule/CMakeLists.txt | 2 + test/bacnet/cov/CMakeLists.txt | 2 + test/bacnet/getalarm/CMakeLists.txt | 2 + test/bacnet/getevent/CMakeLists.txt | 2 + test/bacnet/list_element/CMakeLists.txt | 62 ++ test/bacnet/list_element/src/main.c | 89 ++ test/bacnet/lso/CMakeLists.txt | 2 + test/bacnet/ptransfer/CMakeLists.txt | 2 + test/bacnet/rpm/CMakeLists.txt | 2 + test/bacnet/timesync/CMakeLists.txt | 2 + test/bacnet/weeklyschedule/CMakeLists.txt | 4 +- test/bacnet/wp/CMakeLists.txt | 2 + zephyr/CMakeLists.txt | 8 + zephyr/tests/bacnet/bacapp/CMakeLists.txt | 2 + .../bacnet/basic/object/acc/CMakeLists.txt | 2 + .../object/access_credential/CMakeLists.txt | 2 + .../basic/object/access_door/CMakeLists.txt | 2 + .../basic/object/access_point/CMakeLists.txt | 2 + .../basic/object/access_rights/CMakeLists.txt | 2 + .../basic/object/access_user/CMakeLists.txt | 2 + .../basic/object/access_zone/CMakeLists.txt | 2 + .../bacnet/basic/object/ai/CMakeLists.txt | 2 + .../bacnet/basic/object/ao/CMakeLists.txt | 2 + .../bacnet/basic/object/av/CMakeLists.txt | 2 + .../bacnet/basic/object/bi/CMakeLists.txt | 2 + .../bacnet/basic/object/bo/CMakeLists.txt | 2 + .../bacnet/basic/object/bv/CMakeLists.txt | 2 + .../basic/object/color_object/CMakeLists.txt | 2 + .../object/color_temperature/CMakeLists.txt | 2 + .../basic/object/command/CMakeLists.txt | 2 + .../credential_data_input/CMakeLists.txt | 2 + .../bacnet/basic/object/device/CMakeLists.txt | 2 + .../bacnet/basic/object/lc/CMakeLists.txt | 2 + .../bacnet/basic/object/lo/CMakeLists.txt | 2 + .../bacnet/basic/object/lsp/CMakeLists.txt | 2 + .../basic/object/ms-input/CMakeLists.txt | 2 + .../bacnet/basic/object/mso/CMakeLists.txt | 2 + .../bacnet/basic/object/msv/CMakeLists.txt | 2 + .../basic/object/netport/CMakeLists.txt | 2 + .../basic/object/objects/CMakeLists.txt | 2 + .../bacnet/basic/object/osv/CMakeLists.txt | 2 + .../bacnet/basic/object/piv/CMakeLists.txt | 2 + .../basic/object/schedule/CMakeLists.txt | 2 + zephyr/tests/bacnet/cov/CMakeLists.txt | 2 + zephyr/tests/bacnet/ptransfer/CMakeLists.txt | 2 + zephyr/tests/bacnet/rpm/CMakeLists.txt | 2 + zephyr/tests/bacnet/wp/CMakeLists.txt | 2 + 116 files changed, 4615 insertions(+), 563 deletions(-) create mode 100644 apps/add-list-element/Makefile create mode 100644 apps/add-list-element/main.c create mode 100644 apps/remove-list-element/Makefile create mode 100644 apps/remove-list-element/main.c create mode 100644 src/bacnet/bacdest.c create mode 100644 src/bacnet/bacdest.h create mode 100644 src/bacnet/basic/service/h_list_element.c create mode 100644 src/bacnet/basic/service/h_list_element.h create mode 100644 src/bacnet/basic/service/s_list_element.c create mode 100644 src/bacnet/basic/service/s_list_element.h create mode 100644 src/bacnet/list_element.c create mode 100644 src/bacnet/list_element.h create mode 100644 test/bacnet/bacdest/CMakeLists.txt create mode 100644 test/bacnet/bacdest/src/main.c create mode 100644 test/bacnet/basic/object/nc/CMakeLists.txt create mode 100644 test/bacnet/basic/object/nc/src/main.c create mode 100644 test/bacnet/basic/object/nc/stubs.c create mode 100644 test/bacnet/list_element/CMakeLists.txt create mode 100644 test/bacnet/list_element/src/main.c diff --git a/.gitignore b/.gitignore index 91a0547f..89f85416 100644 --- a/.gitignore +++ b/.gitignore @@ -109,3 +109,6 @@ apps/piface/libpifacedigital/ /apps/writepropm/bacwpm /apps/mstpcap/mstpcap /apps/mstpcrc/mstpcrc +/apps/add-list-element/bacale +/apps/remove-list-element/bacrle + diff --git a/CMakeLists.txt b/CMakeLists.txt index 6cea75a2..2c567b9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,6 +105,8 @@ add_library(${PROJECT_NAME} src/bacnet/bacdcode.c src/bacnet/bacdcode.h src/bacnet/bacdef.h + src/bacnet/bacdest.c + src/bacnet/bacdest.h src/bacnet/bacdevobjpropref.c src/bacnet/bacdevobjpropref.h src/bacnet/bacenum.h @@ -243,6 +245,8 @@ add_library(${PROJECT_NAME} src/bacnet/basic/service/h_ihave.h src/bacnet/basic/service/h_lso.c src/bacnet/basic/service/h_lso.h + src/bacnet/basic/service/h_list_element.c + src/bacnet/basic/service/h_list_element.h src/bacnet/basic/service/h_noserv.c src/bacnet/basic/service/h_noserv.h src/bacnet/basic/service/h_rd.c @@ -299,6 +303,8 @@ add_library(${PROJECT_NAME} src/bacnet/basic/service/s_iam.h src/bacnet/basic/service/s_ihave.c src/bacnet/basic/service/s_ihave.h + src/bacnet/basic/service/s_list_element.c + src/bacnet/basic/service/s_list_element.h src/bacnet/basic/service/s_lso.c src/bacnet/basic/service/s_lso.h src/bacnet/basic/service/s_rd.c @@ -397,6 +403,8 @@ add_library(${PROJECT_NAME} src/bacnet/indtext.h src/bacnet/lighting.c src/bacnet/lighting.h + src/bacnet/list_element.c + src/bacnet/list_element.h src/bacnet/lso.c src/bacnet/lso.h src/bacnet/memcopy.c @@ -549,6 +557,9 @@ if(BACNET_STACK_BUILD_APPS) add_executable(ack-alarm apps/ack-alarm/main.c) target_link_libraries(ack-alarm PRIVATE ${PROJECT_NAME}) + add_executable(add-list-element apps/add-list-element/main.c) + target_link_libraries(add-list-element PRIVATE ${PROJECT_NAME}) + add_executable(dcc apps/dcc/main.c) target_link_libraries(dcc PRIVATE ${PROJECT_NAME}) @@ -615,6 +626,9 @@ if(BACNET_STACK_BUILD_APPS) add_executable(readrange apps/readrange/main.c) target_link_libraries(readrange PRIVATE ${PROJECT_NAME}) + add_executable(remove-list-element apps/remove-list-element/main.c) + target_link_libraries(remove-list-element PRIVATE ${PROJECT_NAME}) + add_executable(reinit apps/reinit/main.c) target_link_libraries(reinit PRIVATE ${PROJECT_NAME}) diff --git a/Makefile b/Makefile index 87ab03c7..6b692cdb 100644 --- a/Makefile +++ b/Makefile @@ -65,6 +65,10 @@ abort: ack-alarm: $(MAKE) -s -C apps $@ +.PHONY: add-list-element +add-list-element: + $(MAKE) -s -C apps $@ + .PHONY: dcc dcc: $(MAKE) -s -C apps $@ @@ -105,6 +109,10 @@ readbdt: readfdt: $(MAKE) -s -C apps $@ +.PHONY: remove-list-element +remove-list-element: + $(MAKE) -s -C apps $@ + .PHONY: writebdt writebdt: $(MAKE) -s -C apps $@ diff --git a/apps/Makefile b/apps/Makefile index d2672599..607f63cb 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -173,7 +173,7 @@ endif SUBDIRS = lib readprop writeprop readfile writefile reinit server dcc \ whohas whois iam ucov scov timesync epics readpropm readrange \ writepropm uptransfer getevent uevent abort error event ack-alarm \ - server-client + server-client add-list-element remove-list-element ifeq (${BACDL_DEFINE},-DBACDL_BIP=1) SUBDIRS += whoisrouter iamrouter initrouter whatisnetnum netnumis @@ -218,6 +218,10 @@ abort: $(BACNET_LIB_TARGET) ack-alarm: $(BACNET_LIB_TARGET) $(MAKE) -B -C $@ +.PHONY: add-list-element +add-list-element: $(BACNET_LIB_TARGET) + $(MAKE) -B -C $@ clean all + .PHONY: dcc dcc: $(BACNET_LIB_TARGET) $(MAKE) -B -C $@ @@ -298,6 +302,10 @@ readrange: $(BACNET_LIB_TARGET) reinit: $(BACNET_LIB_TARGET) $(MAKE) -B -C $@ +.PHONY: remove-list-element +remove-list-element: $(BACNET_LIB_TARGET) + $(MAKE) -B -C $@ clean all + .PHONY: scov scov: $(BACNET_LIB_TARGET) $(MAKE) -B -C $@ diff --git a/apps/add-list-element/Makefile b/apps/add-list-element/Makefile new file mode 100644 index 00000000..ff1318a0 --- /dev/null +++ b/apps/add-list-element/Makefile @@ -0,0 +1,39 @@ +#Makefile to build BACnet Application using GCC compiler + +# Executable file name +TARGET = bacale +# BACnet objects that are used with this app +BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object +SRC = main.c \ + $(BACNET_OBJECT_DIR)/client/device-client.c \ + $(BACNET_OBJECT_DIR)/netport.c + +# TARGET_EXT is defined in apps/Makefile as .exe or nothing +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +OBJS += ${SRC:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) -s ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +.PHONY: depend +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +.PHONY: clean +clean: + rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map ${BACNET_LIB_TARGET} + +.PHONY: include +include: .depend diff --git a/apps/add-list-element/main.c b/apps/add-list-element/main.c new file mode 100644 index 00000000..f34e14c7 --- /dev/null +++ b/apps/add-list-element/main.c @@ -0,0 +1,480 @@ +/** + * @file + * @brief Application to send a BACnet AddListElement service request + * @author Steve Karg + * @date January 2023 + * + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include + +#define PRINT_ENABLED 1 + +#include "bacnet/bacdef.h" +#include "bacnet/config.h" +#include "bacnet/bactext.h" +#include "bacnet/bacdest.h" +#include "bacnet/bacerror.h" +#include "bacnet/iam.h" +#include "bacnet/arf.h" +#include "bacnet/basic/tsm/tsm.h" +#include "bacnet/basic/binding/address.h" +#include "bacnet/npdu.h" +#include "bacnet/apdu.h" +#include "bacnet/basic/object/device.h" +#include "bacnet/datalink/datalink.h" +#include "bacnet/list_element.h" +#include "bacnet/whois.h" +#include "bacnet/version.h" +/* some demo stuff needed */ +#include "bacnet/basic/sys/filename.h" +#include "bacnet/basic/sys/mstimer.h" +#include "bacnet/basic/services.h" +#include "bacnet/basic/services.h" +#include "bacnet/basic/tsm/tsm.h" +#include "bacnet/datalink/dlenv.h" + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; +/* target device data for the request */ +static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; +static BACNET_OBJECT_TYPE Target_Object_Type; +static uint32_t Target_Object_Instance; +static BACNET_PROPERTY_ID Target_Object_Property; +static BACNET_ARRAY_INDEX Target_Object_Array_Index; +static BACNET_APPLICATION_DATA_VALUE Target_Object_Value; +/* needed to filter incoming messages */ +static uint8_t Request_Invoke_ID = 0; +static BACNET_ADDRESS Target_Address; +/* needed for return value of main application */ +static bool Error_Detected = false; +/* Used for verbose */ +static bool Verbose = false; + +static void MyAddListElementErrorHandler(BACNET_ADDRESS *src, + uint8_t invoke_id, + uint8_t service_choice, + uint8_t *service_request, + uint16_t service_len) +{ + int len = 0; + BACNET_LIST_ELEMENT_DATA list_element = { 0 }; + + (void)service_choice; + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + len = list_element_error_ack_decode( + service_request, service_len, &list_element); + if (len > 0) { + printf("BACnet Error: %s: %s [first-failed=%u]\n", + bactext_error_class_name((int)list_element.error_class), + bactext_error_code_name((int)list_element.error_code), + (unsigned)list_element.first_failed_element_number); + } + Error_Detected = true; + } +} + +static void MyAddListElementSimpleAckHandler( + BACNET_ADDRESS *src, uint8_t invoke_id) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("AddListElement Acknowledged!\n"); + } +} + +static void MyAbortHandler( + BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t abort_reason, bool server) +{ + (void)server; + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf( + "BACnet Abort: %s\n", bactext_abort_reason_name((int)abort_reason)); + Error_Detected = true; + } +} + +static void MyRejectHandler( + BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t reject_reason) +{ + /* FIXME: verify src and invoke id */ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Reject: %s\n", + bactext_reject_reason_name((int)reject_reason)); + Error_Detected = true; + } +} + +static void Init_Service_Handlers(void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* 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 or error coming back from confirmed request */ + apdu_set_confirmed_simple_ack_handler( + SERVICE_CONFIRMED_ADD_LIST_ELEMENT, MyAddListElementSimpleAckHandler); + apdu_set_complex_error_handler( + SERVICE_CONFIRMED_ADD_LIST_ELEMENT, MyAddListElementErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +static void print_usage(char *filename) +{ + printf("Usage: %s device-instance object-type object-instance " + "property array-index tag value\n", + filename); + printf(" [--dnet][--dadr][--mac]\n"); + printf(" [--version][--help][--verbose]\n"); +} + +static void print_help(char *filename) +{ + printf("Add a BACnetLIST element to a property of an object\n" + "in a BACnet device.\n"); + printf("\n"); + printf("device-instance:\n" + "BACnet Device Object Instance number that you are\n" + "trying to communicate to. This number will be used\n" + "to try and bind with the device using Who-Is and\n" + "I-Am services. For example, if you were writing\n" + "Device Object 123, the device-instance would be 123.\n"); + printf("\n"); + printf("object-type:\n" + "The object type is object that you are reading. It\n" + "can be defined either as the object-type name string\n" + "as defined in the BACnet specification, or as the\n" + "integer value of the enumeration BACNET_OBJECT_TYPE\n" + "in bacenum.h. For example if you were reading Analog\n" + "Output 2, the object-type would be analog-output or 1.\n"); + printf("\n"); + printf("object-instance:\n" + "This is the object instance number of the object that\n" + "you are writing. For example, if you were writing\n" + "Analog Output 2, the object-instance would be 2.\n"); + printf("\n"); + printf("property:\n" + "The property is an integer value of the enumeration\n" + "BACNET_PROPERTY_ID in bacenum.h. It is the property\n" + "you are writing. For example, if you were writing the\n" + "Present Value property, use 85 as the property.\n"); + printf("\n"); + printf( + "array-index:\n" + "This integer parameter is the index number of an array.\n" + "If the property is an array, individual elements can be written\n" + "to if supported. If this parameter is -1, the index is ignored.\n"); + printf("\n"); + printf( + "tag:\n" + "Tag is the integer value of the enumeration BACNET_APPLICATION_TAG \n" + "in bacenum.h. It is the data type of the value that you are\n" + "writing. For example, if you were writing a REAL value, you would \n" + "use a tag of 4.\n" + "Context tags are created using two tags in a row. The context tag\n" + "is preceded by a C. Ctag tag. C2 4 creates a context 2 tagged REAL.\n" + "Complex data uses a tag of -1, and relies on the property\n" + "to choose the appropriate parser for the value(s).\n"); + printf("\n"); + printf( + "value:\n" + "The value is an ASCII representation of some type of data that you\n" + "are writing. It is encoded using the tag information provided. For\n" + "example, if you were writing a REAL value of 100.0, you would use \n" + "100.0 as the value.\n"); + printf("\n"); + printf( + "Here is a brief overview of BACnet property and tags:\n" + "Certain properties are expected to be written with certain \n" + "application tags, so you probably need to know which ones to use\n" + "with each property of each object. It is almost safe to say that\n" + "given a property and an object and a table, the tag could be looked\n" + "up automatically. There may be a few exceptions to this, such as\n" + "the Any property type in the schedule object and the Present Value\n" + "accepting REAL, BOOLEAN, NULL, etc.\n"); + printf("\n"); + printf("Example:\n" + "If you want to AddListElement to the Recipient-List property in\n" + "Notification Class 1 of Device 123, send the following command:\n" + "%s 123 15 1 102 -1 4 100\n", + filename); +} + +int main(int argc, char *argv[]) +{ + BACNET_ADDRESS src = { 0 }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + unsigned max_apdu = 0; + int tag_value_arg = 0; + struct mstimer apdu_timer; + struct mstimer maintenance_timer; + bool found = false; + char *value_string = NULL; + bool status = false; + unsigned context_tag = 0; + BACNET_APPLICATION_DATA_VALUE *application_value = NULL; + unsigned object_type = 0; + unsigned object_instance = 0; + unsigned property_id = 0; + unsigned property_array_index = 0; + long property_tag = 0; + long dnet = -1; + BACNET_MAC_ADDRESS mac = { 0 }; + BACNET_MAC_ADDRESS adr = { 0 }; + BACNET_ADDRESS dest = { 0 }; + bool specific_address = false; + unsigned int target_args = 0; + int argi = 0; + char *filename = NULL; + + filename = filename_remove_path(argv[0]); + for (argi = 1; argi < argc; argi++) { + if (strcmp(argv[argi], "--help") == 0) { + print_usage(filename); + print_help(filename); + return 0; + } + if (strcmp(argv[argi], "--version") == 0) { + printf("%s %s\n", filename, BACNET_VERSION_TEXT); + printf("Copyright (C) 2023 by Steve Karg and others.\n" + "This is free software; see the source for copying " + "conditions.\n" + "There is NO warranty; not even for MERCHANTABILITY or\n" + "FITNESS FOR A PARTICULAR PURPOSE.\n"); + return 0; + } + if (strcmp(argv[argi], "--mac") == 0) { + if (++argi < argc) { + if (bacnet_address_mac_from_ascii(&mac, argv[argi])) { + specific_address = true; + } + } + } else if (strcmp(argv[argi], "--dnet") == 0) { + if (++argi < argc) { + dnet = strtol(argv[argi], NULL, 0); + if ((dnet >= 0) && (dnet <= UINT16_MAX)) { + specific_address = true; + } + } + } else if (strcmp(argv[argi], "--dadr") == 0) { + if (++argi < argc) { + if (bacnet_address_mac_from_ascii(&adr, argv[argi])) { + specific_address = true; + } + } + } else if (strcmp(argv[argi], "--verbose") == 0) { + Verbose = true; + } else { + if (target_args == 0) { + object_instance = strtoul(argv[argi], NULL, 0); + if (object_instance >= BACNET_MAX_INSTANCE) { + fprintf(stderr, + "device-instance=%u - it must be less than %u\n", + object_instance, BACNET_MAX_INSTANCE); + return 1; + } + Target_Device_Object_Instance = object_instance; + target_args++; + } else if (target_args == 1) { + if (bactext_object_type_strtol(argv[argi], &object_type) == + false) { + fprintf(stderr, "object-type=%s invalid\n", argv[argi]); + return 1; + } + Target_Object_Type = object_type; + target_args++; + } else if (target_args == 2) { + object_instance = strtoul(argv[argi], NULL, 0); + if (object_instance >= BACNET_MAX_INSTANCE) { + fprintf(stderr, + "device-instance=%u - it must be less than %u\n", + Target_Device_Object_Instance, BACNET_MAX_INSTANCE); + return 1; + } + Target_Object_Instance = object_instance; + if (Verbose) { + printf("Instance=%u=%s\n", object_instance, argv[argi]); + } + target_args++; + } else if (target_args == 3) { + if (bactext_property_strtol(argv[argi], &property_id) == + false) { + fprintf(stderr, "property=%s invalid\n", argv[argi]); + return 1; + } + Target_Object_Property = property_id; + if (Verbose) { + printf("Property=%u=%s\n", property_id, argv[argi]); + } + target_args++; + } else if (target_args == 4) { + property_array_index = strtol(argv[argi], NULL, 0); + Target_Object_Array_Index = property_array_index; + if (Verbose) { + printf("Array_Index=%i=%s\n", property_array_index, + argv[argi]); + } + target_args++; + tag_value_arg = 0; + application_value = &Target_Object_Value; + } else if (target_args == 5) { + /* Tag + Value or Cn + Tag + Value */ + if (tag_value_arg == 0) { + /* special case for context tagged values */ + if (toupper(argv[argi][0]) == 'C') { + context_tag = strtoul(&argv[target_args][1], NULL, 0); + application_value->context_tag = context_tag; + application_value->context_specific = true; + argi++; + } else { + application_value->context_specific = false; + } + /* application tag */ + property_tag = strtol(argv[argi], NULL, 0); + if (Verbose) { + printf("tag=%ld\n", property_tag); + } + tag_value_arg++; + } else if (tag_value_arg == 1) { + value_string = argv[argi]; + if (Verbose) { + printf("tag=%ld value=%s\n", property_tag, value_string); + } + if (property_tag < 0) { + /* find the application tag for internal properties */ + property_tag = bacapp_known_property_tag( + Target_Object_Type, Target_Object_Property); + } else if (property_tag >= MAX_BACNET_APPLICATION_TAG) { + fprintf(stderr, + "Error: tag=%ld - it must be less than %u\n", + property_tag, MAX_BACNET_APPLICATION_TAG); + return 1; + } + if (property_tag >= 0) { + /* note: this will also set the tag to property_tag */ + status = bacapp_parse_application_data( + property_tag, value_string, application_value); + if (!status) { + fprintf(stderr, + "Error: unable to parse the tag value\n"); + return 1; + } + } else { + /* FIXME: show the expected entry format for the tag */ + fprintf(stderr, + "Error: unable to parse the known property" + " \"%s\"\r\n", + value_string); + return 1; + } + /* do the next tag+value */ + tag_value_arg = 0; + /* we only support a single property value */ + application_value->next = NULL; + break; + } + } + } + } + if (target_args != 5) { + print_usage(filename); + return 0; + } + if (tag_value_arg != 0) { + fprintf( + stderr, "Error: invalid tag+value pairs (%i).\n", tag_value_arg); + return 1; + } + /* setup my info */ + address_init(); + if (specific_address) { + bacnet_address_init(&dest, &mac, dnet, &adr); + address_add(Target_Device_Object_Instance, MAX_APDU, &dest); + } + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + mstimer_init(); + mstimer_set(&apdu_timer, apdu_timeout()); + mstimer_set(&maintenance_timer, 1000); + /* try to bind with the device */ + found = address_bind_request( + Target_Device_Object_Instance, &max_apdu, &Target_Address); + if (found) { + if (Verbose) { + printf("Found Device %u in address_cache.\n", + Target_Device_Object_Instance); + } + } else { + Send_WhoIs( + Target_Device_Object_Instance, Target_Device_Object_Instance); + } + /* loop forever */ + for (;;) { + if (found) { + /* device is bound! */ + if (Request_Invoke_ID == 0) { + if (Verbose) { + printf("Sending AddListElement to Device %u.\n", + Target_Device_Object_Instance); + } + Request_Invoke_ID = Send_Add_List_Element_Request( + Target_Device_Object_Instance, Target_Object_Type, + Target_Object_Instance, Target_Object_Property, + &Target_Object_Value, Target_Object_Array_Index); + } else if (tsm_invoke_id_free(Request_Invoke_ID)) { + break; + } else if (tsm_invoke_id_failed(Request_Invoke_ID)) { + fprintf(stderr, "\rError: TSM Timeout!\n"); + tsm_free_invoke_id(Request_Invoke_ID); + Error_Detected = true; + /* abort */ + break; + } + } else { + found = address_bind_request( + Target_Device_Object_Instance, &max_apdu, &Target_Address); + } + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + if (mstimer_expired(&maintenance_timer)) { + mstimer_reset(&maintenance_timer); + tsm_timer_milliseconds(mstimer_interval(&maintenance_timer)); + } + if (mstimer_expired(&apdu_timer)) { + printf("\rError: APDU Timeout!\n"); + Error_Detected = true; + } + if (Error_Detected) { + break; + } + } + if (Error_Detected) { + return 1; + } + + return 0; +} diff --git a/apps/remove-list-element/Makefile b/apps/remove-list-element/Makefile new file mode 100644 index 00000000..b25ec37a --- /dev/null +++ b/apps/remove-list-element/Makefile @@ -0,0 +1,39 @@ +#Makefile to build BACnet Application using GCC compiler + +# Executable file name +TARGET = bacrle +# BACnet objects that are used with this app +BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object +SRC = main.c \ + $(BACNET_OBJECT_DIR)/client/device-client.c \ + $(BACNET_OBJECT_DIR)/netport.c + +# TARGET_EXT is defined in apps/Makefile as .exe or nothing +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +OBJS += ${SRC:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) -s ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +.PHONY: depend +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +.PHONY: clean +clean: + rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map ${BACNET_LIB_TARGET} + +.PHONY: include +include: .depend diff --git a/apps/remove-list-element/main.c b/apps/remove-list-element/main.c new file mode 100644 index 00000000..3adcd2af --- /dev/null +++ b/apps/remove-list-element/main.c @@ -0,0 +1,480 @@ +/** + * @file + * @brief Application to send a BACnet RemoveListElement service request + * @author Steve Karg + * @date January 2023 + * + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include + +#define PRINT_ENABLED 1 + +#include "bacnet/bacdef.h" +#include "bacnet/config.h" +#include "bacnet/bactext.h" +#include "bacnet/bacdest.h" +#include "bacnet/bacerror.h" +#include "bacnet/iam.h" +#include "bacnet/arf.h" +#include "bacnet/basic/tsm/tsm.h" +#include "bacnet/basic/binding/address.h" +#include "bacnet/npdu.h" +#include "bacnet/apdu.h" +#include "bacnet/basic/object/device.h" +#include "bacnet/datalink/datalink.h" +#include "bacnet/list_element.h" +#include "bacnet/whois.h" +#include "bacnet/version.h" +/* some demo stuff needed */ +#include "bacnet/basic/sys/filename.h" +#include "bacnet/basic/sys/mstimer.h" +#include "bacnet/basic/services.h" +#include "bacnet/basic/services.h" +#include "bacnet/basic/tsm/tsm.h" +#include "bacnet/datalink/dlenv.h" + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; +/* target device data for the request */ +static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; +static BACNET_OBJECT_TYPE Target_Object_Type; +static uint32_t Target_Object_Instance; +static BACNET_PROPERTY_ID Target_Object_Property; +static BACNET_ARRAY_INDEX Target_Object_Array_Index; +static BACNET_APPLICATION_DATA_VALUE Target_Object_Value; +/* needed to filter incoming messages */ +static uint8_t Request_Invoke_ID = 0; +static BACNET_ADDRESS Target_Address; +/* needed for return value of main application */ +static bool Error_Detected = false; +/* Used for verbose */ +static bool Verbose = false; + +static void MyRemoveListElementErrorHandler(BACNET_ADDRESS *src, + uint8_t invoke_id, + uint8_t service_choice, + uint8_t *service_request, + uint16_t service_len) +{ + int len = 0; + BACNET_LIST_ELEMENT_DATA list_element = { 0 }; + + (void)service_choice; + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + len = list_element_error_ack_decode( + service_request, service_len, &list_element); + if (len > 0) { + printf("BACnet Error: %s: %s [first-failed=%u]\n", + bactext_error_class_name((int)list_element.error_class), + bactext_error_code_name((int)list_element.error_code), + (unsigned)list_element.first_failed_element_number); + } + Error_Detected = true; + } +} + +static void MyRemoveListElementSimpleAckHandler( + BACNET_ADDRESS *src, uint8_t invoke_id) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("RemoveListElement Acknowledged!\n"); + } +} + +static void MyAbortHandler( + BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t abort_reason, bool server) +{ + (void)server; + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf( + "BACnet Abort: %s\n", bactext_abort_reason_name((int)abort_reason)); + Error_Detected = true; + } +} + +static void MyRejectHandler( + BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t reject_reason) +{ + /* FIXME: verify src and invoke id */ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Reject: %s\n", + bactext_reject_reason_name((int)reject_reason)); + Error_Detected = true; + } +} + +static void Init_Service_Handlers(void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* 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 or error coming back from confirmed request */ + apdu_set_confirmed_simple_ack_handler( + SERVICE_CONFIRMED_ADD_LIST_ELEMENT, MyRemoveListElementSimpleAckHandler); + apdu_set_complex_error_handler( + SERVICE_CONFIRMED_ADD_LIST_ELEMENT, MyRemoveListElementErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +static void print_usage(char *filename) +{ + printf("Usage: %s device-instance object-type object-instance " + "property array-index tag value\n", + filename); + printf(" [--dnet][--dadr][--mac]\n"); + printf(" [--version][--help][--verbose]\n"); +} + +static void print_help(char *filename) +{ + printf("Remove an element from a BACnetLIST property of an object\n" + "in a BACnet device.\n"); + printf("\n"); + printf("device-instance:\n" + "BACnet Device Object Instance number that you are\n" + "trying to communicate to. This number will be used\n" + "to try and bind with the device using Who-Is and\n" + "I-Am services. For example, if you were writing\n" + "Device Object 123, the device-instance would be 123.\n"); + printf("\n"); + printf("object-type:\n" + "The object type is object that you are reading. It\n" + "can be defined either as the object-type name string\n" + "as defined in the BACnet specification, or as the\n" + "integer value of the enumeration BACNET_OBJECT_TYPE\n" + "in bacenum.h. For example if you were reading Analog\n" + "Output 2, the object-type would be analog-output or 1.\n"); + printf("\n"); + printf("object-instance:\n" + "This is the object instance number of the object that\n" + "you are writing. For example, if you were writing\n" + "Analog Output 2, the object-instance would be 2.\n"); + printf("\n"); + printf("property:\n" + "The property is an integer value of the enumeration\n" + "BACNET_PROPERTY_ID in bacenum.h. It is the property\n" + "you are writing. For example, if you were writing the\n" + "Present Value property, use 85 as the property.\n"); + printf("\n"); + printf( + "array-index:\n" + "This integer parameter is the index number of an array.\n" + "If the property is an array, individual elements can be written\n" + "to if supported. If this parameter is -1, the index is ignored.\n"); + printf("\n"); + printf( + "tag:\n" + "Tag is the integer value of the enumeration BACNET_APPLICATION_TAG \n" + "in bacenum.h. It is the data type of the value that you are\n" + "writing. For example, if you were writing a REAL value, you would \n" + "use a tag of 4.\n" + "Context tags are created using two tags in a row. The context tag\n" + "is preceded by a C. Ctag tag. C2 4 creates a context 2 tagged REAL.\n" + "Complex data uses a tag of -1, and relies on the property\n" + "to choose the appropriate parser for the value(s).\n"); + printf("\n"); + printf( + "value:\n" + "The value is an ASCII representation of some type of data that you\n" + "are writing. It is encoded using the tag information provided. For\n" + "example, if you were writing a REAL value of 100.0, you would use \n" + "100.0 as the value.\n"); + printf("\n"); + printf( + "Here is a brief overview of BACnet property and tags:\n" + "Certain properties are expected to be written with certain \n" + "application tags, so you probably need to know which ones to use\n" + "with each property of each object. It is almost safe to say that\n" + "given a property and an object and a table, the tag could be looked\n" + "up automatically. There may be a few exceptions to this, such as\n" + "the Any property type in the schedule object and the Present Value\n" + "accepting REAL, BOOLEAN, NULL, etc.\n"); + printf("\n"); + printf("Example:\n" + "If you want to RemoveListElement to the Recipient-List property in\n" + "Notification Class 1 of Device 123, send the following command:\n" + "%s 123 15 1 102 -1 4 100\n", + filename); +} + +int main(int argc, char *argv[]) +{ + BACNET_ADDRESS src = { 0 }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + unsigned max_apdu = 0; + int tag_value_arg = 0; + struct mstimer apdu_timer; + struct mstimer maintenance_timer; + bool found = false; + char *value_string = NULL; + bool status = false; + unsigned context_tag = 0; + BACNET_APPLICATION_DATA_VALUE *application_value = NULL; + unsigned object_type = 0; + unsigned object_instance = 0; + unsigned property_id = 0; + unsigned property_array_index = 0; + long property_tag = 0; + long dnet = -1; + BACNET_MAC_ADDRESS mac = { 0 }; + BACNET_MAC_ADDRESS adr = { 0 }; + BACNET_ADDRESS dest = { 0 }; + bool specific_address = false; + unsigned int target_args = 0; + int argi = 0; + char *filename = NULL; + + filename = filename_remove_path(argv[0]); + for (argi = 1; argi < argc; argi++) { + if (strcmp(argv[argi], "--help") == 0) { + print_usage(filename); + print_help(filename); + return 0; + } + if (strcmp(argv[argi], "--version") == 0) { + printf("%s %s\n", filename, BACNET_VERSION_TEXT); + printf("Copyright (C) 2023 by Steve Karg and others.\n" + "This is free software; see the source for copying " + "conditions.\n" + "There is NO warranty; not even for MERCHANTABILITY or\n" + "FITNESS FOR A PARTICULAR PURPOSE.\n"); + return 0; + } + if (strcmp(argv[argi], "--mac") == 0) { + if (++argi < argc) { + if (bacnet_address_mac_from_ascii(&mac, argv[argi])) { + specific_address = true; + } + } + } else if (strcmp(argv[argi], "--dnet") == 0) { + if (++argi < argc) { + dnet = strtol(argv[argi], NULL, 0); + if ((dnet >= 0) && (dnet <= UINT16_MAX)) { + specific_address = true; + } + } + } else if (strcmp(argv[argi], "--dadr") == 0) { + if (++argi < argc) { + if (bacnet_address_mac_from_ascii(&adr, argv[argi])) { + specific_address = true; + } + } + } else if (strcmp(argv[argi], "--verbose") == 0) { + Verbose = true; + } else { + if (target_args == 0) { + object_instance = strtoul(argv[argi], NULL, 0); + if (object_instance >= BACNET_MAX_INSTANCE) { + fprintf(stderr, + "device-instance=%u - it must be less than %u\n", + object_instance, BACNET_MAX_INSTANCE); + return 1; + } + Target_Device_Object_Instance = object_instance; + target_args++; + } else if (target_args == 1) { + if (bactext_object_type_strtol(argv[argi], &object_type) == + false) { + fprintf(stderr, "object-type=%s invalid\n", argv[argi]); + return 1; + } + Target_Object_Type = object_type; + target_args++; + } else if (target_args == 2) { + object_instance = strtoul(argv[argi], NULL, 0); + if (object_instance >= BACNET_MAX_INSTANCE) { + fprintf(stderr, + "device-instance=%u - it must be less than %u\n", + Target_Device_Object_Instance, BACNET_MAX_INSTANCE); + return 1; + } + Target_Object_Instance = object_instance; + if (Verbose) { + printf("Instance=%u=%s\n", object_instance, argv[argi]); + } + target_args++; + } else if (target_args == 3) { + if (bactext_property_strtol(argv[argi], &property_id) == + false) { + fprintf(stderr, "property=%s invalid\n", argv[argi]); + return 1; + } + Target_Object_Property = property_id; + if (Verbose) { + printf("Property=%u=%s\n", property_id, argv[argi]); + } + target_args++; + } else if (target_args == 4) { + property_array_index = strtol(argv[argi], NULL, 0); + Target_Object_Array_Index = property_array_index; + if (Verbose) { + printf("Array_Index=%i=%s\n", property_array_index, + argv[argi]); + } + target_args++; + tag_value_arg = 0; + application_value = &Target_Object_Value; + } else if (target_args == 5) { + /* Tag + Value or Cn + Tag + Value */ + if (tag_value_arg == 0) { + /* special case for context tagged values */ + if (toupper(argv[argi][0]) == 'C') { + context_tag = strtoul(&argv[target_args][1], NULL, 0); + application_value->context_tag = context_tag; + application_value->context_specific = true; + argi++; + } else { + application_value->context_specific = false; + } + /* application tag */ + property_tag = strtol(argv[argi], NULL, 0); + if (Verbose) { + printf("tag=%ld\n", property_tag); + } + tag_value_arg++; + } else if (tag_value_arg == 1) { + value_string = argv[argi]; + if (Verbose) { + printf("tag=%ld value=%s\n", property_tag, value_string); + } + if (property_tag < 0) { + /* find the application tag for internal properties */ + property_tag = bacapp_known_property_tag( + Target_Object_Type, Target_Object_Property); + } else if (property_tag >= MAX_BACNET_APPLICATION_TAG) { + fprintf(stderr, + "Error: tag=%ld - it must be less than %u\n", + property_tag, MAX_BACNET_APPLICATION_TAG); + return 1; + } + if (property_tag >= 0) { + /* note: this will also set the tag to property_tag */ + status = bacapp_parse_application_data( + property_tag, value_string, application_value); + if (!status) { + fprintf(stderr, + "Error: unable to parse the tag value\n"); + return 1; + } + } else { + /* FIXME: show the expected entry format for the tag */ + fprintf(stderr, + "Error: unable to parse the known property" + " \"%s\"\r\n", + value_string); + return 1; + } + /* do the next tag+value */ + tag_value_arg = 0; + /* we only support a single property value */ + application_value->next = NULL; + break; + } + } + } + } + if (target_args != 5) { + print_usage(filename); + return 0; + } + if (tag_value_arg != 0) { + fprintf( + stderr, "Error: invalid tag+value pair (%i).\n", tag_value_arg); + return 1; + } + /* setup my info */ + address_init(); + if (specific_address) { + bacnet_address_init(&dest, &mac, dnet, &adr); + address_add(Target_Device_Object_Instance, MAX_APDU, &dest); + } + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + mstimer_init(); + mstimer_set(&apdu_timer, apdu_timeout()); + mstimer_set(&maintenance_timer, 1000); + /* try to bind with the device */ + found = address_bind_request( + Target_Device_Object_Instance, &max_apdu, &Target_Address); + if (found) { + if (Verbose) { + printf("Found Device %u in address_cache.\n", + Target_Device_Object_Instance); + } + } else { + Send_WhoIs( + Target_Device_Object_Instance, Target_Device_Object_Instance); + } + /* loop forever */ + for (;;) { + if (found) { + /* device is bound! */ + if (Request_Invoke_ID == 0) { + if (Verbose) { + printf("Sending RemoveListElement to Device %u.\n", + Target_Device_Object_Instance); + } + Request_Invoke_ID = Send_Remove_List_Element_Request( + Target_Device_Object_Instance, Target_Object_Type, + Target_Object_Instance, Target_Object_Property, + &Target_Object_Value, Target_Object_Array_Index); + } else if (tsm_invoke_id_free(Request_Invoke_ID)) { + break; + } else if (tsm_invoke_id_failed(Request_Invoke_ID)) { + fprintf(stderr, "\rError: TSM Timeout!\n"); + tsm_free_invoke_id(Request_Invoke_ID); + Error_Detected = true; + /* abort */ + break; + } + } else { + found = address_bind_request( + Target_Device_Object_Instance, &max_apdu, &Target_Address); + } + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + if (mstimer_expired(&maintenance_timer)) { + mstimer_reset(&maintenance_timer); + tsm_timer_milliseconds(mstimer_interval(&maintenance_timer)); + } + if (mstimer_expired(&apdu_timer)) { + printf("\rError: APDU Timeout!\n"); + Error_Detected = true; + } + if (Error_Detected) { + break; + } + } + if (Error_Detected) { + return 1; + } + + return 0; +} diff --git a/ports/at91sam7s/Makefile b/ports/at91sam7s/Makefile index 5a0166e6..c5966062 100644 --- a/ports/at91sam7s/Makefile +++ b/ports/at91sam7s/Makefile @@ -87,6 +87,7 @@ CORESRC = $(BACNET_CORE)/abort.c \ $(BACNET_CORE)/bacaddr.c \ $(BACNET_CORE)/bacapp.c \ $(BACNET_CORE)/bacdcode.c \ + $(BACNET_CORE)/bacdest.c \ $(BACNET_CORE)/bacdevobjpropref.c \ $(BACNET_CORE)/bacerror.c \ $(BACNET_CORE)/bacint.c \ diff --git a/ports/at91sam7s/bacnet.ewp b/ports/at91sam7s/bacnet.ewp index cd5ac3ae..9efa1d4a 100644 --- a/ports/at91sam7s/bacnet.ewp +++ b/ports/at91sam7s/bacnet.ewp @@ -1127,6 +1127,9 @@ $PROJ_DIR$\..\..\src\bacnet\bacdcode.c + + $PROJ_DIR$\..\..\src\bacnet\bacdest.c + $PROJ_DIR$\..\..\src\bacnet\bacdevobjpropref.c diff --git a/ports/stm32f10x/Makefile b/ports/stm32f10x/Makefile index e0739582..4e28d81a 100644 --- a/ports/stm32f10x/Makefile +++ b/ports/stm32f10x/Makefile @@ -56,6 +56,7 @@ BACNET_SRC = \ $(BACNET_CORE)/bacaddr.c \ $(BACNET_CORE)/bacapp.c \ $(BACNET_CORE)/bacdcode.c \ + $(BACNET_CORE)/bacdest.c \ $(BACNET_CORE)/bacdevobjpropref.c \ $(BACNET_CORE)/bacerror.c \ $(BACNET_CORE)/bacint.c \ diff --git a/ports/stm32f10x/bacnet.ewp b/ports/stm32f10x/bacnet.ewp index 66a40997..8c8f4c50 100644 --- a/ports/stm32f10x/bacnet.ewp +++ b/ports/stm32f10x/bacnet.ewp @@ -1057,6 +1057,9 @@ $PROJ_DIR$\..\..\src\bacnet\bacdcode.c + + $PROJ_DIR$\..\..\src\bacnet\bacdest.c + $PROJ_DIR$\..\..\src\bacnet\bacdevobjpropref.c diff --git a/ports/stm32f4xx/Makefile b/ports/stm32f4xx/Makefile index 4258372f..b3bb7d72 100644 --- a/ports/stm32f4xx/Makefile +++ b/ports/stm32f4xx/Makefile @@ -54,6 +54,7 @@ BACNET_SRC = \ $(BACNET_CORE)/bacaddr.c \ $(BACNET_CORE)/bacapp.c \ $(BACNET_CORE)/bacdcode.c \ + $(BACNET_CORE)/bacdest.c \ $(BACNET_CORE)/bacdevobjpropref.c \ $(BACNET_CORE)/bacerror.c \ $(BACNET_CORE)/bacint.c \ diff --git a/ports/stm32f4xx/bacnet.ewp b/ports/stm32f4xx/bacnet.ewp index ab1cf68b..6c3d169d 100644 --- a/ports/stm32f4xx/bacnet.ewp +++ b/ports/stm32f4xx/bacnet.ewp @@ -1060,6 +1060,9 @@ $PROJ_DIR$\..\..\src\bacnet\bacdcode.c + + $PROJ_DIR$\..\..\src\bacnet\bacdest.c + $PROJ_DIR$\..\..\src\bacnet\bacdevobjpropref.c diff --git a/ports/xplained/Makefile b/ports/xplained/Makefile index a73a3c70..fed36a2b 100644 --- a/ports/xplained/Makefile +++ b/ports/xplained/Makefile @@ -61,6 +61,7 @@ CORESRC = \ $(BACNET_CORE)/bacaddr.c \ $(BACNET_CORE)/bacapp.c \ $(BACNET_CORE)/bacdcode.c \ + $(BACNET_CORE)/bacdest.c \ $(BACNET_CORE)/bacerror.c \ $(BACNET_CORE)/bacint.c \ $(BACNET_CORE)/bacreal.c \ diff --git a/ports/xplained/bacnet.cproj b/ports/xplained/bacnet.cproj index 61f68c02..2c5bb34d 100644 --- a/ports/xplained/bacnet.cproj +++ b/ports/xplained/bacnet.cproj @@ -776,6 +776,10 @@ compile bacnet-stack\bacdcode.c + + compile + bacnet-stack\bacdcode.c + compile bacnet-stack\bacdevobjpropref.c diff --git a/src/bacnet/bacapp.c b/src/bacnet/bacapp.c index 1480ef68..a123541a 100644 --- a/src/bacnet/bacapp.c +++ b/src/bacnet/bacapp.c @@ -196,6 +196,11 @@ int bacapp_encode_application_data( apdu_len = bacapp_encode_obj_property_ref( apdu, &value->type.Object_Property_Reference); break; + case BACNET_APPLICATION_TAG_DESTINATION: + /* BACnetDestination */ + apdu_len = bacnet_destination_encode( + apdu, &value->type.Destination); + break; #endif default: break; @@ -344,6 +349,11 @@ int bacapp_decode_data(uint8_t *apdu, len = bacapp_decode_obj_property_ref(apdu, len_value_type, &value->type.Object_Property_Reference); break; + case BACNET_APPLICATION_TAG_DESTINATION: + /* BACnetDestination */ + len = bacnet_destination_decode(apdu, len_value_type, + &value->type.Destination); + break; #endif default: break; @@ -672,6 +682,11 @@ int bacapp_encode_context_data_value(uint8_t *apdu, apdu_len = bacapp_encode_context_obj_property_ref(apdu, context_tag_number, &value->type.Object_Property_Reference); break; + case BACNET_APPLICATION_TAG_DESTINATION: + /* BACnetDestination */ + apdu_len = bacnet_destination_context_encode(apdu, + context_tag_number, &value->type.Destination); + break; #endif default: break; @@ -1129,8 +1144,8 @@ int bacapp_known_property_tag( return -1; case PROP_RECIPIENT_LIST: - /* FIXME: Properties using BACnetDestination */ - return -1; + /* Properties using BACnetDestination */ + return BACNET_APPLICATION_TAG_DESTINATION; case PROP_TIME_SYNCHRONIZATION_RECIPIENTS: case PROP_RESTART_NOTIFICATION_RECIPIENTS: @@ -1287,6 +1302,11 @@ int bacapp_decode_known_property(uint8_t *apdu, apdu, max_apdu_len, &value->type.Weekly_Schedule); break; + case PROP_RECIPIENT_LIST: + len = bacnet_destination_decode( + apdu, max_apdu_len, &value->type.Destination); + break; + /* properties without a specific decoder - fall through to default */ @@ -1300,8 +1320,6 @@ int bacapp_decode_known_property(uint8_t *apdu, /* FIXME: BACnetCOVSubscription */ case PROP_EFFECTIVE_PERIOD: /* FIXME: Properties using BACnetDateRange (Schedule) */ - case PROP_RECIPIENT_LIST: - /* FIXME: Properties using BACnetDestination */ case PROP_TIME_SYNCHRONIZATION_RECIPIENTS: case PROP_RESTART_NOTIFICATION_RECIPIENTS: case PROP_UTC_TIME_SYNCHRONIZATION_RECIPIENTS: @@ -2242,6 +2260,10 @@ int bacapp_snprintf_value( ret_val = bacapp_snprintf_weeklyschedule(str, str_len, &value->type.Weekly_Schedule, object_value->array_index); break; + case BACNET_APPLICATION_TAG_DESTINATION: + /* BACnetWeeklySchedule */ + ret_val = bacnet_destination_to_ascii(&value->type.Destination, str, str_len); + break; case BACNET_APPLICATION_TAG_HOST_N_PORT: if (value->type.Host_Address.host_ip_address) { octet_str = octetstring_value( @@ -2763,6 +2785,10 @@ bool bacapp_parse_application_data(BACNET_APPLICATION_TAG tag_number, status = false; } break; + case BACNET_APPLICATION_TAG_DESTINATION: + status = bacnet_destination_from_ascii(&value->type.Destination, + argv); + break; #endif default: break; diff --git a/src/bacnet/bacapp.h b/src/bacnet/bacapp.h index 31417aed..9adb5c8f 100644 --- a/src/bacnet/bacapp.h +++ b/src/bacnet/bacapp.h @@ -29,6 +29,7 @@ #include #include "bacnet/bacnet_stack_exports.h" #include "bacnet/bacdef.h" +#include "bacnet/bacdest.h" #include "bacnet/bacint.h" #include "bacnet/bacstr.h" #include "bacnet/datetime.h" @@ -95,6 +96,7 @@ typedef struct BACnet_Application_Data_Value { Device_Object_Reference; BACNET_OBJECT_PROPERTY_REFERENCE Object_Property_Reference; + BACNET_DESTINATION Destination; #endif } type; /* simple linked list if needed */ diff --git a/src/bacnet/bacdest.c b/src/bacnet/bacdest.c new file mode 100644 index 00000000..8b85d53d --- /dev/null +++ b/src/bacnet/bacdest.c @@ -0,0 +1,993 @@ +/** + * @file + * @brief BACnetDestination complex data type encode and decode + * @author Steve Karg + * @date December 2022 + * @section LICENSE + * + * Copyright (C) 2022 Steve Karg + * + * SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#include +#include +#include +#include +#include +#include +#include +#include "bacnet/bacdef.h" +#include "bacnet/bacaddr.h" +#include "bacnet/bacapp.h" +#include "bacnet/bacdcode.h" +#include "bacnet/bacdest.h" +#include "bacnet/basic/binding/address.h" +#include "bacdest.h" + +/** + * @brief Initialize the BACnetDestination data structure with defaults + * + * BACnetDestination ::= SEQUENCE { + * valid-days BACnetDaysOfWeek, + * from-time Time, + * to-time Time, + * recipient BACnetRecipient, + * process-identifier Unsigned32, + * issue-confirmed-notifications BOOLEAN, + * transitions BACnetEventTransitionBits + * } + * + * @param destination BACnetDestination to be initialized + */ +void bacnet_destination_default_init(BACNET_DESTINATION *destination) +{ + unsigned i; + + if (!destination) { + return; + } + /* configure for every day, all day long */ + for (i = 0; i < MAX_BACNET_DAYS_OF_WEEK; i++) { + bitstring_set_bit(&destination->ValidDays, i, true); + } + datetime_set_time(&destination->FromTime, 0, 0, 0, 0); + datetime_set_time(&destination->ToTime, 23, 59, 59, 99); + /* initialize Recipient to *wildcard* device instance - invalid! */ + destination->Recipient.tag = BACNET_RECIPIENT_TAG_DEVICE; + destination->Recipient.type.device.type = OBJECT_DEVICE; + destination->Recipient.type.device.instance = BACNET_MAX_INSTANCE; + destination->ProcessIdentifier = 0; + destination->ConfirmedNotify = false; + bitstring_set_bit( + &destination->Transitions, TRANSITION_TO_OFFNORMAL, false); + bitstring_set_bit(&destination->Transitions, TRANSITION_TO_FAULT, false); + bitstring_set_bit(&destination->Transitions, TRANSITION_TO_NORMAL, false); +} + +/** + * @brief Compare the BACnetRecipient complex data of r1 and r2 + * @param r1 - BACnetRecipient 1 structure + * @param r2 - BACnetRecipient 2 structure + * @return true if r1 and r2 are the same + */ +bool bacnet_recipient_same( + BACNET_RECIPIENT *r1, BACNET_RECIPIENT *r2) +{ + bool status = false; + + if (r1 && r2) { + if (r1->tag == r2->tag) { + status = true; + } + if (status) { + if (r1->tag == BACNET_RECIPIENT_TAG_DEVICE) { + if ((r1->type.device.type == r2->type.device.type) && + (r1->type.device.instance == r2->type.device.instance)) { + status = true; + } + } else if (r1->tag == BACNET_RECIPIENT_TAG_ADDRESS) { + status = + bacnet_address_same(&r1->type.address, &r2->type.address); + } else { + status = false; + } + } + } + + return status; +} + +/** + * @brief Copy the BACnetRecipient complex data from src to dest + * @param src - BACnetRecipient 1 structure + * @param dest - BACnetRecipient 2 structure + */ +void bacnet_recipient_copy( + BACNET_RECIPIENT *dest, BACNET_RECIPIENT *src) +{ + if (dest && src) { + memmove(dest, src, sizeof(BACNET_RECIPIENT)); + } +} + +/** + * @brief Compare the BACnetRecipient data structure device object wildcard + * @param recipient - BACnetRecipient structure + * @return true if BACnetRecipient is equal to the device object wildcard + */ +bool bacnet_recipient_device_wildcard(BACNET_RECIPIENT *recipient) +{ + bool status = false; + + if (recipient) { + if ((recipient->tag == BACNET_RECIPIENT_TAG_DEVICE) && + (recipient->type.device.type == OBJECT_DEVICE) && + (recipient->type.device.instance == BACNET_MAX_INSTANCE)) { + status = true; + } + } + + return status; +} + +/** + * @brief Compare the BACnetRecipient data structure to a valid device + * @param recipient - BACnetRecipient structure + * @return true if BACnetRecipient is a valid device object instance + */ +bool bacnet_recipient_device_valid(BACNET_RECIPIENT *recipient) +{ + bool status = false; + + if (recipient) { + if ((recipient->tag == BACNET_RECIPIENT_TAG_DEVICE) && + (recipient->type.device.type == OBJECT_DEVICE) && + (recipient->type.device.instance < BACNET_MAX_INSTANCE)) { + status = true; + } + } + + return status; +} + +/** + * @brief Compare the BACnetDestination complex data of dest1 and dest2 + * @param d1 - BACnetDestination 1 structure + * @param d2 - BACnetDestination 2 structure + * @return true if dest1 and dest2 are the same + */ +bool bacnet_destination_same(BACNET_DESTINATION *d1, BACNET_DESTINATION *d2) +{ + bool status = false; + + if (d1 && d2) { + status = bitstring_same(&d1->ValidDays, &d2->ValidDays); + if (status) { + status = (datetime_compare_time(&d1->FromTime, &d2->FromTime) == 0); + } + if (status) { + status = (datetime_compare_time(&d1->ToTime, &d2->ToTime) == 0); + } + if (status) { + status = bacnet_recipient_same( + &d1->Recipient, &d2->Recipient); + } + if (status) { + status = (d1->ProcessIdentifier == d2->ProcessIdentifier); + } + if (status) { + status = (d1->ConfirmedNotify == d2->ConfirmedNotify); + } + if (status) { + status = bitstring_same(&d1->Transitions, &d2->Transitions); + } + } + + return status; +} + +/** + * @brief Copy the BACnetDestination complex data from src to dest + * @param dest - BACnetDestination 1 structure + * @param src - BACnetDestination 2 structure + */ +void bacnet_destination_copy(BACNET_DESTINATION *dest, BACNET_DESTINATION *src) +{ + if (dest && src) { + memmove(dest, src, sizeof(BACNET_DESTINATION)); + } +} + +/** + * @brief Compare the BACnetDestination data structure to defaults + * @param d1 - BACnetDestination 1 structure + * @return true if d1 and d2 (defaults) are the same + */ +bool bacnet_destination_default(BACNET_DESTINATION *d1) +{ + BACNET_DESTINATION d2 = { 0 }; + + bacnet_destination_default_init(&d2); + + return bacnet_destination_same(d1, &d2); +} + +/** + * @brief Encode the BACnetDestination complex data + * + * BACnetDestination ::= SEQUENCE { + * valid-days BACnetDaysOfWeek, + * from-time Time, + * to-time Time, + * recipient BACnetRecipient, + * process-identifier Unsigned32, + * issue-confirmed-notifications BOOLEAN, + * transitions BACnetEventTransitionBits + * } + * + * @param apdu Pointer to the buffer for encoding. + * @param destination Pointer to the property data to be encoded. + * + * @return bytes encoded or zero on error. + */ +int bacnet_destination_encode(uint8_t *apdu, BACNET_DESTINATION *destination) +{ + int apdu_len = 0, len = 0; + + if (destination->Recipient.tag < BACNET_RECIPIENT_TAG_MAX) { + len = encode_application_bitstring(apdu, &destination->ValidDays); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_application_time(apdu, &destination->FromTime); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_application_time(apdu, &destination->ToTime); + apdu_len += len; + if (apdu) { + apdu += len; + } + if (destination->Recipient.tag == BACNET_RECIPIENT_TAG_DEVICE) { + len = encode_context_object_id(apdu, 0, OBJECT_DEVICE, + destination->Recipient.type.device.instance); + apdu_len += len; + if (apdu) { + apdu += len; + } + } else if (destination->Recipient.tag == BACNET_RECIPIENT_TAG_ADDRESS) { + /* opening tag 1 */ + len = encode_opening_tag(apdu, 1); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_bacnet_address( + apdu, &destination->Recipient.type.address); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* closing tag 1 */ + len = encode_closing_tag(apdu, 1); + apdu_len += len; + if (apdu) { + apdu += len; + } + } + /* Process Identifier - Unsigned32 */ + len = encode_application_unsigned(apdu, destination->ProcessIdentifier); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* Issue Confirmed Notifications - boolean */ + len = encode_application_boolean(apdu, destination->ConfirmedNotify); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* Transitions - BACnet Event Transition Bits [bitstring] */ + len = encode_application_bitstring(apdu, &destination->Transitions); + apdu_len += len; + } + + return apdu_len; +} + +/** + * @brief Encode a BACnetDestination complex data type + * @param apdu - the APDU buffer + * @param tag_number - context tag number + * @param destination Pointer to the property data to be encoded. + * @return length of the APDU buffer, or 0 if not able to encode + */ +int bacnet_destination_context_encode( + uint8_t *apdu, uint8_t tag_number, BACNET_DESTINATION *destination) +{ + int len = 0; + int apdu_len = 0; + + if (destination) { + len = encode_opening_tag(apdu, tag_number); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = bacnet_destination_encode(apdu, destination); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag(apdu, tag_number); + apdu_len += len; + } + + return apdu_len; +} + +/** + * @brief Decode the BACnetDestination complex data + * + * BACnetDestination ::= SEQUENCE { + * valid-days BACnetDaysOfWeek, + * from-time Time, + * to-time Time, + * recipient BACnetRecipient, + * process-identifier Unsigned32, + * issue-confirmed-notifications BOOLEAN, + * transitions BACnetEventTransitionBits + * } + * + * @param apdu Pointer to the buffer for decoding. + * @param apdu_size Count of valid bytes in the buffer. + * @param destination Pointer to the property data to be encoded. + * + * @return bytes encoded or #BACNET_STATUS_REJECT on error. + */ +int bacnet_destination_decode( + uint8_t *apdu, int apdu_size, BACNET_DESTINATION *destination) +{ + int len = 0, apdu_len = 0; + BACNET_APPLICATION_DATA_VALUE value = { 0 }; + + if (!apdu) { + return BACNET_STATUS_REJECT; + } + if (!destination) { + return BACNET_STATUS_REJECT; + } + /* Decode Valid Days */ + len = bacapp_decode_application_data(apdu, apdu_size, &value); + if ((len == 0) || (len == BACNET_STATUS_ERROR) || + (value.tag != BACNET_APPLICATION_TAG_BIT_STRING)) { + return BACNET_STATUS_REJECT; + } + bitstring_copy(&destination->ValidDays, &value.type.Bit_String); + apdu_len += len; + apdu += len; + /* Decode From Time */ + len = bacapp_decode_application_data(apdu, apdu_size, &value); + if ((len == 0) || (len == BACNET_STATUS_ERROR) || + (value.tag != BACNET_APPLICATION_TAG_TIME)) { + return BACNET_STATUS_REJECT; + } + /* store value */ + datetime_copy_time(&destination->FromTime, &value.type.Time); + apdu_len += len; + apdu += len; + /* Decode To Time */ + len = bacapp_decode_application_data(apdu, apdu_size, &value); + if ((len == 0) || (len == BACNET_STATUS_ERROR) || + (value.tag != BACNET_APPLICATION_TAG_TIME)) { + return BACNET_STATUS_REJECT; + } + /* store value */ + datetime_copy_time(&destination->ToTime, &value.type.Time); + apdu_len += len; + apdu += len; + if (decode_is_context_tag(apdu, BACNET_RECIPIENT_TAG_DEVICE)) { + /* device [0] BACnetObjectIdentifier */ + destination->Recipient.tag = BACNET_RECIPIENT_TAG_DEVICE; + len = decode_context_object_id(apdu, BACNET_RECIPIENT_TAG_DEVICE, + &destination->Recipient.type.device.type, + &destination->Recipient.type.device.instance); + if (len == BACNET_STATUS_ERROR) { + return BACNET_STATUS_REJECT; + } + if (destination->Recipient.type.device.type != OBJECT_DEVICE) { + return BACNET_STATUS_REJECT; + } + apdu_len += len; + apdu += len; + } else if (decode_is_opening_tag_number( + apdu, BACNET_RECIPIENT_TAG_ADDRESS)) { + /* address [1] BACnetAddress */ + destination->Recipient.tag = BACNET_RECIPIENT_TAG_ADDRESS; + /* opening tag [1] is len 1 */ + len = 1; + apdu_len += len; + apdu += len; + len = decode_bacnet_address(apdu, &destination->Recipient.type.address); + if ((len == 0) || (len == BACNET_STATUS_ERROR)) { + return BACNET_STATUS_REJECT; + } + apdu_len += len; + apdu += len; + /* closing tag [1] */ + if (decode_is_closing_tag_number(apdu, BACNET_RECIPIENT_TAG_ADDRESS)) { + /* closing tag [1] is len 1 */ + len = 1; + apdu_len += len; + apdu += len; + } else { + return BACNET_STATUS_REJECT; + } + } else { + return BACNET_STATUS_REJECT; + } + /* Process Identifier */ + len = bacapp_decode_application_data(apdu, apdu_size, &value); + if ((len == 0) || (len == BACNET_STATUS_ERROR) || + (value.tag != BACNET_APPLICATION_TAG_UNSIGNED_INT)) { + return BACNET_STATUS_REJECT; + } + /* store value */ + destination->ProcessIdentifier = value.type.Unsigned_Int; + apdu_len += len; + apdu += len; + /* Issue Confirmed Notifications */ + len = bacapp_decode_application_data(apdu, apdu_size, &value); + if ((len == 0) || (len == BACNET_STATUS_ERROR) || + (value.tag != BACNET_APPLICATION_TAG_BOOLEAN)) { + return BACNET_STATUS_REJECT; + } + /* store value */ + destination->ConfirmedNotify = value.type.Boolean; + apdu_len += len; + apdu += len; + /* Transitions */ + len = bacapp_decode_application_data(apdu, apdu_size, &value); + if ((len == 0) || (len == BACNET_STATUS_ERROR) || + (value.tag != BACNET_APPLICATION_TAG_BIT_STRING)) { + return BACNET_STATUS_REJECT; + } + /* store value */ + bitstring_copy(&destination->Transitions, &value.type.Bit_String); + apdu_len += len; + + return apdu_len; +} + + +#define LEN_BRANCH(snprintf_expr) \ + do { \ + len = snprintf_expr; \ + output_len += len; \ + if (buf) { \ + if (len > buf_size) { \ + return -1; \ + } else { \ + buf += len; \ + buf_size -= len; \ + } \ + } \ + } while (0) + +/** + * Convert BACnet_Destination to ASCII for printing + * + * Output format: + * + * (ValidDays=[1,2,5,6,7];FromTime=0:00:00.0;ToTime=23:59:59.9;Recipient=Device(type=8,instance=15);ProcessIdentifier=0;ConfirmedNotify=false;Transitions=[to-offnormal,to-fault,to-normal]) + * + * - ValidDays ... array of numbers, 1=Mon through 7=Sun + * - FromTime, ToTime ... HH:MM:SS.s + * - Recipient ... two variants: Recipient=Device(type=8,instance=15) or Recipient=Address(net=1234,mac=c0:a8:00:0f) + * - type ... bacnet object type enum + * - instance ... bacnet object instance + * - net ... bacnet network number + * - mac ... bacnet MAC address; can be separated by colons or periods. + * - ProcessIdentifier ... 32bit unsigned int, process ID + * - ConfirmedNotify ... true or false + * - Transitions ... array with any of the three items: to-offnormal, to-fault, to-normal + * + * @param bacdest - Destination struct to convert to ASCII + * @param buf - ASCII output buffer + * @param buf_size - ASCII output buffer capacity + * + * @return the number of characters which would be generated for the given + * input, excluding the trailing null. negative is returned if the capacity was not sufficient. + * @note buf and buf_size may be null and zero to return only the size + */ +int bacnet_destination_to_ascii(const BACNET_DESTINATION *bacdest, char *buf, size_t buf_size) +{ + int len = 0; + int output_len = 0; + bool comma; + int i; + + LEN_BRANCH(snprintf(buf, buf_size, "(")); + + /* + BACnetDaysOfWeek ::= BIT STRING { + monday (0), + tuesday (1), + wednesday (2), + thursday (3), + friday (4), + saturday (5), + sunday (6) + } + */ + + /* Use numbers 1-7 (ISO 8601) */ + LEN_BRANCH(snprintf(buf, buf_size, "ValidDays=[")); + comma = false; + for (i = 0; i < 7; i++) { + if (bitstring_bit((BACNET_BIT_STRING *) &bacdest->ValidDays, i)) { + if (comma) { + LEN_BRANCH(snprintf(buf, buf_size, ",")); + } + LEN_BRANCH(snprintf(buf, buf_size, "%d", i + 1)); + comma = true; + } + } + LEN_BRANCH(snprintf(buf, buf_size, "];")); + + LEN_BRANCH(snprintf(buf, buf_size, "FromTime=%d:%02d:%02d.%02d;", + bacdest->FromTime.hour, + bacdest->FromTime.min, + bacdest->FromTime.sec, + bacdest->FromTime.hundredths + )); + + + LEN_BRANCH(snprintf(buf, buf_size, "ToTime=%d:%02d:%02d.%02d;", + bacdest->ToTime.hour, + bacdest->ToTime.min, + bacdest->ToTime.sec, + bacdest->ToTime.hundredths + )); + + LEN_BRANCH(snprintf(buf, buf_size, "Recipient=")); + if (bacdest->Recipient.tag == BACNET_RECIPIENT_TAG_DEVICE) { + LEN_BRANCH(snprintf(buf, buf_size, "Device(type=%d,instance=%lu)", + bacdest->Recipient.type.device.type, + (unsigned long)bacdest->Recipient.type.device.instance)); + } else { + /* + BACnetAddress ::= SEQUENCE { + network-number Unsigned16, -- A value of 0 indicates the local network + mac-address OCTET STRING -- A string of length 0 indicates a broadcast + } + */ + LEN_BRANCH(snprintf(buf, buf_size, "Address(net=%d,mac=", bacdest->Recipient.type.address.net)); + + /* TODO determine if it's IPv4+port or Ethernet mac address and print it nicer - how? Both are 6 bytes long. */ + + for (i = 0; i < bacdest->Recipient.type.address.mac_len; i++) { + if (i > 0) { + LEN_BRANCH(snprintf(buf, buf_size, ":")); + } + LEN_BRANCH(snprintf(buf, buf_size, "%02x", bacdest->Recipient.type.address.mac[i])); + } + LEN_BRANCH(snprintf(buf, buf_size, ")")); + } + LEN_BRANCH(snprintf(buf, buf_size, ";")); + + LEN_BRANCH(snprintf(buf, buf_size, "ProcessIdentifier=%lu;", + (unsigned long)bacdest->ProcessIdentifier)); + + LEN_BRANCH(snprintf(buf, buf_size, "ConfirmedNotify=%s;", + bacdest->ConfirmedNotify ? "true" : "false" + )); + + /* + BACnetEventTransitionBits ::= BIT STRING { + to-offnormal (0), + to-fault (1), + to-normal (2) + } + */ + LEN_BRANCH(snprintf(buf, buf_size, "Transitions=[")); + comma = false; + /* TODO remove casting when bitstring_bit() has const added - Github issue #320 */ + if (bitstring_bit((BACNET_BIT_STRING *) &bacdest->Transitions, TRANSITION_TO_OFFNORMAL)) { + LEN_BRANCH(snprintf(buf, buf_size, "to-offnormal")); + comma = true; + } + if (bitstring_bit((BACNET_BIT_STRING *) &bacdest->Transitions, TRANSITION_TO_FAULT)) { + if (comma) { + LEN_BRANCH(snprintf(buf, buf_size, ",")); + } + LEN_BRANCH(snprintf(buf, buf_size, "to-fault")); + comma = true; + } + if (bitstring_bit((BACNET_BIT_STRING *) &bacdest->Transitions, TRANSITION_TO_NORMAL)) { + if (comma) { + LEN_BRANCH(snprintf(buf, buf_size, ",")); + } + LEN_BRANCH(snprintf(buf, buf_size, "to-normal")); + } + LEN_BRANCH(snprintf(buf, buf_size, "])")); /* end of the outer paren */ + + return output_len; +} + + +/** + * Parse BACnet_Destination from ASCII string (as entered by user) + * + * @param bacdest - Destination struct to pupulate with data from the ASCII string + * @param buf - ASCII string, zero terminated + * @return true on success + */ +bool bacnet_destination_from_ascii(BACNET_DESTINATION *bacdest, const char *buf) +{ + enum ParsePhase { + PH_START, + PH_PAIR_SPACER, + PH_KEYWORD, + PH_VALUE_SPACER, + PH_VALUE + }; + enum ParseKeyword { + KW_ValidDays = 0, + KW_FromTime, + KW_ToTime, + KW_Recipient, + KW_ProcessIdentifier, + KW_ConfirmedNotify, + KW_Transitions, + KW_MAX + }; + enum ParsePhase ph; + size_t buflen; + size_t toklen; + size_t pos; + char c; + int i; + int j; + int _number_i; + size_t _must_consume_tmplen; + uint32_t tmp; + enum ParseKeyword kw = 0; + BACNET_TIME *ptime; + BACNET_MAC_ADDRESS tmpmac; + static const char * KW_LOOKUP[] = { + "ValidDays", + "FromTime", + "ToTime", + "Recipient", + "ProcessIdentifier", + "ConfirmedNotify", + "Transitions", + }; + + if (bacdest == NULL || buf == NULL) { + return false; + } + + bacnet_destination_default_init(bacdest); + + + /* Helper macros to simplify the parser ... */ + +/* true if the character is whitespace */ +#define ISWHITE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n') + +/* Discard characters while they match a given test. Goes to parse_end on NUL. + * ctest is a boolean expression where c is the tested character */ +#define DISCARD_WHILE(ctest) \ + do { \ + while(1) { \ + c = buf[pos]; \ + if (c == 0) { \ + goto parse_end; \ + } \ + if ((ctest)) { \ + pos++; \ + continue; \ + } \ + break; \ + } \ + } while(0) + +/* Discard all whitespace. Goes to parse_end on NUL. */ +#define DISCARD_WHITESPACE() DISCARD_WHILE(ISWHITE(c)) + +/* Must consume a given word; return false otherwise. */ +#define MUST_CONSUME(s) \ + do { \ + _must_consume_tmplen = strlen(s); \ + if (0 == strncmp(&buf[pos], s, _must_consume_tmplen)) { \ + pos += _must_consume_tmplen; \ + } else { \ + return false; \ + } \ + } while(0) + +/* Collect a decimal number and store the result into tmp; stop on a non-digit. Clobbers "c" and "tmp". TODO replace with strtol? */ +#define COLLECT_NUMBER_TMP(maxdigits) \ + do { \ + tmp = 0; \ + for (_number_i = 0; _number_i < (maxdigits); _number_i++) { \ + c = buf[pos]; \ + if (c >= '0' && c <= '9') { \ + tmp = (tmp * 10) + (c - '0'); \ + pos++; \ + } else { \ + break; \ + } \ + } \ + } while (0) + + /* Go through all key=value pieces in the string */ + buflen = strlen(buf); + ph = PH_START; + pos = 0; + while (pos < buflen) { + switch (ph) { + case PH_START: /* Expect the outer opening paren */ + DISCARD_WHITESPACE(); + MUST_CONSUME("("); + ph = PH_KEYWORD; + break; + + case PH_PAIR_SPACER: /* Expect end of string, or semicolon */ + DISCARD_WHILE(c == ')' || c == ']' || ISWHITE(c)); + MUST_CONSUME(";"); + DISCARD_WHITESPACE(); + ph = PH_KEYWORD; + break; + + case PH_KEYWORD: /* Key */ + DISCARD_WHITESPACE(); + for (i = 0; i < KW_MAX; i++) { + toklen = strlen(KW_LOOKUP[i]); + if (0 == strncmp(&buf[pos], KW_LOOKUP[i], toklen)) { + /* kw matched */ + kw = i; + pos += toklen; + ph = PH_VALUE_SPACER; + break; + } + } + if (ph != PH_VALUE_SPACER) { + /* Invalid token? */ + return false; + } + break; + + case PH_VALUE_SPACER: /* Equals between key and value, also consuming opening square bracket if present. */ + DISCARD_WHITESPACE(); + MUST_CONSUME("="); + DISCARD_WHILE(c == '[' || ISWHITE(c)); + ph = PH_VALUE; + break; + + case PH_VALUE: /* Parse the value */ + switch (kw) { + case KW_ValidDays: + /* Clear all weekdays */ + for (i = 0; i < MAX_BACNET_DAYS_OF_WEEK; i++) { + bitstring_set_bit(&bacdest->ValidDays, i, false); + } + + j = 0; /* 0 = number, 1 = comma */ + do { + DISCARD_WHITESPACE(); + c = buf[pos]; + if (c == 0) { + goto parse_end; + } + if (c == ']') { + pos++; + /* end of numbers */ + break; + } + if (j == 0) { + if (c >= '1' && c <= '7') { + bitstring_set_bit(&bacdest->ValidDays, c - '1', true); + pos++; + j = 1; + } else { + return false; + } + } else { + MUST_CONSUME(","); + j = 0; + } + } while (1); + break; + + case KW_FromTime: + case KW_ToTime: + DISCARD_WHITESPACE(); + + if (kw == KW_FromTime) { + ptime = &bacdest->FromTime; + } else { + ptime = &bacdest->ToTime; + } + + /* TODO implemented in bacapp_parse_application_data - extract & reuse? */ + + /* Hour */ + COLLECT_NUMBER_TMP(2); + ptime->hour = tmp; + MUST_CONSUME(":"); + + /* Min */ + COLLECT_NUMBER_TMP(2); + ptime->min = tmp; + + if (buf[pos] == ':') { + /* have seconds */ + MUST_CONSUME(":"); + /* Sec */ + COLLECT_NUMBER_TMP(2); + ptime->sec = tmp; + + /* ? hundredths */ + c = buf[pos]; + if (c == '.') { + pos++; + COLLECT_NUMBER_TMP(2); + ptime->hundredths = tmp; + } else { + ptime->hundredths = 0; + } + } else { + ptime->sec = 0; + ptime->hundredths = 0; + } + break; + + case KW_ProcessIdentifier: + DISCARD_WHITESPACE(); + /* Collect number */ + COLLECT_NUMBER_TMP(10); + bacdest->ProcessIdentifier = tmp; + break; + + case KW_ConfirmedNotify: + DISCARD_WHITESPACE(); + if (0 == strncmp(&buf[pos], "true", 4)) { + bacdest->ConfirmedNotify = true; + pos += 4; + } else if (0 == strncmp(&buf[pos], "false", 5)) { + bacdest->ConfirmedNotify = false; + pos += 5; + } else { + return false; + } + break; + + case KW_Transitions: + /* Clear all transitions */ + for (i = 0; i < MAX_BACNET_EVENT_TRANSITION; i++) { + bitstring_set_bit(&bacdest->Transitions, i, false); + } + j = 0; /* 0 = value, 1 = comma */ + do { + DISCARD_WHITESPACE(); + c = buf[pos]; + if (c == 0) { + goto parse_end; + } + if (c == ']') { + pos++; + break; + } + if (j == 0) { + if (0 == + strncmp(&buf[pos], "to-offnormal", 12)) { + bitstring_set_bit(&bacdest->Transitions, + TRANSITION_TO_OFFNORMAL, true); + pos += 12; + } else if (0 == + strncmp(&buf[pos], "to-fault", 8)) { + bitstring_set_bit(&bacdest->Transitions, + TRANSITION_TO_FAULT, true); + pos += 8; + } else if (0 == + strncmp(&buf[pos], "to-normal", 9)) { + bitstring_set_bit(&bacdest->Transitions, + TRANSITION_TO_NORMAL, true); + pos += 9; + } else { + return false; + } + j = 1; + } else { + MUST_CONSUME(","); + j = 0; + } + } while (1); + break; + + case KW_Recipient: + if (0 == strncmp(&buf[pos], "Device", 6)) { + pos += 6; + bacdest->Recipient.tag = BACNET_RECIPIENT_TAG_DEVICE; + + DISCARD_WHITESPACE(); + MUST_CONSUME("("); + DISCARD_WHITESPACE(); + MUST_CONSUME("type"); + DISCARD_WHITESPACE(); + MUST_CONSUME("="); + DISCARD_WHITESPACE(); + + COLLECT_NUMBER_TMP(6); + bacdest->Recipient.type.device.type = tmp; + + DISCARD_WHITESPACE(); + MUST_CONSUME(","); + DISCARD_WHITESPACE(); + MUST_CONSUME("instance"); + DISCARD_WHITESPACE(); + MUST_CONSUME("="); + DISCARD_WHITESPACE(); + + COLLECT_NUMBER_TMP(10); + bacdest->Recipient.type.device.instance = tmp; + + DISCARD_WHITESPACE(); + MUST_CONSUME(")"); + + } else if (0 == strncmp(&buf[pos], "Address", 7)) { + pos += 7; + bacdest->Recipient.tag = BACNET_RECIPIENT_TAG_ADDRESS; + + DISCARD_WHITESPACE(); + MUST_CONSUME("("); + DISCARD_WHITESPACE(); + MUST_CONSUME("net"); + DISCARD_WHITESPACE(); + MUST_CONSUME("="); + DISCARD_WHITESPACE(); + + COLLECT_NUMBER_TMP(6); + bacdest->Recipient.type.address.net = tmp; + + DISCARD_WHITESPACE(); + MUST_CONSUME(","); + DISCARD_WHITESPACE(); + MUST_CONSUME("mac"); + DISCARD_WHITESPACE(); + MUST_CONSUME("="); + DISCARD_WHITESPACE(); + + if (!bacnet_address_mac_from_ascii(&tmpmac, &buf[pos])) { + return false; + } + bacdest->Recipient.type.address.mac_len = tmpmac.len; + memcpy(&bacdest->Recipient.type.address.mac, &tmpmac.adr, MAX_MAC_LEN); + + /* address_mac_from_ascii doesn't return number of digits + * - we have to discard until ) */ + + DISCARD_WHILE(c != ')'); + pos++; /* discard the paren */ + } + break; + + default: + return false; + } + ph = PH_PAIR_SPACER; + break; + + default: + return false; + } + } +parse_end: + + return true; +} diff --git a/src/bacnet/bacdest.h b/src/bacnet/bacdest.h new file mode 100644 index 00000000..a195511a --- /dev/null +++ b/src/bacnet/bacdest.h @@ -0,0 +1,104 @@ +/** + * @file + * @brief API for BACnetDestination complex data type encode and decode + * @author Steve Karg + * @date May 2022 + * @section LICENSE + * + * Copyright (C) 2022 Steve Karg + * + * SPDX-License-Identifier: MIT + */ +#ifndef BACNET_DESTINATION_H +#define BACNET_DESTINATION_H + +#include +#include +#include "bacnet/bacnet_stack_exports.h" +#include "bacnet/bacenum.h" +#include "bacnet/bacstr.h" +#include "bacnet/datetime.h" + +typedef enum BACnet_Recipient_Tag { + BACNET_RECIPIENT_TAG_DEVICE = 0, + BACNET_RECIPIENT_TAG_ADDRESS = 1, + BACNET_RECIPIENT_TAG_MAX = 2 +} BACNET_RECIPIENT_TAG; + +typedef struct BACnet_Recipient { + uint8_t tag; + /** + * BACnetRecipient ::= CHOICE { + * device [0] BACnetObjectIdentifier, + * address [1] BACnetAddress + * } + */ + union { + BACNET_OBJECT_ID device; + BACNET_ADDRESS address; + } type; +} BACNET_RECIPIENT; + +typedef struct BACnet_Destination { + /** + * BACnetDestination ::= SEQUENCE { + * valid-days BACnetDaysOfWeek, + * from-time Time, + * to-time Time, + * recipient BACnetRecipient, + * process-identifier Unsigned32, + * issue-confirmed-notifications BOOLEAN, + * transitions BACnetEventTransitionBits + * } + */ + BACNET_BIT_STRING ValidDays; + BACNET_TIME FromTime; + BACNET_TIME ToTime; + BACNET_RECIPIENT Recipient; + uint32_t ProcessIdentifier; + bool ConfirmedNotify; + BACNET_BIT_STRING Transitions; +} BACNET_DESTINATION; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +BACNET_STACK_EXPORT +int bacnet_destination_encode(uint8_t *apdu, BACNET_DESTINATION *destination); +BACNET_STACK_EXPORT +int bacnet_destination_context_encode( + uint8_t *apdu, uint8_t tag_number, BACNET_DESTINATION *destination); +BACNET_STACK_EXPORT +int bacnet_destination_decode( + uint8_t *apdu, int apdu_len, BACNET_DESTINATION *destination); +BACNET_STACK_EXPORT +void bacnet_destination_default_init(BACNET_DESTINATION *destination); +BACNET_STACK_EXPORT +bool bacnet_destination_default(BACNET_DESTINATION *destination); +BACNET_STACK_EXPORT +bool bacnet_destination_same( + BACNET_DESTINATION *dest1, BACNET_DESTINATION *dest2); +BACNET_STACK_EXPORT +void bacnet_destination_copy(BACNET_DESTINATION *dest, BACNET_DESTINATION *src); + +BACNET_STACK_EXPORT +void bacnet_recipient_copy(BACNET_RECIPIENT *dest, BACNET_RECIPIENT *src); +BACNET_STACK_EXPORT +bool bacnet_recipient_same(BACNET_RECIPIENT *r1, BACNET_RECIPIENT *r2); +BACNET_STACK_EXPORT +bool bacnet_recipient_device_wildcard(BACNET_RECIPIENT *recipient); +BACNET_STACK_EXPORT +bool bacnet_recipient_device_valid(BACNET_RECIPIENT *recipient); + +BACNET_STACK_EXPORT +int bacnet_destination_to_ascii( + const BACNET_DESTINATION *bacdest, char *buf, size_t buf_size); +BACNET_STACK_EXPORT +bool bacnet_destination_from_ascii( + BACNET_DESTINATION *bacdest, const char *buf); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/src/bacnet/basic/object/client/device-client.c b/src/bacnet/basic/object/client/device-client.c index fa14b0b9..84a84dc3 100644 --- a/src/bacnet/basic/object/client/device-client.c +++ b/src/bacnet/basic/object/client/device-client.c @@ -116,14 +116,16 @@ static object_functions_t Object_Table[] = { Device_Read_Property_Local, NULL /* Write_Property */, Device_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */, NULL /* COV Clear */, - NULL /* Intrinsic Reporting */ }, + NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, #if (BACNET_PROTOCOL_REVISION >= 17) { OBJECT_NETWORK_PORT, Network_Port_Init, Network_Port_Count, Network_Port_Index_To_Instance, Network_Port_Valid_Instance, Network_Port_Object_Name, Network_Port_Read_Property, Network_Port_Write_Property, Network_Port_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, #endif { MAX_BACNET_OBJECT_TYPE, NULL /* Init */, NULL /* Count */, NULL /* Index_To_Instance */, NULL /* Valid_Instance */, @@ -131,7 +133,8 @@ static object_functions_t Object_Table[] = { NULL /* Write_Property */, NULL /* Property_Lists */, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */, NULL /* COV Clear */, - NULL /* Intrinsic Reporting */ } + NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ } }; /** Glue function to let the Device object, when called by a handler, diff --git a/src/bacnet/basic/object/device.c b/src/bacnet/basic/object/device.c index 592d5a4d..43a65bbc 100644 --- a/src/bacnet/basic/object/device.c +++ b/src/bacnet/basic/object/device.c @@ -97,14 +97,16 @@ static object_functions_t My_Object_Table[] = { Device_Read_Property_Local, Device_Write_Property_Local, Device_Property_Lists, DeviceGetRRInfo, NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */, NULL /* COV Clear */, - NULL /* Intrinsic Reporting */ }, + NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */, + NULL /* Remove_List_Element */ }, #if (BACNET_PROTOCOL_REVISION >= 17) { OBJECT_NETWORK_PORT, Network_Port_Init, Network_Port_Count, Network_Port_Index_To_Instance, Network_Port_Valid_Instance, Network_Port_Object_Name, Network_Port_Read_Property, Network_Port_Write_Property, Network_Port_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, #endif { OBJECT_ANALOG_INPUT, Analog_Input_Init, Analog_Input_Count, Analog_Input_Index_To_Instance, Analog_Input_Valid_Instance, @@ -112,39 +114,45 @@ static object_functions_t My_Object_Table[] = { Analog_Input_Write_Property, Analog_Input_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, Analog_Input_Encode_Value_List, Analog_Input_Change_Of_Value, - Analog_Input_Change_Of_Value_Clear, Analog_Input_Intrinsic_Reporting }, + Analog_Input_Change_Of_Value_Clear, Analog_Input_Intrinsic_Reporting, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, { OBJECT_ANALOG_OUTPUT, Analog_Output_Init, Analog_Output_Count, Analog_Output_Index_To_Instance, Analog_Output_Valid_Instance, Analog_Output_Object_Name, Analog_Output_Read_Property, Analog_Output_Write_Property, Analog_Output_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, { OBJECT_ANALOG_VALUE, Analog_Value_Init, Analog_Value_Count, Analog_Value_Index_To_Instance, Analog_Value_Valid_Instance, Analog_Value_Object_Name, Analog_Value_Read_Property, Analog_Value_Write_Property, Analog_Value_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, Analog_Value_Encode_Value_List, Analog_Value_Change_Of_Value, - Analog_Value_Change_Of_Value_Clear, Analog_Value_Intrinsic_Reporting }, + Analog_Value_Change_Of_Value_Clear, Analog_Value_Intrinsic_Reporting, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, { OBJECT_BINARY_INPUT, Binary_Input_Init, Binary_Input_Count, Binary_Input_Index_To_Instance, Binary_Input_Valid_Instance, Binary_Input_Object_Name, Binary_Input_Read_Property, Binary_Input_Write_Property, Binary_Input_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, Binary_Input_Encode_Value_List, Binary_Input_Change_Of_Value, - Binary_Input_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */ }, + Binary_Input_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, { OBJECT_BINARY_OUTPUT, Binary_Output_Init, Binary_Output_Count, Binary_Output_Index_To_Instance, Binary_Output_Valid_Instance, Binary_Output_Object_Name, Binary_Output_Read_Property, Binary_Output_Write_Property, Binary_Output_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, { OBJECT_BINARY_VALUE, Binary_Value_Init, Binary_Value_Count, Binary_Value_Index_To_Instance, Binary_Value_Valid_Instance, Binary_Value_Object_Name, Binary_Value_Read_Property, Binary_Value_Write_Property, Binary_Value_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, { OBJECT_CHARACTERSTRING_VALUE, CharacterString_Value_Init, CharacterString_Value_Count, CharacterString_Value_Index_To_Instance, CharacterString_Value_Valid_Instance, CharacterString_Value_Object_Name, @@ -154,18 +162,21 @@ static object_functions_t My_Object_Table[] = { NULL /* Iterator */, CharacterString_Value_Encode_Value_List, CharacterString_Value_Change_Of_Value, CharacterString_Value_Change_Of_Value_Clear, - NULL /* Intrinsic Reporting */ }, + NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */, + NULL /* Remove_List_Element */ }, { OBJECT_COMMAND, Command_Init, Command_Count, Command_Index_To_Instance, Command_Valid_Instance, Command_Object_Name, Command_Read_Property, Command_Write_Property, Command_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, { OBJECT_INTEGER_VALUE, Integer_Value_Init, Integer_Value_Count, Integer_Value_Index_To_Instance, Integer_Value_Valid_Instance, Integer_Value_Object_Name, Integer_Value_Read_Property, Integer_Value_Write_Property, Integer_Value_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, #if defined(INTRINSIC_REPORTING) { OBJECT_NOTIFICATION_CLASS, Notification_Class_Init, Notification_Class_Count, Notification_Class_Index_To_Instance, @@ -173,86 +184,99 @@ static object_functions_t My_Object_Table[] = { Notification_Class_Read_Property, Notification_Class_Write_Property, Notification_Class_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */, - NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + Notification_Class_Add_List_Element, + Notification_Class_Remove_List_Element }, #endif { OBJECT_LIFE_SAFETY_POINT, Life_Safety_Point_Init, Life_Safety_Point_Count, Life_Safety_Point_Index_To_Instance, Life_Safety_Point_Valid_Instance, Life_Safety_Point_Object_Name, Life_Safety_Point_Read_Property, Life_Safety_Point_Write_Property, Life_Safety_Point_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, { OBJECT_LOAD_CONTROL, Load_Control_Init, Load_Control_Count, Load_Control_Index_To_Instance, Load_Control_Valid_Instance, Load_Control_Object_Name, Load_Control_Read_Property, Load_Control_Write_Property, Load_Control_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, { OBJECT_MULTI_STATE_INPUT, Multistate_Input_Init, Multistate_Input_Count, Multistate_Input_Index_To_Instance, Multistate_Input_Valid_Instance, Multistate_Input_Object_Name, Multistate_Input_Read_Property, Multistate_Input_Write_Property, Multistate_Input_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, { OBJECT_MULTI_STATE_OUTPUT, Multistate_Output_Init, Multistate_Output_Count, Multistate_Output_Index_To_Instance, Multistate_Output_Valid_Instance, Multistate_Output_Object_Name, Multistate_Output_Read_Property, Multistate_Output_Write_Property, Multistate_Output_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */, - NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, { OBJECT_MULTI_STATE_VALUE, Multistate_Value_Init, Multistate_Value_Count, Multistate_Value_Index_To_Instance, Multistate_Value_Valid_Instance, Multistate_Value_Object_Name, Multistate_Value_Read_Property, Multistate_Value_Write_Property, Multistate_Value_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, Multistate_Value_Encode_Value_List, Multistate_Value_Change_Of_Value, - Multistate_Value_Change_Of_Value_Clear, - NULL /* Intrinsic Reporting */ }, + Multistate_Value_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, { OBJECT_TRENDLOG, Trend_Log_Init, Trend_Log_Count, Trend_Log_Index_To_Instance, Trend_Log_Valid_Instance, Trend_Log_Object_Name, Trend_Log_Read_Property, Trend_Log_Write_Property, Trend_Log_Property_Lists, TrendLogGetRRInfo, NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */, - NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, #if (BACNET_PROTOCOL_REVISION >= 14) { OBJECT_LIGHTING_OUTPUT, Lighting_Output_Init, Lighting_Output_Count, Lighting_Output_Index_To_Instance, Lighting_Output_Valid_Instance, Lighting_Output_Object_Name, Lighting_Output_Read_Property, Lighting_Output_Write_Property, Lighting_Output_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, { OBJECT_CHANNEL, Channel_Init, Channel_Count, Channel_Index_To_Instance, Channel_Valid_Instance, Channel_Object_Name, Channel_Read_Property, Channel_Write_Property, Channel_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, #endif #if (BACNET_PROTOCOL_REVISION >= 24) { OBJECT_COLOR, Color_Init, Color_Count, Color_Index_To_Instance, Color_Valid_Instance, Color_Object_Name, Color_Read_Property, Color_Write_Property, Color_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */, - NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, { OBJECT_COLOR_TEMPERATURE, Color_Temperature_Init, Color_Temperature_Count, Color_Temperature_Index_To_Instance, Color_Temperature_Valid_Instance, Color_Temperature_Object_Name, Color_Temperature_Read_Property, Color_Temperature_Write_Property, Color_Temperature_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, #endif #if defined(BACFILE) { OBJECT_FILE, bacfile_init, bacfile_count, bacfile_index_to_instance, bacfile_valid_instance, bacfile_object_name, bacfile_read_property, bacfile_write_property, BACfile_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, #endif { OBJECT_OCTETSTRING_VALUE, OctetString_Value_Init, OctetString_Value_Count, OctetString_Value_Index_To_Instance, OctetString_Value_Valid_Instance, OctetString_Value_Object_Name, OctetString_Value_Read_Property, OctetString_Value_Write_Property, OctetString_Value_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, { OBJECT_POSITIVE_INTEGER_VALUE, PositiveInteger_Value_Init, PositiveInteger_Value_Count, PositiveInteger_Value_Index_To_Instance, PositiveInteger_Value_Valid_Instance, PositiveInteger_Value_Object_Name, @@ -260,26 +284,29 @@ static object_functions_t My_Object_Table[] = { PositiveInteger_Value_Write_Property, PositiveInteger_Value_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */, - NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, { OBJECT_SCHEDULE, Schedule_Init, Schedule_Count, Schedule_Index_To_Instance, Schedule_Valid_Instance, Schedule_Object_Name, Schedule_Read_Property, Schedule_Write_Property, Schedule_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */, NULL /* COV Clear */, - NULL /* Intrinsic Reporting */ }, + NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */, + NULL /* Remove_List_Element */ }, { OBJECT_ACCUMULATOR, Accumulator_Init, Accumulator_Count, Accumulator_Index_To_Instance, Accumulator_Valid_Instance, Accumulator_Object_Name, Accumulator_Read_Property, Accumulator_Write_Property, Accumulator_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ }, + NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }, { MAX_BACNET_OBJECT_TYPE, NULL /* Init */, NULL /* Count */, NULL /* Index_To_Instance */, NULL /* Valid_Instance */, NULL /* Object_Name */, NULL /* Read_Property */, NULL /* Write_Property */, NULL /* Property_Lists */, NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */, - NULL /* COV */, NULL /* COV Clear */, - NULL /* Intrinsic Reporting */ } + NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */, + NULL /* Add_List_Element */, NULL /* Remove_List_Element */ } }; /** Glue function to let the Device object, when called by a handler, @@ -1686,7 +1713,7 @@ bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data) */ bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) { - bool status = false; /* Ever the pessamist! */ + bool status = false; /* Ever the pessimist! */ struct object_functions *pObject = NULL; /* initialize the default return values */ @@ -1722,6 +1749,76 @@ bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) return (status); } +/** + * @brief AddListElement from an object list property + * @param list_element [in] Pointer to the BACnet_List_Element_Data structure, + * which is packed with the information from the request. + * @return The length of the apdu encoded or #BACNET_STATUS_ERROR or + * #BACNET_STATUS_ABORT or #BACNET_STATUS_REJECT. + */ +int Device_Add_List_Element( + BACNET_LIST_ELEMENT_DATA * list_element) +{ + int status = BACNET_STATUS_ERROR; + struct object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(list_element->object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(list_element->object_instance)) { + if (pObject->Object_Add_List_Element) { + status = pObject->Object_Add_List_Element(list_element); + } else { + list_element->error_class = ERROR_CLASS_PROPERTY; + list_element->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + } else { + list_element->error_class = ERROR_CLASS_OBJECT; + list_element->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + } else { + list_element->error_class = ERROR_CLASS_OBJECT; + list_element->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + + return status; +} + +/** + * @brief RemoveListElement from an object list property + * @param list_element [in] Pointer to the BACnet_List_Element_Data structure, + * which is packed with the information from the request. + * @return The length of the apdu encoded or #BACNET_STATUS_ERROR or + * #BACNET_STATUS_ABORT or #BACNET_STATUS_REJECT. + */ +int Device_Remove_List_Element( + BACNET_LIST_ELEMENT_DATA * list_element) +{ + int status = BACNET_STATUS_ERROR; + struct object_functions *pObject = NULL; + + pObject = Device_Objects_Find_Functions(list_element->object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(list_element->object_instance)) { + if (pObject->Object_Remove_List_Element) { + status = pObject->Object_Remove_List_Element(list_element); + } else { + list_element->error_class = ERROR_CLASS_PROPERTY; + list_element->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + } else { + list_element->error_class = ERROR_CLASS_OBJECT; + list_element->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + } else { + list_element->error_class = ERROR_CLASS_OBJECT; + list_element->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + + return status; +} + /** Looks up the requested Object, and fills the Property Value list. * If the Object or Property can't be found, returns false. * @ingroup ObjHelpers @@ -1734,7 +1831,7 @@ bool Device_Encode_Value_List(BACNET_OBJECT_TYPE object_type, uint32_t object_instance, BACNET_PROPERTY_VALUE *value_list) { - bool status = false; /* Ever the pessamist! */ + bool status = false; /* Ever the pessimist! */ struct object_functions *pObject = NULL; pObject = Device_Objects_Find_Functions(object_type); diff --git a/src/bacnet/basic/object/device.h b/src/bacnet/basic/object/device.h index 6ad4b596..4a3f3fce 100644 --- a/src/bacnet/basic/object/device.h +++ b/src/bacnet/basic/object/device.h @@ -34,6 +34,7 @@ #include "bacnet/bacnet_stack_exports.h" #include "bacnet/bacdef.h" #include "bacnet/bacenum.h" +#include "bacnet/list_element.h" #include "bacnet/wp.h" #include "bacnet/rd.h" #include "bacnet/rp.h" @@ -165,6 +166,8 @@ typedef struct object_functions { object_cov_function Object_COV; object_cov_clear_function Object_COV_Clear; object_intrinsic_reporting_function Object_Intrinsic_Reporting; + list_element_function Object_Add_List_Element; + list_element_function Object_Remove_List_Element; } object_functions_t; /* String Lengths - excluding any nul terminator */ @@ -431,6 +434,14 @@ extern "C" { bool Device_Write_Property( BACNET_WRITE_PROPERTY_DATA * wp_data); + BACNET_STACK_EXPORT + int Device_Add_List_Element( + BACNET_LIST_ELEMENT_DATA *list_element); + + BACNET_STACK_EXPORT + int Device_Remove_List_Element( + BACNET_LIST_ELEMENT_DATA *list_element); + BACNET_STACK_EXPORT bool DeviceGetRRInfo( BACNET_READ_RANGE_DATA * pRequest, /* Info on the request */ diff --git a/src/bacnet/basic/object/nc.c b/src/bacnet/basic/object/nc.c index 65e469ff..85cda48b 100644 --- a/src/bacnet/basic/object/nc.c +++ b/src/bacnet/basic/object/nc.c @@ -38,8 +38,10 @@ #include "bacnet/bacdcode.h" #include "bacnet/bacenum.h" #include "bacnet/bacapp.h" +#include "bacnet/bacdest.h" #include "bacnet/basic/services.h" #include "bacnet/config.h" +#include "bacnet/datetime.h" #include "bacnet/basic/object/device.h" #include "bacnet/event.h" #include "bacnet/basic/services.h" @@ -57,6 +59,8 @@ #if defined(INTRINSIC_REPORTING) static NOTIFICATION_CLASS_INFO NC_Info[MAX_NOTIFICATION_CLASSES]; +/* buffer for sending event messages */ +static uint8_t Event_Buffer[MAX_APDU]; /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Notification_Properties_Required[] = { PROP_OBJECT_IDENTIFIER, @@ -89,34 +93,16 @@ void Notification_Class_Init(void) memset(&NC_Info[NotifyIdx], 0x00, sizeof(NOTIFICATION_CLASS_INFO)); /* set the basic parameters */ NC_Info[NotifyIdx].Ack_Required = 0; - NC_Info[NotifyIdx].Priority[TRANSITION_TO_OFFNORMAL] = - 255; /* The lowest priority for Normal message. */ - NC_Info[NotifyIdx].Priority[TRANSITION_TO_FAULT] = - 255; /* The lowest priority for Normal message. */ - NC_Info[NotifyIdx].Priority[TRANSITION_TO_NORMAL] = - 255; /* PRINTF lowest priority for Normal message. */ - /* configure for every day, all day long */ - for (i = 0; i < MAX_BACNET_DAYS_OF_WEEK; i++) { - NC_Info[NotifyIdx].Recipient_List->ValidDays |= (1 << i); + /* The lowest priority for Normal message = 255 */ + NC_Info[NotifyIdx].Priority[TRANSITION_TO_OFFNORMAL] = 255; + NC_Info[NotifyIdx].Priority[TRANSITION_TO_FAULT] = 255; + NC_Info[NotifyIdx].Priority[TRANSITION_TO_NORMAL] = 255; + /* note: default uses wildcard device destination */ + for (i = 0; i < NC_MAX_RECIPIENTS; i++) { + BACNET_DESTINATION *destination; + destination = &NC_Info[NotifyIdx].Recipient_List[i]; + bacnet_destination_default_init(destination); } - NC_Info[NotifyIdx].Recipient_List->FromTime.hour = 0; - NC_Info[NotifyIdx].Recipient_List->FromTime.min = 0; - NC_Info[NotifyIdx].Recipient_List->FromTime.sec = 0; - NC_Info[NotifyIdx].Recipient_List->FromTime.hundredths = 0; - NC_Info[NotifyIdx].Recipient_List->ToTime.hour = 23; - NC_Info[NotifyIdx].Recipient_List->ToTime.min = 59; - NC_Info[NotifyIdx].Recipient_List->ToTime.sec = 59; - NC_Info[NotifyIdx].Recipient_List->ToTime.hundredths = 0; - NC_Info[NotifyIdx].Recipient_List->Transitions = - TRANSITION_TO_OFFNORMAL_MASKED | TRANSITION_TO_FAULT_MASKED | - TRANSITION_TO_NORMAL_MASKED; - NC_Info[NotifyIdx].Recipient_List->ConfirmedNotify = false; - NC_Info[NotifyIdx].Recipient_List->ConfirmedNotify = false; - NC_Info[NotifyIdx].Recipient_List->Recipient.RecipientType = - RECIPIENT_TYPE_DEVICE; - /* initialize to *wildcard* device instance - invalid! */ - NC_Info[NotifyIdx].Recipient_List->Recipient._.DeviceIdentifier = - BACNET_MAX_INSTANCE; } return; @@ -184,12 +170,12 @@ int Notification_Class_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) { NOTIFICATION_CLASS_INFO *CurrentNotify; BACNET_CHARACTER_STRING char_string; - BACNET_OCTET_STRING octet_string; BACNET_BIT_STRING bit_string; uint8_t *apdu = NULL; uint8_t u8Val; int idx; int apdu_len = 0; /* return value */ + uint16_t apdu_max = 0; if ((rpdata == NULL) || (rpdata->application_data == NULL) || (rpdata->application_data_len == 0)) { @@ -197,6 +183,7 @@ int Notification_Class_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) } apdu = rpdata->application_data; + apdu_max = rpdata->application_data_len; CurrentNotify = &NC_Info[Notification_Class_Instance_To_Index(rpdata->object_instance)]; @@ -262,103 +249,32 @@ int Notification_Class_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) break; case PROP_RECIPIENT_LIST: - /* encode all entry of Recipient_List */ + /* get the size of all entry of Recipient_List */ for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++) { - BACNET_DESTINATION *RecipientEntry; - int i = 0; - - /* get pointer of current element for Recipient_List - easier - * for use */ - RecipientEntry = &CurrentNotify->Recipient_List[idx]; - if (RecipientEntry->Recipient.RecipientType != - RECIPIENT_TYPE_NOTINITIALIZED) { - /* Valid Days - BACnetDaysOfWeek - [bitstring] monday-sunday - */ - u8Val = 0x01; - bitstring_init(&bit_string); - - for (i = 0; i < MAX_BACNET_DAYS_OF_WEEK; i++) { - if (RecipientEntry->ValidDays & u8Val) - bitstring_set_bit(&bit_string, i, true); - else - bitstring_set_bit(&bit_string, i, false); - u8Val <<= 1; /* next day */ - } - apdu_len += encode_application_bitstring( - &apdu[apdu_len], &bit_string); - - /* From Time */ - apdu_len += encode_application_time( - &apdu[apdu_len], &RecipientEntry->FromTime); - - /* To Time */ - apdu_len += encode_application_time( - &apdu[apdu_len], &RecipientEntry->ToTime); - - /* - BACnetRecipient ::= CHOICE { - device [0] BACnetObjectIdentifier, - address [1] BACnetAddress - } */ - - /* CHOICE - device [0] BACnetObjectIdentifier */ - if (RecipientEntry->Recipient.RecipientType == - RECIPIENT_TYPE_DEVICE) { - apdu_len += encode_context_object_id(&apdu[apdu_len], 0, - OBJECT_DEVICE, - RecipientEntry->Recipient._.DeviceIdentifier); - } - /* CHOICE - address [1] BACnetAddress */ - else if (RecipientEntry->Recipient.RecipientType == - RECIPIENT_TYPE_ADDRESS) { - /* opening tag 1 */ - apdu_len += encode_opening_tag(&apdu[apdu_len], 1); - /* network-number Unsigned16, */ - apdu_len += encode_application_unsigned(&apdu[apdu_len], - RecipientEntry->Recipient._.Address.net); - - /* mac-address OCTET STRING */ - if (RecipientEntry->Recipient._.Address.net) { - octetstring_init(&octet_string, - RecipientEntry->Recipient._.Address.adr, - RecipientEntry->Recipient._.Address.len); - } else { - octetstring_init(&octet_string, - RecipientEntry->Recipient._.Address.mac, - RecipientEntry->Recipient._.Address.mac_len); - } - apdu_len += encode_application_octet_string( - &apdu[apdu_len], &octet_string); - - /* closing tag 1 */ - apdu_len += encode_closing_tag(&apdu[apdu_len], 1); - - } else { - ; - } /* shouldn't happen */ - - /* Process Identifier - Unsigned32 */ - apdu_len += encode_application_unsigned( - &apdu[apdu_len], RecipientEntry->ProcessIdentifier); - - /* Issue Confirmed Notifications - boolean */ - apdu_len += encode_application_boolean( - &apdu[apdu_len], RecipientEntry->ConfirmedNotify); - - /* Transitions - BACnet Event Transition Bits [bitstring] */ - u8Val = RecipientEntry->Transitions; - - bitstring_init(&bit_string); - bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, - (u8Val & TRANSITION_TO_OFFNORMAL_MASKED) ? true - : false); - bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, - (u8Val & TRANSITION_TO_FAULT_MASKED) ? true : false); - bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, - (u8Val & TRANSITION_TO_NORMAL_MASKED) ? true : false); - - apdu_len += encode_application_bitstring( - &apdu[apdu_len], &bit_string); + BACNET_DESTINATION *Destination; + BACNET_RECIPIENT *Recipient; + Destination = &CurrentNotify->Recipient_List[idx]; + Recipient = &Destination->Recipient; + if (!bacnet_recipient_device_wildcard(Recipient)) { + apdu_len += bacnet_destination_encode(NULL, Destination); + } + } + if (apdu_len > apdu_max) { + /* Abort response */ + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + apdu_len = BACNET_STATUS_ABORT; + break; + } + /* size fits, therefore, encode all entry of Recipient_List */ + for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++) { + BACNET_DESTINATION *Destination; + BACNET_RECIPIENT *Recipient; + Destination = &CurrentNotify->Recipient_List[idx]; + Recipient = &Destination->Recipient; + if (!bacnet_recipient_device_wildcard(Recipient)) { + apdu_len += + bacnet_destination_encode(&apdu[apdu_len], Destination); } } break; @@ -481,232 +397,26 @@ bool Notification_Class_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) break; case PROP_RECIPIENT_LIST: - - memset(&TmpNotify, 0x00, sizeof(NOTIFICATION_CLASS_INFO)); + for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++) { + BACNET_DESTINATION *destination; + destination = &TmpNotify.Recipient_List[idx]; + bacnet_destination_default_init(destination); + } idx = 0; iOffset = 0; /* decode all packed */ while (iOffset < wp_data->application_data_len) { - /* Decode Valid Days */ - len = bacapp_decode_application_data( + BACNET_DESTINATION *destination; + destination = &TmpNotify.Recipient_List[idx]; + len = bacnet_destination_decode( &wp_data->application_data[iOffset], - wp_data->application_data_len, &value); - - if ((len == 0) || - (value.tag != BACNET_APPLICATION_TAG_BIT_STRING)) { - /* Bad decode, wrong tag or following required parameter - * missing */ + wp_data->application_data_len, destination); + if (len == BACNET_STATUS_REJECT) { wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; return false; } - - if (value.type.Bit_String.bits_used == MAX_BACNET_DAYS_OF_WEEK) - /* store value */ - TmpNotify.Recipient_List[idx].ValidDays = - value.type.Bit_String.value[0]; - else { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_OTHER; - return false; - } - - iOffset += len; - /* Decode From Time */ - len = bacapp_decode_application_data( - &wp_data->application_data[iOffset], - wp_data->application_data_len, &value); - - if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_TIME)) { - /* Bad decode, wrong tag or following required parameter - * missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - /* store value */ - TmpNotify.Recipient_List[idx].FromTime = value.type.Time; - - iOffset += len; - /* Decode To Time */ - len = bacapp_decode_application_data( - &wp_data->application_data[iOffset], - wp_data->application_data_len, &value); - - if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_TIME)) { - /* Bad decode, wrong tag or following required parameter - * missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - /* store value */ - TmpNotify.Recipient_List[idx].ToTime = value.type.Time; - - iOffset += len; - /* context tag [0] - Device */ - if (decode_is_context_tag( - &wp_data->application_data[iOffset], 0)) { - TmpNotify.Recipient_List[idx].Recipient.RecipientType = - RECIPIENT_TYPE_DEVICE; - /* Decode Network Number */ - len = bacapp_decode_context_data( - &wp_data->application_data[iOffset], - wp_data->application_data_len, &value, - PROP_RECIPIENT_LIST); - - if ((len == 0) || - (value.tag != BACNET_APPLICATION_TAG_OBJECT_ID)) { - /* Bad decode, wrong tag or following required parameter - * missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - /* store value */ - TmpNotify.Recipient_List[idx].Recipient._.DeviceIdentifier = - value.type.Object_Id.instance; - - iOffset += len; - } - /* opening tag [1] - Recipient */ - else if (decode_is_opening_tag_number( - &wp_data->application_data[iOffset], 1)) { - iOffset++; - TmpNotify.Recipient_List[idx].Recipient.RecipientType = - RECIPIENT_TYPE_ADDRESS; - /* Decode Network Number */ - len = bacapp_decode_application_data( - &wp_data->application_data[iOffset], - wp_data->application_data_len, &value); - - if ((len == 0) || - (value.tag != BACNET_APPLICATION_TAG_UNSIGNED_INT)) { - /* Bad decode, wrong tag or following required parameter - * missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - /* store value */ - TmpNotify.Recipient_List[idx].Recipient._.Address.net = - value.type.Unsigned_Int; - - iOffset += len; - /* Decode Address */ - len = bacapp_decode_application_data( - &wp_data->application_data[iOffset], - wp_data->application_data_len, &value); - - if ((len == 0) || - (value.tag != BACNET_APPLICATION_TAG_OCTET_STRING)) { - /* Bad decode, wrong tag or following required parameter - * missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - /* store value */ - if (TmpNotify.Recipient_List[idx].Recipient._.Address.net == - 0) { - memcpy(TmpNotify.Recipient_List[idx] - .Recipient._.Address.mac, - value.type.Octet_String.value, - value.type.Octet_String.length); - TmpNotify.Recipient_List[idx] - .Recipient._.Address.mac_len = - value.type.Octet_String.length; - } else { - memcpy(TmpNotify.Recipient_List[idx] - .Recipient._.Address.adr, - value.type.Octet_String.value, - value.type.Octet_String.length); - TmpNotify.Recipient_List[idx].Recipient._.Address.len = - value.type.Octet_String.length; - } - - iOffset += len; - /* closing tag [1] - Recipient */ - if (decode_is_closing_tag_number( - &wp_data->application_data[iOffset], 1)) - iOffset++; - else { - /* Bad decode, wrong tag or following required parameter - * missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - } else { - /* Bad decode, wrong tag or following required parameter - * missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - - /* Process Identifier */ - len = bacapp_decode_application_data( - &wp_data->application_data[iOffset], - wp_data->application_data_len, &value); - - if ((len == 0) || - (value.tag != BACNET_APPLICATION_TAG_UNSIGNED_INT)) { - /* Bad decode, wrong tag or following required parameter - * missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - /* store value */ - TmpNotify.Recipient_List[idx].ProcessIdentifier = - value.type.Unsigned_Int; - - iOffset += len; - /* Issue Confirmed Notifications */ - len = bacapp_decode_application_data( - &wp_data->application_data[iOffset], - wp_data->application_data_len, &value); - - if ((len == 0) || - (value.tag != BACNET_APPLICATION_TAG_BOOLEAN)) { - /* Bad decode, wrong tag or following required parameter - * missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - /* store value */ - TmpNotify.Recipient_List[idx].ConfirmedNotify = - value.type.Boolean; - - iOffset += len; - /* Transitions */ - len = bacapp_decode_application_data( - &wp_data->application_data[iOffset], - wp_data->application_data_len, &value); - - if ((len == 0) || - (value.tag != BACNET_APPLICATION_TAG_BIT_STRING)) { - /* Bad decode, wrong tag or following required parameter - * missing */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; - return false; - } - - if (value.type.Bit_String.bits_used == - MAX_BACNET_EVENT_TRANSITION) - /* store value */ - TmpNotify.Recipient_List[idx].Transitions = - value.type.Bit_String.value[0]; - else { - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_OTHER; - return false; - } iOffset += len; - /* Increasing element of list */ if (++idx >= NC_MAX_RECIPIENTS) { wp_data->error_class = ERROR_CLASS_RESOURCES; @@ -714,33 +424,24 @@ bool Notification_Class_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) return false; } } - /* Decoded all recipient list */ /* copy elements from temporary object */ for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++) { BACNET_ADDRESS src = { 0 }; unsigned max_apdu = 0; - int32_t DeviceID; + uint32_t device_id; + BACNET_DESTINATION *destination; + BACNET_RECIPIENT *recipient; - CurrentNotify->Recipient_List[idx] = - TmpNotify.Recipient_List[idx]; - - if (CurrentNotify->Recipient_List[idx] - .Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) { - /* copy Device_ID */ - DeviceID = CurrentNotify->Recipient_List[idx] - .Recipient._.DeviceIdentifier; - address_bind_request(DeviceID, &max_apdu, &src); - - } else if (CurrentNotify->Recipient_List[idx] - .Recipient.RecipientType == - RECIPIENT_TYPE_ADDRESS) { - /* copy Address */ - /* src = - * CurrentNotify->Recipient_List[idx].Recipient._.Address; - */ - /* address_bind_request(BACNET_MAX_INSTANCE, &max_apdu, - * &src); */ + destination = &CurrentNotify->Recipient_List[idx]; + bacnet_destination_copy( + destination, &TmpNotify.Recipient_List[idx]); + recipient = &destination->Recipient; + if (bacnet_recipient_device_valid(recipient)) { + device_id = recipient->type.device.instance; + address_bind_request(device_id, &max_apdu, &src); + } else if (recipient->tag == BACNET_RECIPIENT_TAG_ADDRESS) { + /* nothing to do - we have the address */ } } status = true; @@ -791,18 +492,22 @@ static bool IsRecipientActive( case EVENT_STATE_OFFNORMAL: case EVENT_STATE_HIGH_LIMIT: case EVENT_STATE_LOW_LIMIT: - if (!(pBacDest->Transitions & TRANSITION_TO_OFFNORMAL_MASKED)) + if (bitstring_bit( + &pBacDest->Transitions, TRANSITION_TO_OFFNORMAL)) { return false; + } break; case EVENT_STATE_FAULT: - if (!(pBacDest->Transitions & TRANSITION_TO_FAULT_MASKED)) + if (bitstring_bit(&pBacDest->Transitions, TRANSITION_TO_FAULT)) { return false; + } break; case EVENT_STATE_NORMAL: - if (!(pBacDest->Transitions & TRANSITION_TO_NORMAL_MASKED)) + if (bitstring_bit(&pBacDest->Transitions, TRANSITION_TO_NORMAL)) { return false; + } break; default: @@ -810,12 +515,12 @@ static bool IsRecipientActive( } /* get actual date and time */ - Device_getCurrentDateTime(&DateTime); + datetime_local(&DateTime.date, &DateTime.time, NULL, NULL); /* valid Days */ - if (!((0x01 << (DateTime.date.wday - 1)) & pBacDest->ValidDays)) + if (!(bitstring_bit(&pBacDest->ValidDays, (DateTime.date.wday - 1)))) { return false; - + } /* valid FromTime */ if (datetime_compare_time(&DateTime.time, &pBacDest->FromTime) < 0) return false; @@ -890,10 +595,9 @@ void Notification_Class_common_reporting_function( /* pointer to first recipient */ pBacDest = &CurrentNotify->Recipient_List[0]; for (index = 0; index < NC_MAX_RECIPIENTS; index++, pBacDest++) { - /* check if recipient is defined */ - if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_NOTINITIALIZED) - break; /* recipient doesn't defined - end of list */ - + if (bacnet_recipient_device_wildcard(&pBacDest->Recipient)) { + continue; + } if (IsRecipientActive(pBacDest, event_data->toState)) { BACNET_ADDRESS dest; uint32_t device_id; @@ -903,18 +607,17 @@ void Notification_Class_common_reporting_function( event_data->processIdentifier = pBacDest->ProcessIdentifier; /* send notification */ - if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) { + if (pBacDest->Recipient.tag == BACNET_RECIPIENT_TAG_DEVICE) { /* send notification to the specified device */ - device_id = pBacDest->Recipient._.DeviceIdentifier; + device_id = pBacDest->Recipient.type.device.instance; PRINTF("Notification Class[%u]: send notification to %u\n", event_data->notificationClass, (unsigned)device_id); if (pBacDest->ConfirmedNotify == true) Send_CEvent_Notify(device_id, event_data); else if (address_get_by_device(device_id, &max_apdu, &dest)) - Send_UEvent_Notify( - Handler_Transmit_Buffer, event_data, &dest); - } else if (pBacDest->Recipient.RecipientType == - RECIPIENT_TYPE_ADDRESS) { + Send_UEvent_Notify(Event_Buffer, event_data, &dest); + } else if (pBacDest->Recipient.tag == + BACNET_RECIPIENT_TAG_ADDRESS) { PRINTF("Notification Class[%u]: send notification to ADDR\n", event_data->notificationClass); /* send notification to the address indicated */ @@ -922,9 +625,8 @@ void Notification_Class_common_reporting_function( if (address_get_device_id(&dest, &device_id)) Send_CEvent_Notify(device_id, event_data); } else { - dest = pBacDest->Recipient._.Address; - Send_UEvent_Notify( - Handler_Transmit_Buffer, event_data, &dest); + dest = pBacDest->Recipient.type.address; + Send_UEvent_Notify(Event_Buffer, event_data, &dest); } } } @@ -935,39 +637,332 @@ void Notification_Class_common_reporting_function( /* It should be called periodically (example once per minute). */ void Notification_Class_find_recipient(void) { - NOTIFICATION_CLASS_INFO *CurrentNotify; - BACNET_DESTINATION *pBacDest; + NOTIFICATION_CLASS_INFO *notification; + BACNET_DESTINATION *destination; + BACNET_RECIPIENT *recipient; BACNET_ADDRESS src = { 0 }; unsigned max_apdu = 0; - uint32_t notify_index; - uint32_t DeviceID; - uint8_t idx; + uint32_t device_id; + unsigned i, j; - for (notify_index = 0; notify_index < MAX_NOTIFICATION_CLASSES; - notify_index++) { - /* pointer to current notification */ - CurrentNotify = - &NC_Info[Notification_Class_Instance_To_Index(notify_index)]; - /* pointer to first recipient */ - pBacDest = &CurrentNotify->Recipient_List[0]; - for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++, pBacDest++) { - if (CurrentNotify->Recipient_List[idx].Recipient.RecipientType == - RECIPIENT_TYPE_DEVICE) { - /* Device ID */ - DeviceID = CurrentNotify->Recipient_List[idx] - .Recipient._.DeviceIdentifier; - /* Send who_ is request only when address of device is unknown. - */ - if (DeviceID < BACNET_MAX_INSTANCE) { - /* note: BACNET_MAX_INSTANCE = wildcard, not valid */ - if (!address_bind_request(DeviceID, &max_apdu, &src)) { - Send_WhoIs(DeviceID, DeviceID); - } + for (i = 0; i < MAX_NOTIFICATION_CLASSES; i++) { + notification = &NC_Info[i]; + for (j = 0; j < NC_MAX_RECIPIENTS; j++) { + destination = ¬ification->Recipient_List[j]; + recipient = &destination->Recipient; + if (bacnet_recipient_device_valid(recipient)) { + device_id = recipient->type.device.instance; + if (!address_bind_request(device_id, &max_apdu, &src)) { + /* Send who_ is request only when + address of device is unknown. */ + Send_WhoIs(device_id, device_id); } - } else if (CurrentNotify->Recipient_List[idx] - .Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) { } } } } + +/** + * @brief AddListElement from an object list property + * @ingroup ObjHelpers + * @param list_element [in] Pointer to the BACnet_List_Element_Data structure, + * which is packed with the information from the request. + * @return #BACNET_STATUS_OK or #BACNET_STATUS_ERROR or + * #BACNET_STATUS_ABORT or #BACNET_STATUS_REJECT. + * @note 15.1.2 Service Procedure + * After verifying the validity of the request, the responding BACnet-user + * shall attempt to modify the object identified in the + * 'Object Identifier' parameter. If the identified object exists + * and has the property specified in the 'Property Identifier' parameter, + * and, if present, has the array element specified in the + * 'Property Array Index' parameter, an attempt shall be made to add all of + * the elements specified in the 'List of Elements' parameter to the + * specified property or array element of the property. If this + * attempt is successful, a 'Result(+)' primitive shall be issued. + * + * When comparing elements in the 'List of Elements' parameter with + * elements in the specified property or array element of the + * property, the complete element shall be compared unless the + * property description specifies otherwise. If one or more of the + * elements is already present in the BACnetLIST, it shall be updated + * with the provided element, that is, the existing element is + * over-written with the provided element. Optionally, if the provided + * element is exactly the same as the existing element in every + * way, it can be ignored, that is, not added to the BACnetLIST. + * Ignoring an element that already exists shall not cause the + * service to fail. + * + * If the specified object does not exist, the specified property + * does not exist, the specified array element does not exist, or the + * specified property or array element is not a BACnetLIST, then + * the service shall fail and a 'Result(-)' response primitive shall + * be issued. If one or more elements cannot be added to, or updated + * in, the BACnetLIST, a 'Result(-)' response primitive shall be + * issued and no elements shall be added to, or updated in, the BACnetLIST. + * + * The effect of this service shall be to add to, or update in, + * the BACnetLIST all of the specified elements, or to neither add nor + * update any elements at all. + */ +int Notification_Class_Add_List_Element( + BACNET_LIST_ELEMENT_DATA * list_element) +{ + NOTIFICATION_CLASS_INFO *notification = NULL; + BACNET_DESTINATION recipient_list[NC_MAX_RECIPIENTS] = { 0 }; + uint8_t *application_data = NULL; + int application_data_len = 0, len = 0; + uint32_t notify_index = 0; + unsigned index = 0; + unsigned element_count = 0, new_element_count = 0; + unsigned added_element_count = 0, same_element_count = 0; + bool same_element = false; + unsigned i = 0, j = 0; + + if (!list_element) { + return BACNET_STATUS_ABORT; + } + if (list_element->object_property != PROP_RECIPIENT_LIST) { + list_element->error_class = ERROR_CLASS_SERVICES; + list_element->error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST; + return BACNET_STATUS_ERROR; + } + if (list_element->array_index != BACNET_ARRAY_ALL) { + list_element->error_class = ERROR_CLASS_PROPERTY; + list_element->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return BACNET_STATUS_ERROR; + } + notify_index = + Notification_Class_Instance_To_Index(list_element->object_instance); + if (notify_index < MAX_NOTIFICATION_CLASSES) { + notification = &NC_Info[notify_index]; + } else { + list_element->error_class = ERROR_CLASS_OBJECT; + list_element->error_code = ERROR_CODE_UNKNOWN_OBJECT; + return BACNET_STATUS_ERROR; + } + /* get the current size of all entry of Recipient_List */ + for (index = 0; index < NC_MAX_RECIPIENTS; index++) { + BACNET_DESTINATION *d1; + BACNET_RECIPIENT *r1; + d1 = ¬ification->Recipient_List[index]; + r1 = &d1->Recipient; + if (!bacnet_recipient_device_wildcard(r1)) { + element_count++; + } + } + /* decode the elements */ + new_element_count = 0; + application_data = list_element->application_data; + application_data_len = list_element->application_data_len; + while (application_data_len > 0) { + len = bacnet_destination_decode(application_data, + application_data_len, &recipient_list[index]); + if (len > 0) { + new_element_count++; + application_data_len -= len; + } else { + list_element->first_failed_element_number = new_element_count; + list_element->error_class = ERROR_CLASS_PROPERTY; + list_element->error_code = ERROR_CODE_INVALID_DATA_ENCODING; + return BACNET_STATUS_ERROR; + } + } + if (new_element_count == 0) { + return BACNET_STATUS_OK; + } + /* determine the same and added recipient counts */ + same_element_count = 0; + added_element_count = 0; + for (i = 0; i < new_element_count; i++) { + BACNET_DESTINATION *d1; + BACNET_RECIPIENT *r1; + d1 = &recipient_list[i]; + r1 = &d1->Recipient; + same_element = false; + for (j = 0; j < NC_MAX_RECIPIENTS; j++) { + BACNET_DESTINATION *d2; + BACNET_RECIPIENT *r2; + d2 = ¬ification->Recipient_List[j]; + r2 = &d2->Recipient; + if (bacnet_recipient_same(r1, r2)) { + same_element = true; + break; + } + } + if (same_element) { + same_element_count++; + } else { + added_element_count++; + if ((added_element_count+element_count) > NC_MAX_RECIPIENTS) { + list_element->first_failed_element_number = 1 + i; + list_element->error_class = ERROR_CLASS_RESOURCES; + list_element->error_code = + ERROR_CODE_NO_SPACE_TO_ADD_LIST_ELEMENT; + return BACNET_STATUS_ERROR; + } + } + } + /* update existing and add new */ + for (i = 0; i < new_element_count; i++) { + BACNET_DESTINATION *d1; + BACNET_RECIPIENT *r1; + d1 = &recipient_list[i]; + r1 = &d1->Recipient; + same_element = false; + for (j = 0; j < NC_MAX_RECIPIENTS; j++) { + BACNET_DESTINATION *d2; + BACNET_RECIPIENT *r2; + d2 = ¬ification->Recipient_List[j]; + r2 = &d2->Recipient; + if (bacnet_recipient_same(r1, r2)) { + /* update existing element */ + same_element = true; + bacnet_destination_copy(d2, d1); + } + } + if (!same_element) { + /* add new element to next free slot */ + for (j = 0; j < NC_MAX_RECIPIENTS; j++) { + BACNET_DESTINATION *d2; + BACNET_RECIPIENT *r2; + d2 = ¬ification->Recipient_List[j]; + r2 = &d2->Recipient; + if (!bacnet_recipient_device_wildcard(r2)) { + bacnet_destination_copy(d2, d1); + break; + } + } + } + } + + return BACNET_STATUS_OK; +} + +/** + * @brief RemoveListElement from an object list property + * @ingroup ObjHelpers + * @param list_element [in] Pointer to the BACnet_List_Element_Data structure, + * which is packed with the information from the request. + * @return The length of the apdu encoded or #BACNET_STATUS_ERROR or + * #BACNET_STATUS_ABORT or #BACNET_STATUS_REJECT. + * @note After verifying the validity of the request, the responding + * BACnet-user shall attempt to modify the object identified in the + * 'Object Identifier' parameter. If the identified object exists and it + * has the property specified in the 'Property Identifier' parameter, + * and if present has the array element specified in the 'Property Array Index' + * parameter, an attempt shall be made to remove the + * elements in the 'List of Elements' from the property or array element + * of the property of the object. + * + * When comparing elements of the service with entries in the affected + * BACnetLIST, the complete element shall be compared + * unless the property description specifies otherwise. If one or more + * of the elements does not exist or cannot be removed because + * of insufficient authority, none of the elements shall be removed and + * a 'Result(-)' response primitive shall be issued. + */ +int Notification_Class_Remove_List_Element( + BACNET_LIST_ELEMENT_DATA * list_element) +{ + NOTIFICATION_CLASS_INFO *notification = NULL; + uint32_t notify_index = 0; + unsigned index = 0; + BACNET_DESTINATION recipient_list[NC_MAX_RECIPIENTS] = { 0 }; + uint8_t *application_data = NULL; + int application_data_len = 0, len = 0; + unsigned element_count = 0; + unsigned remove_element_count = 0; + bool same_element = false; + unsigned i = 0, j = 0; + + if (!list_element) { + return BACNET_STATUS_ABORT; + } + if (list_element->object_property != PROP_RECIPIENT_LIST) { + list_element->error_class = ERROR_CLASS_SERVICES; + list_element->error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST; + return BACNET_STATUS_ERROR; + } + if (list_element->array_index != BACNET_ARRAY_ALL) { + list_element->error_class = ERROR_CLASS_PROPERTY; + list_element->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return BACNET_STATUS_ERROR; + } + notify_index = + Notification_Class_Instance_To_Index(list_element->object_instance); + if (notify_index < MAX_NOTIFICATION_CLASSES) { + notification = &NC_Info[notify_index]; + } else { + list_element->error_class = ERROR_CLASS_OBJECT; + list_element->error_code = ERROR_CODE_UNKNOWN_OBJECT; + return BACNET_STATUS_ERROR; + } + /* get the current size of all entry of Recipient_List */ + for (index = 0; index < NC_MAX_RECIPIENTS; index++) { + BACNET_DESTINATION *d1; + BACNET_RECIPIENT *r1; + d1 = ¬ification->Recipient_List[index]; + r1 = &d1->Recipient; + if (!bacnet_recipient_device_wildcard(r1)) { + element_count++; + } + } + /* decode the elements */ + application_data = list_element->application_data; + application_data_len = list_element->application_data_len; + while (application_data_len > 0) { + len = bacnet_destination_decode(application_data, + application_data_len, &recipient_list[index]); + if (len > 0) { + remove_element_count++; + application_data_len -= len; + } else { + list_element->first_failed_element_number = remove_element_count; + list_element->error_class = ERROR_CLASS_PROPERTY; + list_element->error_code = ERROR_CODE_INVALID_DATA_ENCODING; + return BACNET_STATUS_ERROR; + } + } + /* determine if one or more of the elements does not exist */ + for (i = 0; i < remove_element_count; i++) { + BACNET_DESTINATION *d1; + BACNET_RECIPIENT *r1; + d1 = &recipient_list[i]; + r1 = &d1->Recipient; + same_element = false; + for (j = 0; j < NC_MAX_RECIPIENTS; j++) { + BACNET_DESTINATION *d2; + BACNET_RECIPIENT *r2; + d2 = ¬ification->Recipient_List[j]; + r2 = &d2->Recipient; + if (bacnet_recipient_same(r1, r2)) { + same_element = true; + } + } + if (!same_element) { + list_element->first_failed_element_number = 1 + i; + list_element->error_class = ERROR_CLASS_SERVICES; + list_element->error_code = ERROR_CODE_LIST_ELEMENT_NOT_FOUND; + return BACNET_STATUS_ERROR; + } + } + /* remove any matching elements */ + for (i = 0; i < remove_element_count; i++) { + BACNET_DESTINATION *d1; + BACNET_RECIPIENT *r1; + d1 = &recipient_list[i]; + r1 = &d1->Recipient; + for (j = 0; j < NC_MAX_RECIPIENTS; j++) { + BACNET_DESTINATION *d2; + BACNET_RECIPIENT *r2; + d2 = ¬ification->Recipient_List[j]; + r2 = &d2->Recipient; + if (bacnet_recipient_same(r1, r2)) { + bacnet_destination_default_init(d2); + } + } + } + + return BACNET_STATUS_OK; +} #endif /* defined(INTRINSIC_REPORTING) */ diff --git a/src/bacnet/basic/object/nc.h b/src/bacnet/basic/object/nc.h index 16cc6912..fa3cc9aa 100644 --- a/src/bacnet/basic/object/nc.h +++ b/src/bacnet/basic/object/nc.h @@ -1,152 +1,112 @@ /************************************************************************** -* -* Copyright (C) 2011 Krzysztof Malorny -* -* 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. -* -*********************************************************************/ + * + * Copyright (C) 2011 Krzysztof Malorny + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + *********************************************************************/ #ifndef NC_H #define NC_H #include "bacnet/bacnet_stack_exports.h" +#include "bacnet/bacdest.h" #include "bacnet/event.h" - +#include "bacnet/list_element.h" +#include "bacnet/rp.h" +#include "bacnet/wp.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ - -#define NC_RESCAN_RECIPIENTS_SECS 60 +#define NC_RESCAN_RECIPIENTS_SECS 60 /* max "length" of recipient_list */ #define NC_MAX_RECIPIENTS 10 -/* Recipient types */ - typedef enum { - RECIPIENT_TYPE_NOTINITIALIZED = 0, - RECIPIENT_TYPE_DEVICE = 1, - RECIPIENT_TYPE_ADDRESS = 2 - } NC_RECIPIENT_TYPE; - #if defined(INTRINSIC_REPORTING) -/* BACnetRecipient structure */ -/* -BACnetRecipient ::= CHOICE { - device [0] BACnetObjectIdentifier, - address [1] BACnetAddress -} -*/ - typedef struct BACnet_Recipient { - uint8_t RecipientType; /* Type of Recipient */ - union { - uint32_t DeviceIdentifier; - BACNET_ADDRESS Address; - } _; - } BACNET_RECIPIENT; - - -/* BACnetDestination structure */ - typedef struct BACnet_Destination { - uint8_t ValidDays; - BACNET_TIME FromTime; - BACNET_TIME ToTime; - BACNET_RECIPIENT Recipient; - uint32_t ProcessIdentifier; - uint8_t Transitions; - bool ConfirmedNotify; - } BACNET_DESTINATION; - /* Structure containing configuration for a Notification Class */ - typedef struct Notification_Class_info { - uint8_t Priority[MAX_BACNET_EVENT_TRANSITION]; /* BACnetARRAY[3] of Unsigned */ - uint8_t Ack_Required; /* BACnetEventTransitionBits */ - BACNET_DESTINATION Recipient_List[NC_MAX_RECIPIENTS]; /* List of BACnetDestination */ - } NOTIFICATION_CLASS_INFO; - +typedef struct Notification_Class_info { + uint8_t + Priority[MAX_BACNET_EVENT_TRANSITION]; /* BACnetARRAY[3] of Unsigned */ + uint8_t Ack_Required; /* BACnetEventTransitionBits */ + BACNET_DESTINATION + Recipient_List[NC_MAX_RECIPIENTS]; /* List of BACnetDestination */ +} NOTIFICATION_CLASS_INFO; /* Indicates whether the transaction has been confirmed */ - typedef struct Acked_info { - bool bIsAcked; /* true when transitions is acked */ - BACNET_DATE_TIME Time_Stamp; /* time stamp of when a alarm was generated */ - } ACKED_INFO; - +typedef struct Acked_info { + bool bIsAcked; /* true when transitions is acked */ + BACNET_DATE_TIME Time_Stamp; /* time stamp of when a alarm was generated */ +} ACKED_INFO; /* Information needed to send AckNotification */ - typedef struct Ack_Notification { - bool bSendAckNotify; /* true if need to send AckNotification */ - uint8_t EventState; - } ACK_NOTIFICATION; +typedef struct Ack_Notification { + bool bSendAckNotify; /* true if need to send AckNotification */ + uint8_t EventState; +} ACK_NOTIFICATION; +BACNET_STACK_EXPORT +void Notification_Class_Property_Lists( + const int **pRequired, const int **pOptional, const int **pProprietary); +BACNET_STACK_EXPORT +void Notification_Class_Init(void); - BACNET_STACK_EXPORT - void Notification_Class_Property_Lists( - const int **pRequired, - const int **pOptional, - const int **pProprietary); +bool Notification_Class_Valid_Instance(uint32_t object_instance); +BACNET_STACK_EXPORT +unsigned Notification_Class_Count(void); +BACNET_STACK_EXPORT +uint32_t Notification_Class_Index_To_Instance(unsigned index); +BACNET_STACK_EXPORT +unsigned Notification_Class_Instance_To_Index(uint32_t object_instance); +BACNET_STACK_EXPORT +bool Notification_Class_Object_Name( + uint32_t object_instance, BACNET_CHARACTER_STRING *object_name); - BACNET_STACK_EXPORT - void Notification_Class_Init( - void); +BACNET_STACK_EXPORT +int Notification_Class_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata); - bool Notification_Class_Valid_Instance( - uint32_t object_instance); - BACNET_STACK_EXPORT - unsigned Notification_Class_Count( - void); - BACNET_STACK_EXPORT - uint32_t Notification_Class_Index_To_Instance( - unsigned index); - BACNET_STACK_EXPORT - unsigned Notification_Class_Instance_To_Index( - uint32_t object_instance); - BACNET_STACK_EXPORT - bool Notification_Class_Object_Name( - uint32_t object_instance, - BACNET_CHARACTER_STRING * object_name); +BACNET_STACK_EXPORT +bool Notification_Class_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data); - BACNET_STACK_EXPORT - int Notification_Class_Read_Property( - BACNET_READ_PROPERTY_DATA * rpdata); +BACNET_STACK_EXPORT +int Notification_Class_Add_List_Element(BACNET_LIST_ELEMENT_DATA *list_element); - BACNET_STACK_EXPORT - bool Notification_Class_Write_Property( - BACNET_WRITE_PROPERTY_DATA * wp_data); +BACNET_STACK_EXPORT +int Notification_Class_Remove_List_Element( + BACNET_LIST_ELEMENT_DATA *list_element); - BACNET_STACK_EXPORT - void Notification_Class_Get_Priorities( - uint32_t Object_Instance, - uint32_t * pPriorityArray); +BACNET_STACK_EXPORT +void Notification_Class_Get_Priorities( + uint32_t Object_Instance, uint32_t *pPriorityArray); - BACNET_STACK_EXPORT - void Notification_Class_common_reporting_function( - BACNET_EVENT_NOTIFICATION_DATA * event_data); +BACNET_STACK_EXPORT +void Notification_Class_common_reporting_function( + BACNET_EVENT_NOTIFICATION_DATA *event_data); - BACNET_STACK_EXPORT - void Notification_Class_find_recipient( - void); +BACNET_STACK_EXPORT +void Notification_Class_find_recipient(void); #endif /* defined(INTRINSIC_REPORTING) */ - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/bacnet/basic/service/h_list_element.c b/src/bacnet/basic/service/h_list_element.c new file mode 100644 index 00000000..1ab6ca5f --- /dev/null +++ b/src/bacnet/basic/service/h_list_element.c @@ -0,0 +1,211 @@ +/** + * @file + * @brief AddListElement and RemoveListElement service application handlers + * @author Steve Karg + * @date December 2022 + * @section LICENSE + * + * Copyright (C) 2022 Steve Karg + * + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include "bacnet/config.h" +#include "bacnet/bacdef.h" +#include "bacnet/bacdcode.h" +#include "bacnet/bacerror.h" +#include "bacnet/apdu.h" +#include "bacnet/npdu.h" +#include "bacnet/abort.h" +#include "bacnet/list_element.h" +/* basic objects, services, TSM, and datalink */ +#include "bacnet/basic/object/device.h" +#include "bacnet/basic/tsm/tsm.h" +#include "bacnet/basic/services.h" +#include "bacnet/basic/sys/debug.h" +#include "bacnet/datalink/datalink.h" + +/** + * @brief Handler for a AddListElement Service request. + * This handler will be invoked by apdu_handler() if it has been enabled + * via call to apdu_set_confirmed_handler(). + * This handler builds a response packet, which is + * - an Abort if + * - the message is segmented + * - if decoding fails + * - a SimpleACK if Device_Add_List_Element() succeeds + * - an Error if Device_Add_List_Element() fails + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ +void handler_add_list_element(uint8_t *service_request, + uint16_t service_len, + BACNET_ADDRESS *src, + BACNET_CONFIRMED_SERVICE_DATA *service_data) +{ + BACNET_LIST_ELEMENT_DATA list_element = { 0 }; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + int len = 0; + bool status = true; + int pdu_len = 0; + int bytes_sent = 0; + + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = npdu_encode_pdu( + &Handler_Transmit_Buffer[0], src, &my_address, &npdu_data); + debug_perror("AddListElement: Received Request!\n"); + if (service_data->segmented_message) { + len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); + debug_perror("AddListElement: Segmented message. Sending Abort!\n"); + status = false; + } + if (status) { + /* decode the service request only */ + len = list_element_decode_service_request( + service_request, service_len, &list_element); + if (len > 0) { + debug_perror("AddListElement: type=%lu instance=%lu property=%lu " + "index=%ld\n", + (unsigned long)list_element.object_type, + (unsigned long)list_element.object_instance, + (unsigned long)list_element.object_property, + (long)list_element.array_index); + } else { + debug_perror("AddListElement: Unable to decode request!\n"); + } + /* bad decoding or something we didn't understand - send an abort */ + if (len <= 0) { + len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); + debug_perror("AddListElement: Bad Encoding. Sending Abort!\n"); + status = false; + } + if (status) { + if (Device_Add_List_Element(&list_element)) { + len = encode_simple_ack(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_ADD_LIST_ELEMENT); + debug_perror("AddListElement: Sending Simple Ack!\n"); + } else { + len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_ADD_LIST_ELEMENT, + list_element.error_class, list_element.error_code); + debug_perror("AddListElement: Sending Error!\n"); + } + } + } + /* Send PDU */ + pdu_len += len; + bytes_sent = datalink_send_pdu( + src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len); + if (bytes_sent <= 0) { + debug_perror( + "AddListElement: Failed to send PDU (%s)!\n", strerror(errno)); + } + + return; +} + +/** + * @brief Handler for a RemoveListElement Service request. + * This handler will be invoked by apdu_handler() if it has been enabled + * via call to apdu_set_confirmed_handler(). + * This handler builds a response packet, which is + * - an Abort if + * - the message is segmented + * - if decoding fails + * - a SimpleACK if Device_Remove_List_Element() succeeds + * - an Error if Device_Remove_List_Element() fails + * + * @param service_request [in] The contents of the service request. + * @param service_len [in] The length of the service_request. + * @param src [in] BACNET_ADDRESS of the source of the message + * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information + * decoded from the APDU header of this message. + */ +void handler_remove_list_element(uint8_t *service_request, + uint16_t service_len, + BACNET_ADDRESS *src, + BACNET_CONFIRMED_SERVICE_DATA *service_data) +{ + BACNET_LIST_ELEMENT_DATA list_element = { 0 }; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + int len = 0; + bool status = true; + int pdu_len = 0; + int bytes_sent = 0; + + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = npdu_encode_pdu( + &Handler_Transmit_Buffer[0], src, &my_address, &npdu_data); + debug_perror("RemoveListElement: Received Request!\n"); + if (service_data->segmented_message) { + len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); + debug_perror("RemoveListElement: Segmented message. Sending Abort!\n"); + status = false; + } + if (status) { + /* decode the service request only */ + len = list_element_decode_service_request( + service_request, service_len, &list_element); + if (len > 0) { + debug_perror("RemoveListElement: type=%lu instance=%lu " + "property=%lu index=%ld\n", + (unsigned long)list_element.object_type, + (unsigned long)list_element.object_instance, + (unsigned long)list_element.object_property, + (long)list_element.array_index); + } else { + debug_perror("RemoveListElement: Unable to decode request!\n"); + } + /* bad decoding or something we didn't understand - send an abort */ + if (len <= 0) { + len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); + debug_perror("RemoveListElement: Bad Encoding. Sending Abort!\n"); + status = false; + } + if (status) { + if (Device_Remove_List_Element(&list_element)) { + len = encode_simple_ack(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT); + debug_perror("RemoveListElement: Sending Simple Ack!\n"); + } else { + len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT, + list_element.error_class, list_element.error_code); + debug_perror("RemoveListElement: Sending Error!\n"); + } + } + } + /* Send PDU */ + pdu_len += len; + bytes_sent = datalink_send_pdu( + src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len); + if (bytes_sent <= 0) { + debug_perror( + "RemoveListElement: Failed to send PDU (%s)!\n", strerror(errno)); + } + + return; +} diff --git a/src/bacnet/basic/service/h_list_element.h b/src/bacnet/basic/service/h_list_element.h new file mode 100644 index 00000000..10445f56 --- /dev/null +++ b/src/bacnet/basic/service/h_list_element.h @@ -0,0 +1,43 @@ +/** + * @file + * @brief API for AddListElement and RemoveListElement application handlers + * @author Steve Karg + * @date December 2022 + * @section LICENSE + * + * Copyright (C) 2022 Steve Karg + * + * SPDX-License-Identifier: MIT + */ +#ifndef HANDLER_LIST_ELEMENT_H +#define HANDLER_LIST_ELEMENT_H + +#include +#include +#include "bacnet/bacnet_stack_exports.h" +#include "bacnet/bacenum.h" +#include "bacnet/bacdef.h" +#include "bacnet/apdu.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + BACNET_STACK_EXPORT + void handler_add_list_element( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA *service_data); + + BACNET_STACK_EXPORT + void handler_remove_list_element( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA *service_data); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/src/bacnet/basic/service/s_list_element.c b/src/bacnet/basic/service/s_list_element.c new file mode 100644 index 00000000..6b603eb1 --- /dev/null +++ b/src/bacnet/basic/service/s_list_element.c @@ -0,0 +1,282 @@ +/** + * @file + * @brief AddListElement and RemoveListElement service initiation + * @author Steve Karg + * @date January 2023 + * @section LICENSE + * + * Copyright (C) 2023 Steve Karg + * + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include "bacnet/config.h" +#include "bacnet/bacdef.h" +#include "bacnet/bacdcode.h" +#include "bacnet/bacerror.h" +#include "bacnet/apdu.h" +#include "bacnet/npdu.h" +#include "bacnet/abort.h" +#include "bacnet/bactext.h" +#include "bacnet/dcc.h" +#include "bacnet/list_element.h" +#include "bacnet/whois.h" +/* some demo stuff needed */ +#include "bacnet/basic/binding/address.h" +#include "bacnet/basic/object/device.h" +#include "bacnet/basic/services.h" +#include "bacnet/basic/sys/debug.h" +#include "bacnet/basic/tsm/tsm.h" +#include "bacnet/datalink/datalink.h" + +/** + * @brief Send a ListElement service message + * @param service [in] AddListElement or RemoveListElement + * @param device_id [in] ID of the destination device + * @param object_type [in] Type of the object whose property is to be written. + * @param object_instance [in] Instance # of the object to be written. + * @param object_property [in] Property to be written. + * @param object_value [in] The value to be written to the property. + * @param array_index [in] Optional: if the Property is an array, + * - 0 for the array size + * - 1 to n for individual array members + * - BACNET_ARRAY_ALL (~0) for the array value to be ignored (not sent) + * @return invoke id of outgoing message, or 0 on failure. + * @return the invoke ID for confirmed request, or zero on failure + */ +uint8_t Send_List_Element_Request_Data( + BACNET_CONFIRMED_SERVICE service, + uint32_t device_id, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + uint8_t *application_data, + int application_data_len, + uint32_t array_index) +{ + BACNET_ADDRESS dest; + BACNET_ADDRESS my_address; + unsigned max_apdu = 0; + uint8_t invoke_id = 0; + bool status = false; + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_LIST_ELEMENT_DATA data; + BACNET_NPDU_DATA npdu_data; + + if (!dcc_communication_enabled()) { + return 0; + } + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + /* is there a tsm available? */ + if (status) { + invoke_id = tsm_next_free_invokeID(); + } + if (invoke_id) { + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); + pdu_len = npdu_encode_pdu( + &Handler_Transmit_Buffer[0], &dest, &my_address, &npdu_data); + /* encode the APDU header portion of the packet */ + Handler_Transmit_Buffer[pdu_len++] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + Handler_Transmit_Buffer[pdu_len++] = + encode_max_segs_max_apdu(0, MAX_APDU); + Handler_Transmit_Buffer[pdu_len++] = invoke_id; + Handler_Transmit_Buffer[pdu_len++] = service; + /* encode the APDU service */ + data.object_type = object_type; + data.object_instance = object_instance; + data.object_property = object_property; + data.array_index = array_index; + data.application_data_len = application_data_len; + data.application_data = application_data; + len = list_element_encode_service_request( + &Handler_Transmit_Buffer[pdu_len], &data); + pdu_len += len; + /* will it fit in the sender? + note: if there is a bottleneck router in between + us and the destination, we won't know unless + we have a way to check for that and update the + max_apdu in the address binding table. */ + if ((unsigned)pdu_len < max_apdu) { + tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest, + &npdu_data, &Handler_Transmit_Buffer[0], (uint16_t)pdu_len); + bytes_sent = datalink_send_pdu( + &dest, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len); + if (bytes_sent <= 0) { + debug_perror("%s service: Failed to Send %i/%i (%s)!\n", + bactext_confirmed_service_name(service), bytes_sent, + pdu_len, strerror(errno)); + } + } else { + tsm_free_invoke_id(invoke_id); + invoke_id = 0; + debug_perror("%s service: Failed to Send " + "(exceeds destination maximum APDU)!\n", + bactext_confirmed_service_name(service)); + } + } + + return invoke_id; +} + +/** + * @brief Send an AddListElement service message + * @param device_id [in] ID of the destination device + * @param object_type [in] Type of the object whose property is to be written. + * @param object_instance [in] Instance # of the object to be written. + * @param object_property [in] Property to be written. + * @param object_value [in] The value to be written to the property. + * @param array_index [in] Optional: if the Property is an array, + * - 0 for the array size + * - 1 to n for individual array members + * - BACNET_ARRAY_ALL (~0) for the array value to be ignored (not sent) + * @return invoke id of outgoing message, or 0 on failure. + * @return the invoke ID for confirmed request, or zero on failure + */ +uint8_t Send_Add_List_Element_Request_Data(uint32_t device_id, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + uint8_t *application_data, + int application_data_len, + uint32_t array_index) +{ + return Send_List_Element_Request_Data( + SERVICE_CONFIRMED_ADD_LIST_ELEMENT, + device_id, + object_type, + object_instance, + object_property, + application_data, + application_data_len, + array_index); + +} + +/** + * @brief Send a RemoveListElement service message + * @param device_id [in] ID of the destination device + * @param object_type [in] Type of the object whose property is to be written. + * @param object_instance [in] Instance # of the object to be written. + * @param object_property [in] Property to be written. + * @param object_value [in] The value to be written to the property. + * @param array_index [in] Optional: if the Property is an array, + * - 0 for the array size + * - 1 to n for individual array members + * - BACNET_ARRAY_ALL (~0) for the array value to be ignored (not sent) + * @return invoke id of outgoing message, or 0 on failure. + * @return the invoke ID for confirmed request, or zero on failure + */ +uint8_t Send_Remove_List_Element_Request_Data(uint32_t device_id, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + uint8_t *application_data, + int application_data_len, + uint32_t array_index) +{ + return Send_List_Element_Request_Data( + SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT, + device_id, + object_type, + object_instance, + object_property, + application_data, + application_data_len, + array_index); + +} + +/** + * @brief Sends an AddListElement service message + * @param device_id [in] ID of the destination device + * @param object_type [in] Type of the object whose property is to be written. + * @param object_instance [in] Instance # of the object to be written. + * @param object_property [in] Property to be written. + * @param object_value [in] The value to be written to the property. + * @param array_index [in] Optional: if the Property is an array, + * - 0 for the array size + * - 1 to n for individual array members + * - BACNET_ARRAY_ALL (~0) for the array value to be ignored (not sent) + * @return invoke id of outgoing message, or 0 on failure. + */ +uint8_t Send_Add_List_Element_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, + uint32_t array_index) +{ + uint8_t application_data[MAX_APDU] = { 0 }; + int apdu_len = 0, len = 0; + + while (object_value) { + debug_printf("AddListElement service: " + "%s tag=%d\n", + (object_value->context_specific ? "context" : "application"), + (int)(object_value->context_specific ? object_value->context_tag + : object_value->tag)); + 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_Add_List_Element_Request_Data(device_id, object_type, + object_instance, object_property, &application_data[0], apdu_len, + array_index); +} + +/** + * @brief Sends an RemoveListElement service message + * @param device_id [in] ID of the destination device + * @param object_type [in] Type of the object whose property is to be written. + * @param object_instance [in] Instance # of the object to be written. + * @param object_property [in] Property to be written. + * @param object_value [in] The value to be written to the property. + * @param array_index [in] Optional: if the Property is an array, + * - 0 for the array size + * - 1 to n for individual array members + * - BACNET_ARRAY_ALL (~0) for the array value to be ignored (not sent) + * @return invoke id of outgoing message, or 0 on failure. + */ +uint8_t Send_Remove_List_Element_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, + uint32_t array_index) +{ + uint8_t application_data[MAX_APDU] = { 0 }; + int apdu_len = 0, len = 0; + + while (object_value) { + debug_perror("RemoveListElement service: " + "%s tag=%d\n", + (object_value->context_specific ? "context" : "application"), + (int)(object_value->context_specific ? object_value->context_tag + : object_value->tag)); + 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_Remove_List_Element_Request_Data(device_id, object_type, + object_instance, object_property, &application_data[0], apdu_len, + array_index); +} diff --git a/src/bacnet/basic/service/s_list_element.h b/src/bacnet/basic/service/s_list_element.h new file mode 100644 index 00000000..33067ea3 --- /dev/null +++ b/src/bacnet/basic/service/s_list_element.h @@ -0,0 +1,75 @@ +/** + * @file + * @brief AddListElement and RemoveListElement service initiation + * @author Steve Karg + * @date January 2023 + * @section LICENSE + * + * Copyright (C) 2023 Steve Karg + * + * SPDX-License-Identifier: MIT + */ +#ifndef SEND_LIST_ELEMENT_H +#define SEND_LIST_ELEMENT_H + +#include +#include +#include +#include +#include "bacnet/bacnet_stack_exports.h" +#include "bacnet/bacapp.h" +#include "bacnet/bacdef.h" +#include "bacnet/bacenum.h" +#include "bacnet/apdu.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +BACNET_STACK_EXPORT +uint8_t Send_List_Element_Request_Data( + BACNET_CONFIRMED_SERVICE service, + uint32_t device_id, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + uint8_t *application_data, + int application_data_len, + uint32_t array_index); + +BACNET_STACK_EXPORT +uint8_t Send_Add_List_Element_Request_Data(uint32_t device_id, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + uint8_t *application_data, + int application_data_len, + uint32_t array_index); +BACNET_STACK_EXPORT +uint8_t Send_Add_List_Element_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, + uint32_t array_index); + +BACNET_STACK_EXPORT +uint8_t Send_Remove_List_Element_Request_Data(uint32_t device_id, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + uint8_t *application_data, + int application_data_len, + uint32_t array_index); +BACNET_STACK_EXPORT +uint8_t Send_Remove_List_Element_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, + uint32_t array_index); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/src/bacnet/basic/services.h b/src/bacnet/basic/services.h index 5da1366c..dedda1e6 100644 --- a/src/bacnet/basic/services.h +++ b/src/bacnet/basic/services.h @@ -48,6 +48,7 @@ #include "bacnet/basic/service/h_getevent_a.h" #include "bacnet/basic/service/h_iam.h" #include "bacnet/basic/service/h_ihave.h" +#include "bacnet/basic/service/h_list_element.h" #include "bacnet/basic/service/h_lso.h" #include "bacnet/basic/service/h_noserv.h" #include "bacnet/basic/service/h_rd.h" @@ -79,6 +80,7 @@ #include "bacnet/basic/service/s_getevent.h" #include "bacnet/basic/service/s_iam.h" #include "bacnet/basic/service/s_ihave.h" +#include "bacnet/basic/service/s_list_element.h" #include "bacnet/basic/service/s_lso.h" #include "bacnet/basic/service/s_rd.h" #include "bacnet/basic/service/s_readrange.h" diff --git a/src/bacnet/list_element.c b/src/bacnet/list_element.c new file mode 100644 index 00000000..f23a3647 --- /dev/null +++ b/src/bacnet/list_element.c @@ -0,0 +1,363 @@ +/** + * @file + * @brief AddListElement and RemoveListElement service encode and decode + * @author Steve Karg + * @date December 2022 + * @section LICENSE + * + * Copyright (C) 2022 Steve Karg + * + * SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 + */ +#include +#include +#include "bacnet/bacenum.h" +#include "bacnet/bacdcode.h" +#include "bacnet/bacdef.h" +#include "bacnet/bacerror.h" +#include "bacnet/list_element.h" + +/** + * @brief Encode the Add/Remove ListElement service request only + * + * AddListElement-Request ::= SEQUENCE { + * object-identifier [0] BACnetObjectIdentifier, + * property-identifier [1] BACnetPropertyIdentifier, + * property-array-index [2] Unsigned OPTIONAL, + * -- used only with array datatype + * list-of-elements [3] ABSTRACT-SYNTAX.&Type + * } + * RemoveListElement-Request ::= SEQUENCE { + * object-identifier [0] BACnetObjectIdentifier, + * property-identifier [1] BACnetPropertyIdentifier, + * property-array-index [2] Unsigned OPTIONAL, + * -- used only with array datatype + * list-of-elements [3] ABSTRACT-SYNTAX.&Type + * } + * + * @param apdu Pointer to the buffer for encoding. + * @param list_element Pointer to the property data to be encoded. + * + * @return Bytes encoded or zero on error. + */ +int list_element_encode_service_request( + uint8_t *apdu, BACNET_LIST_ELEMENT_DATA *list_element) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (list_element) { + len = encode_context_object_id( + apdu, 0, list_element->object_type, list_element->object_instance); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_context_enumerated(apdu, 1, list_element->object_property); + apdu_len += len; + if (apdu) { + apdu += len; + } + if (list_element->array_index != BACNET_ARRAY_ALL) { + /* optional array index */ + len = encode_context_unsigned(apdu, 2, list_element->array_index); + apdu_len += len; + if (apdu) { + apdu += len; + } + } + len = encode_opening_tag(apdu, 3); + apdu_len += len; + if (apdu) { + apdu += len; + } + for (len = 0; len < list_element->application_data_len; len++) { + if (apdu) { + *apdu = list_element->application_data[len]; + apdu++; + } + } + apdu_len += len; + len = encode_closing_tag(apdu, 3); + apdu_len += len; + } + + return apdu_len; +} + +/** + * @brief Decode the Add/Remove ListElement service request only + * @param apdu Pointer to the buffer for decoding. + * @param apdu_len Count of valid bytes in the buffer. + * @param list_element Pointer to the property decoded data to be stored + * + * @return Bytes decoded or BACNET_STATUS_REJECT on error. + */ +int list_element_decode_service_request( + uint8_t *apdu, unsigned apdu_len, BACNET_LIST_ELEMENT_DATA *list_element) +{ + unsigned len = 0, application_data_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + BACNET_OBJECT_TYPE object_type = OBJECT_NONE; + uint32_t object_instance = 0; + uint32_t property = 0; + BACNET_UNSIGNED_INTEGER unsigned_value = 0; + + + /* Must have at least 2 tags, an object id and a property identifier + * of at least 1 byte in length to have any chance of parsing */ + if (apdu_len < 7) { + if (list_element) { + list_element->error_code = + ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + } + return BACNET_STATUS_REJECT; + } + + /* Tag 0: Object ID */ + if (!decode_is_context_tag(&apdu[len++], 0)) { + if (list_element) { + list_element->error_code = ERROR_CODE_REJECT_INVALID_TAG; + } + return BACNET_STATUS_REJECT; + } + len += decode_object_id(&apdu[len], &object_type, &object_instance); + if (list_element) { + list_element->object_type = object_type; + } + /* Tag 1: Property ID */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, &len_value_type); + if (tag_number != 1) { + if (list_element) { + list_element->error_code = ERROR_CODE_REJECT_INVALID_TAG; + } + return BACNET_STATUS_REJECT; + } + if (len >= apdu_len) { + if (list_element) { + list_element->error_code = + ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + } + return BACNET_STATUS_REJECT; + } + len += decode_enumerated(&apdu[len], len_value_type, &property); + if (list_element) { + list_element->object_property = (BACNET_PROPERTY_ID)property; + } + if (len >= apdu_len) { + if (list_element) { + list_element->error_code = + ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + } + return BACNET_STATUS_REJECT; + } + /* Tag 2: Optional Array Index */ + if (decode_is_opening_tag_number(&apdu[len], 3)) { + /* this is tag 3, therefore, optional tag 2 is not present */ + if (list_element) { + list_element->array_index = BACNET_ARRAY_ALL; + } + } else { + len += decode_tag_number_and_value( + &apdu[len], &tag_number, &len_value_type); + if ((tag_number == 2) && (len < apdu_len)) { + len += decode_unsigned(&apdu[len], len_value_type, &unsigned_value); + if (list_element) { + list_element->array_index = (BACNET_ARRAY_INDEX)unsigned_value; + } + } + } + if (len >= apdu_len) { + if (list_element) { + list_element->error_code = + ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + } + return BACNET_STATUS_REJECT; + } + /* Tag 3: opening context tag */ + if (decode_is_opening_tag_number(&apdu[len], 3)) { + /* an opening tag number of 3 is not extended so only one octet */ + len++; + /* don't decode the application tag number or its data here */ + if (list_element) { + list_element->application_data = &apdu[len]; + } + application_data_len = apdu_len - len - 1 /*closing tag */; + if (list_element) { + /* Just to ensure we do not create a wrapped over value here. */ + if (len < apdu_len) { + list_element->application_data_len = application_data_len; + } else { + list_element->application_data_len = 0; + } + } + len += application_data_len; + } else { + if (list_element) { + list_element->error_code = ERROR_CODE_REJECT_INVALID_TAG; + } + return BACNET_STATUS_REJECT; + } + if (len >= apdu_len) { + if (list_element) { + list_element->error_code = ERROR_CODE_REJECT_INVALID_TAG; + } + return BACNET_STATUS_REJECT; + } + if (decode_is_closing_tag_number(&apdu[len], 3)) { + /* a closing tag number of 3 is not extended so only one octet */ + len++; + } else { + if (list_element) { + list_element->error_code = ERROR_CODE_REJECT_INVALID_TAG; + } + return BACNET_STATUS_REJECT; + } + if (len < apdu_len) { + /* If something left over now, we have an invalid request */ + if (list_element) { + list_element->error_code = ERROR_CODE_REJECT_TOO_MANY_ARGUMENTS; + } + return BACNET_STATUS_REJECT; + } + + return (int)len; +} + +/** + * @brief Encode a AddListElement-Error or RemoveListElement-Error APDU + * AddListElement-Error ::= SEQUENCE { + * error-type [0] Error, + * first-failed-element-number [1] UNSIGNED + * } + * RemoveListElement-Error ::= SEQUENCE { + * error-type [0] Error, + * first-failed-element-number [1] UNSIGNED + * } + * @param apdu Pointer to the buffer for encoding. + * @param list_element Pointer to the property data to be encoded. + * @return Bytes encoded or zero on error. + */ +int list_element_error_ack_encode( + uint8_t *apdu, BACNET_LIST_ELEMENT_DATA *list_element) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + len = encode_opening_tag(apdu, 0); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_application_enumerated(apdu, list_element->error_class); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_application_enumerated(apdu, list_element->error_code); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag(apdu, 0); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_context_unsigned( + apdu, 1, list_element->first_failed_element_number); + apdu_len += len; + + return apdu_len; +} + +/** + * @brief Decoding for AddListElement or RemoveListElement Error Ack + * AddListElement-Error ::= SEQUENCE { + * error-type [0] Error, + * first-failed-element-number [1] UNSIGNED + * } + * RemoveListElement-Error ::= SEQUENCE { + * error-type [0] Error, + * first-failed-element-number [1] UNSIGNED + * } + * + * @param apdu Pointer to the buffer for decoding. + * @param apdu_size size of the buffer for decoding. + * @param list_element Pointer to the property data to be encoded. + * @return Bytes encoded or zero on error. + */ +int list_element_error_ack_decode( + uint8_t *apdu, uint16_t apdu_size, BACNET_LIST_ELEMENT_DATA *list_element) +{ + int len = 0, apdu_len = 0; + BACNET_ERROR_CLASS error_class = ERROR_CLASS_SERVICES; + BACNET_ERROR_CODE error_code = ERROR_CODE_SUCCESS; + BACNET_UNSIGNED_INTEGER first_failed_element_number = 0; + + if (!apdu) { + return 0; + } + if (list_element) { + list_element->first_failed_element_number = 0; + list_element->error_class = ERROR_CLASS_SERVICES; + list_element->error_code = ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE; + } + if (apdu_size < apdu_len) { + return 0; + } + /* Opening Context tag 0 - Error */ + if (decode_is_opening_tag_number(apdu, 0)) { + /* opening tag 0 is 1 byte */ + len = 1; + apdu_len += len; + apdu += len; + } else { + return 0; + } + if (apdu_size < apdu_len) { + return 0; + } + len = bacerror_decode_error_class_and_code( + apdu, apdu_size - apdu_len, &error_class, &error_code); + if (len > 0) { + if (list_element) { + list_element->error_class = error_class; + list_element->error_code = error_code; + } + apdu_len += len; + apdu += len; + } else { + return 0; + } + if (apdu_size < apdu_len) { + return 0; + } + /* Closing Context tag 0 - Error */ + if (decode_is_closing_tag_number(apdu, 0)) { + /* closing tag 0 is 1 byte */ + len = 1; + apdu_len += len; + apdu += len; + } else { + return 0; + } + if (apdu_size < apdu_len) { + return 0; + } + len = bacnet_unsigned_context_decode( + apdu, apdu_size - apdu_len, 1, &first_failed_element_number); + if (len > 0) { + if (list_element) { + list_element->first_failed_element_number = + first_failed_element_number; + } + apdu_len += len; + } else { + return 0; + } + + return apdu_len; +} diff --git a/src/bacnet/list_element.h b/src/bacnet/list_element.h new file mode 100644 index 00000000..846c35a9 --- /dev/null +++ b/src/bacnet/list_element.h @@ -0,0 +1,83 @@ +/** + * @file + * @brief API for AddListElement and RemoveListElement service codec + * @author Steve Karg + * @date December 2022 + * @section LICENSE + * + * Copyright (C) 2022 Steve Karg + * + * SPDX-License-Identifier: MIT + */ +#ifndef BACNET_LIST_ELEMENT_H +#define BACNET_LIST_ELEMENT_H + +#include +#include +#include "bacnet/bacnet_stack_exports.h" +#include "bacnet/bacenum.h" +#include "bacnet/bacdcode.h" +#include "bacnet/bacdef.h" + +/** + * AddListElement-Request ::= SEQUENCE { + * object-identifier [0] BACnetObjectIdentifier, + * property-identifier [1] BACnetPropertyIdentifier, + * property-array-index [2] Unsigned OPTIONAL, + * -- used only with array datatype + * list-of-elements [3] ABSTRACT-SYNTAX.&Type + * } + * RemoveListElement-Request ::= SEQUENCE { + * object-identifier [0] BACnetObjectIdentifier, + * property-identifier [1] BACnetPropertyIdentifier, + * property-array-index [2] Unsigned OPTIONAL, + * -- used only with array datatypes + * list-of-elements [3] ABSTRACT-SYNTAX.&Type + * } + */ + +typedef struct BACnet_List_Element_Data { + /* note: number type first to avoid enum cast warning on = { 0 } */ + uint32_t object_instance; + BACNET_OBJECT_TYPE object_type; + BACNET_PROPERTY_ID object_property; + BACNET_ARRAY_INDEX array_index; + uint8_t *application_data; + int application_data_len; + BACNET_UNSIGNED_INTEGER first_failed_element_number; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; +} BACNET_LIST_ELEMENT_DATA; + +/** + * @brief AddListElement or RemoveListElement from an object list property + * @ingroup ObjHelpers + * @param list_element [in] Pointer to the BACnet_List_Element_Data structure, + * which is packed with the information from the request. + * @return The length of the apdu encoded or #BACNET_STATUS_ERROR or + * #BACNET_STATUS_ABORT or #BACNET_STATUS_REJECT. + */ +typedef int (*list_element_function)(BACNET_LIST_ELEMENT_DATA *list_element); + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +BACNET_STACK_EXPORT +int list_element_encode_service_request( + uint8_t *apdu, BACNET_LIST_ELEMENT_DATA *list_element); +BACNET_STACK_EXPORT +int list_element_decode_service_request( + uint8_t *apdu, unsigned apdu_len, BACNET_LIST_ELEMENT_DATA *list_element); +BACNET_STACK_EXPORT +int list_element_error_ack_encode( + uint8_t *apdu, BACNET_LIST_ELEMENT_DATA *list_element); +BACNET_STACK_EXPORT +int list_element_error_ack_decode( + uint8_t *apdu, uint16_t apdu_size, BACNET_LIST_ELEMENT_DATA *list_element); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif \ No newline at end of file diff --git a/test/bacnet/bacapp/CMakeLists.txt b/test/bacnet/bacapp/CMakeLists.txt index 0023cae9..49012ddf 100644 --- a/test/bacnet/bacapp/CMakeLists.txt +++ b/test/bacnet/bacapp/CMakeLists.txt @@ -36,6 +36,8 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/bacapp.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdcode.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c diff --git a/test/bacnet/bacdest/CMakeLists.txt b/test/bacnet/bacdest/CMakeLists.txt new file mode 100644 index 00000000..655e170d --- /dev/null +++ b/test/bacnet/bacdest/CMakeLists.txt @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) + +get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME) +project(test_${basename} + VERSION 1.0.0 + LANGUAGES C) + + +string(REGEX REPLACE + "/test/bacnet/[a-zA-Z_/-]*$" + "/src" + SRC_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/bacnet/[a-zA-Z_/-]*$" + "/test" + TST_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +set(ZTST_DIR "${TST_DIR}/ztest/src") + +add_compile_definitions( + BIG_ENDIAN=0 + CONFIG_ZTEST=1 + BACAPP_ALL + ) + +include_directories( + ${SRC_DIR} + ${TST_DIR}/ztest/include + ) + +add_executable(${PROJECT_NAME} + # File(s) under test + ${SRC_DIR}/bacnet/bacdest.c + # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c + ${SRC_DIR}/bacnet/bacapp.c + ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdevobjpropref.c + ${SRC_DIR}/bacnet/bacerror.c + ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacreal.c + ${SRC_DIR}/bacnet/bacstr.c + ${SRC_DIR}/bacnet/bactext.c + ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/datetime.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/hostnport.c + ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/timesync.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c + # Test and test library files + ./src/main.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) diff --git a/test/bacnet/bacdest/src/main.c b/test/bacnet/bacdest/src/main.c new file mode 100644 index 00000000..168363cd --- /dev/null +++ b/test/bacnet/bacdest/src/main.c @@ -0,0 +1,160 @@ +/** + * @file + * @brief Unit test for BACnetDestination encode and decode + * @author Steve Karg + * @date December 2022 + * + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include +#include + +/** + * @addtogroup bacnet_tests + * @{ + */ + +/** + * @brief Test + */ +static void testBACnetRecipientData( + BACNET_RECIPIENT *data1, + BACNET_RECIPIENT *data2) +{ + unsigned i = 0; + + if (data1 && data2) { + zassert_equal(data1->tag, data2->tag, NULL); + if (data1->tag == BACNET_RECIPIENT_TAG_DEVICE) { + zassert_equal( + data1->type.device.type, data2->type.device.type, NULL); + zassert_equal( + data1->type.device.instance, + data2->type.device.instance, NULL); + } else if (data1->tag == BACNET_RECIPIENT_TAG_ADDRESS) { + zassert_equal( + data1->type.address.net, data2->type.address.net, NULL); + if (data1->type.address.net == BACNET_BROADCAST_NETWORK) { + zassert_equal( + data1->type.address.mac_len, + data2->type.address.mac_len, NULL); + } else if (data1->type.address.net) { + zassert_equal( + data1->type.address.len, + data2->type.address.len, NULL); + for (i = 0; i < data1->type.address.len; i++) { + zassert_equal( + data1->type.address.adr[i], + data2->type.address.adr[i], NULL); + } + } else { + zassert_equal( + data1->type.address.mac_len, + data2->type.address.mac_len, NULL); + for (i = 0; i < data1->type.address.mac_len; i++) { + zassert_equal( + data1->type.address.mac[i], + data2->type.address.mac[i], NULL); + } + } + } else { + zassert_true(data1->tag < BACNET_RECIPIENT_TAG_MAX, NULL); + } + } +} + +static void testBACnetDestination(void) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + BACNET_DESTINATION destination = { 0 }, test_destination = { 0 }; + int len = 0, apdu_len = 0, null_len = 0, test_len = 0; + + destination.Recipient.tag = BACNET_RECIPIENT_TAG_DEVICE; + null_len = bacnet_destination_encode(NULL, &destination); + apdu_len = bacnet_destination_encode(apdu, &destination); + zassert_equal(apdu_len, null_len, NULL); + test_len = bacnet_destination_decode(apdu, apdu_len, + &test_destination); + zassert_equal(apdu_len, test_len, NULL); + + destination.Recipient.tag = BACNET_RECIPIENT_TAG_ADDRESS; + null_len = bacnet_destination_encode(NULL, &destination); + apdu_len = bacnet_destination_encode(apdu, &destination); + zassert_equal(apdu_len, null_len, NULL); + test_len = bacnet_destination_decode(apdu, apdu_len, + &test_destination); + zassert_equal(test_len, apdu_len, NULL); + + null_len = bacnet_destination_encode(NULL, &destination); + apdu_len = bacnet_destination_encode(apdu, &destination); + zassert_equal(apdu_len, null_len, NULL); + test_len = bacnet_destination_decode(apdu, apdu_len, + &test_destination); + zassert_equal(test_len, apdu_len, NULL); + + /* decoding, some negative tests */ + test_len = bacnet_destination_decode(NULL, apdu_len, + &test_destination); + zassert_equal(test_len, BACNET_STATUS_REJECT, NULL); + test_len = bacnet_destination_decode(apdu, 0, + &test_destination); + zassert_equal(test_len, BACNET_STATUS_REJECT, NULL); + test_len = bacnet_destination_decode(apdu, apdu_len, + NULL); + zassert_equal(test_len, BACNET_STATUS_REJECT, NULL); +} +/** + * @} + */ + +/** +* Unit Test for ASCII conversion +*/ +static void test_BACnetDestination_ASCII(void) +{ + BACNET_DESTINATION destination = { 0 }, test_destination = { 0 }; + int len = 0, test_len = 0; + const char *ascii = "(" + "ValidDays=[1,2,3,4,5,6,7];" + "FromTime=0:00:00.0;ToTime=23:59:59.99;" + "Recipient=Device(type=8,instance=4194303);" + "ProcessIdentifier=0;" + "ConfirmedNotify=false;" + "Transitions=[]" + ")"; + bool status = false; + char *test_ascii = NULL; + + bacnet_destination_default_init(&destination); + len = bacnet_destination_from_ascii(&test_destination, ascii); + zassert_true(len > 0, NULL); + status = bacnet_destination_same(&destination, &test_destination); + zassert_true(status, NULL); + /* get the length */ + len = bacnet_destination_to_ascii(&test_destination, NULL, 0); + if (len > 0) { + test_ascii = calloc(len, 1); + if (test_ascii) { + test_len = bacnet_destination_to_ascii( + &test_destination, test_ascii, len); + zassert_equal(len, test_len, NULL); + free(test_ascii); + } + } + +} + +void test_main(void) +{ + ztest_test_suite(bacnet_destination_tests, + ztest_unit_test(testBACnetDestination), + ztest_unit_test(test_BACnetDestination_ASCII) + ); + + ztest_run_test_suite(bacnet_destination_tests); +} diff --git a/test/bacnet/bacdevobjpropref/CMakeLists.txt b/test/bacnet/bacdevobjpropref/CMakeLists.txt index cf885b3c..27210d11 100644 --- a/test/bacnet/bacdevobjpropref/CMakeLists.txt +++ b/test/bacnet/bacdevobjpropref/CMakeLists.txt @@ -35,8 +35,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/bacdevobjpropref.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c ${SRC_DIR}/bacnet/bacstr.c diff --git a/test/bacnet/bactimevalue/CMakeLists.txt b/test/bacnet/bactimevalue/CMakeLists.txt index c8099baa..3d885daf 100644 --- a/test/bacnet/bactimevalue/CMakeLists.txt +++ b/test/bacnet/bactimevalue/CMakeLists.txt @@ -35,8 +35,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/bactimevalue.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/binding/address/CMakeLists.txt b/test/bacnet/basic/binding/address/CMakeLists.txt index 08d6b7ec..fefe1e7d 100644 --- a/test/bacnet/basic/binding/address/CMakeLists.txt +++ b/test/bacnet/basic/binding/address/CMakeLists.txt @@ -37,6 +37,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/acc/CMakeLists.txt b/test/bacnet/basic/object/acc/CMakeLists.txt index fa16aca9..5cd996e7 100644 --- a/test/bacnet/basic/object/acc/CMakeLists.txt +++ b/test/bacnet/basic/object/acc/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/acc.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/access_credential/CMakeLists.txt b/test/bacnet/basic/object/access_credential/CMakeLists.txt index 84789aa6..512a2921 100644 --- a/test/bacnet/basic/object/access_credential/CMakeLists.txt +++ b/test/bacnet/basic/object/access_credential/CMakeLists.txt @@ -36,8 +36,10 @@ add_executable(${PROJECT_NAME} # Support files and stubs (pathname alphabetical) ${SRC_DIR}/bacnet/assigned_access_rights.c ${SRC_DIR}/bacnet/authentication_factor.c + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/access_door/CMakeLists.txt b/test/bacnet/basic/object/access_door/CMakeLists.txt index e085dff2..6fcb65d5 100644 --- a/test/bacnet/basic/object/access_door/CMakeLists.txt +++ b/test/bacnet/basic/object/access_door/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/access_door.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/access_point/CMakeLists.txt b/test/bacnet/basic/object/access_point/CMakeLists.txt index c63f13a5..acf201a4 100644 --- a/test/bacnet/basic/object/access_point/CMakeLists.txt +++ b/test/bacnet/basic/object/access_point/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/access_point.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/access_rights/CMakeLists.txt b/test/bacnet/basic/object/access_rights/CMakeLists.txt index 2198bebc..326ee99d 100644 --- a/test/bacnet/basic/object/access_rights/CMakeLists.txt +++ b/test/bacnet/basic/object/access_rights/CMakeLists.txt @@ -35,8 +35,10 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/basic/object/access_rights.c # Support files and stubs (pathname alphabetical) ${SRC_DIR}/bacnet/access_rule.c + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/access_user/CMakeLists.txt b/test/bacnet/basic/object/access_user/CMakeLists.txt index e859f8b2..ca90257f 100644 --- a/test/bacnet/basic/object/access_user/CMakeLists.txt +++ b/test/bacnet/basic/object/access_user/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/access_user.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/access_zone/CMakeLists.txt b/test/bacnet/basic/object/access_zone/CMakeLists.txt index 68af71a6..d72d98a3 100644 --- a/test/bacnet/basic/object/access_zone/CMakeLists.txt +++ b/test/bacnet/basic/object/access_zone/CMakeLists.txt @@ -36,8 +36,10 @@ add_executable(${PROJECT_NAME} # Support files and stubs (pathname alphabetical) ${SRC_DIR}/bacnet/authentication_factor.c ${SRC_DIR}/bacnet/assigned_access_rights.c + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/ai/CMakeLists.txt b/test/bacnet/basic/object/ai/CMakeLists.txt index 9178144a..e4689519 100644 --- a/test/bacnet/basic/object/ai/CMakeLists.txt +++ b/test/bacnet/basic/object/ai/CMakeLists.txt @@ -34,7 +34,9 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/ai.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c ${SRC_DIR}/bacnet/bacstr.c diff --git a/test/bacnet/basic/object/ao/CMakeLists.txt b/test/bacnet/basic/object/ao/CMakeLists.txt index f8895daa..46fadcd8 100644 --- a/test/bacnet/basic/object/ao/CMakeLists.txt +++ b/test/bacnet/basic/object/ao/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/ao.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/av/CMakeLists.txt b/test/bacnet/basic/object/av/CMakeLists.txt index dc2cdc23..a5c8ba19 100644 --- a/test/bacnet/basic/object/av/CMakeLists.txt +++ b/test/bacnet/basic/object/av/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/av.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/bacfile/CMakeLists.txt b/test/bacnet/basic/object/bacfile/CMakeLists.txt index f5be9340..4a3126f1 100644 --- a/test/bacnet/basic/object/bacfile/CMakeLists.txt +++ b/test/bacnet/basic/object/bacfile/CMakeLists.txt @@ -37,8 +37,10 @@ add_executable(${PROJECT_NAME} # Support files and stubs (pathname alphabetical) ${SRC_DIR}/bacnet/arf.c ${SRC_DIR}/bacnet/awf.c + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/bi/CMakeLists.txt b/test/bacnet/basic/object/bi/CMakeLists.txt index 27a72a57..7ff88570 100644 --- a/test/bacnet/basic/object/bi/CMakeLists.txt +++ b/test/bacnet/basic/object/bi/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/bi.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/bo/CMakeLists.txt b/test/bacnet/basic/object/bo/CMakeLists.txt index 5d5ef49e..9bf65a78 100644 --- a/test/bacnet/basic/object/bo/CMakeLists.txt +++ b/test/bacnet/basic/object/bo/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/bo.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/bv/CMakeLists.txt b/test/bacnet/basic/object/bv/CMakeLists.txt index 13922a45..280fd248 100644 --- a/test/bacnet/basic/object/bv/CMakeLists.txt +++ b/test/bacnet/basic/object/bv/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/bv.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/color_object/CMakeLists.txt b/test/bacnet/basic/object/color_object/CMakeLists.txt index fd864712..ac4815f9 100644 --- a/test/bacnet/basic/object/color_object/CMakeLists.txt +++ b/test/bacnet/basic/object/color_object/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/color_object.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/color_temperature/CMakeLists.txt b/test/bacnet/basic/object/color_temperature/CMakeLists.txt index 8ae88e12..951b951d 100644 --- a/test/bacnet/basic/object/color_temperature/CMakeLists.txt +++ b/test/bacnet/basic/object/color_temperature/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/color_temperature.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/command/CMakeLists.txt b/test/bacnet/basic/object/command/CMakeLists.txt index 3a2e8e7a..c5a3e366 100644 --- a/test/bacnet/basic/object/command/CMakeLists.txt +++ b/test/bacnet/basic/object/command/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/command.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/credential_data_input/CMakeLists.txt b/test/bacnet/basic/object/credential_data_input/CMakeLists.txt index 797f52a1..622e256e 100644 --- a/test/bacnet/basic/object/credential_data_input/CMakeLists.txt +++ b/test/bacnet/basic/object/credential_data_input/CMakeLists.txt @@ -36,8 +36,10 @@ add_executable(${PROJECT_NAME} # Support files and stubs (pathname alphabetical) ${SRC_DIR}/bacnet/authentication_factor.c ${SRC_DIR}/bacnet/authentication_factor_format.c + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/device/CMakeLists.txt b/test/bacnet/basic/object/device/CMakeLists.txt index cb539e6c..c6bb0b64 100644 --- a/test/bacnet/basic/object/device/CMakeLists.txt +++ b/test/bacnet/basic/object/device/CMakeLists.txt @@ -38,6 +38,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacerror.c ${SRC_DIR}/bacnet/bacint.c diff --git a/test/bacnet/basic/object/lc/CMakeLists.txt b/test/bacnet/basic/object/lc/CMakeLists.txt index 2e906ca2..5fde0e75 100644 --- a/test/bacnet/basic/object/lc/CMakeLists.txt +++ b/test/bacnet/basic/object/lc/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/lc.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacerror.c ${SRC_DIR}/bacnet/bacint.c diff --git a/test/bacnet/basic/object/lo/CMakeLists.txt b/test/bacnet/basic/object/lo/CMakeLists.txt index ddb13fc8..cbdd938a 100644 --- a/test/bacnet/basic/object/lo/CMakeLists.txt +++ b/test/bacnet/basic/object/lo/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/lo.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacerror.c ${SRC_DIR}/bacnet/bacint.c diff --git a/test/bacnet/basic/object/lsp/CMakeLists.txt b/test/bacnet/basic/object/lsp/CMakeLists.txt index c18e2a17..82eeffa9 100644 --- a/test/bacnet/basic/object/lsp/CMakeLists.txt +++ b/test/bacnet/basic/object/lsp/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/lsp.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacerror.c ${SRC_DIR}/bacnet/bacint.c diff --git a/test/bacnet/basic/object/ms-input/CMakeLists.txt b/test/bacnet/basic/object/ms-input/CMakeLists.txt index 52da6c89..5a61d9c8 100644 --- a/test/bacnet/basic/object/ms-input/CMakeLists.txt +++ b/test/bacnet/basic/object/ms-input/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/ms-input.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/mso/CMakeLists.txt b/test/bacnet/basic/object/mso/CMakeLists.txt index d34c5eb2..8c68da66 100644 --- a/test/bacnet/basic/object/mso/CMakeLists.txt +++ b/test/bacnet/basic/object/mso/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/mso.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/msv/CMakeLists.txt b/test/bacnet/basic/object/msv/CMakeLists.txt index e44eaa89..4db9f4a7 100644 --- a/test/bacnet/basic/object/msv/CMakeLists.txt +++ b/test/bacnet/basic/object/msv/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/msv.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/nc/CMakeLists.txt b/test/bacnet/basic/object/nc/CMakeLists.txt new file mode 100644 index 00000000..2cdfb7fc --- /dev/null +++ b/test/bacnet/basic/object/nc/CMakeLists.txt @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) + +get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME) +project(test_${basename} + VERSION 1.0.0 + LANGUAGES C) + + +string(REGEX REPLACE + "/test/bacnet/[a-zA-Z_/-]*$" + "/src" + SRC_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/bacnet/[a-zA-Z_/-]*$" + "/test" + TST_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +set(ZTST_DIR "${TST_DIR}/ztest/src") + +add_compile_definitions( + BIG_ENDIAN=0 + INTRINSIC_REPORTING=1 + CONFIG_ZTEST=1 + ) + +include_directories( + ${SRC_DIR} + ${TST_DIR}/ztest/include + ) + +add_executable(${PROJECT_NAME} + # File(s) under test + ${SRC_DIR}/bacnet/basic/object/nc.c + # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c + ${SRC_DIR}/bacnet/bacapp.c + ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c + ${SRC_DIR}/bacnet/bacdevobjpropref.c + ${SRC_DIR}/bacnet/bacerror.c + ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacreal.c + ${SRC_DIR}/bacnet/bacstr.c + ${SRC_DIR}/bacnet/bactext.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/basic/binding/address.c + ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/basic/sys/debug.c + ${SRC_DIR}/bacnet/datetime.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/hostnport.c + ${SRC_DIR}/bacnet/list_element.c + ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/wp.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/dailyschedule.c + # Test and test library files + ./stubs.c + ./src/main.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) diff --git a/test/bacnet/basic/object/nc/src/main.c b/test/bacnet/basic/object/nc/src/main.c new file mode 100644 index 00000000..d2938772 --- /dev/null +++ b/test/bacnet/basic/object/nc/src/main.c @@ -0,0 +1,61 @@ +/** + * @file + * @brief Unit test for object + * @author Steve Karg + * @date December 2022 + * + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include + +/** + * @addtogroup bacnet_tests + * @{ + */ + +/** + * @brief Test + */ +static void test_Notification_Class(void) +{ + BACNET_READ_PROPERTY_DATA rpdata; + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + BACNET_OBJECT_TYPE decoded_type = 0; + uint32_t decoded_instance = 0; + + Notification_Class_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_NOTIFICATION_CLASS; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Notification_Class_Read_Property(&rpdata); + zassert_not_equal(len, 0, NULL); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + zassert_equal(tag_number, BACNET_APPLICATION_TAG_OBJECT_ID, NULL); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + zassert_equal(decoded_type, rpdata.object_type, NULL); + zassert_equal(decoded_instance, rpdata.object_instance, NULL); + + return; +} +/** + * @} + */ + + +void test_main(void) +{ + ztest_test_suite(notification_class_tests, + ztest_unit_test(test_Notification_Class) + ); + + ztest_run_test_suite(notification_class_tests); +} diff --git a/test/bacnet/basic/object/nc/stubs.c b/test/bacnet/basic/object/nc/stubs.c new file mode 100644 index 00000000..319f6112 --- /dev/null +++ b/test/bacnet/basic/object/nc/stubs.c @@ -0,0 +1,55 @@ +/** + * @file + * @brief Stub functions for unit test of a BACnet object + * @author Steve Karg + * @date December 2022 + * + * SPDX-License-Identifier: MIT + */ +#include +#include +#include "bacnet/datetime.h" +#include "bacnet/bacdef.h" +#include "bacnet/npdu.h" +#include "bacnet/basic/object/nc.h" + +uint32_t Device_Object_Instance_Number(void) +{ + return 0; +} + +int Send_UEvent_Notify( + uint8_t *buffer, BACNET_EVENT_NOTIFICATION_DATA *data, BACNET_ADDRESS *dest) +{ + (void)buffer; + (void)data; + (void)dest; + return 0; +} + +uint8_t Send_CEvent_Notify( + uint32_t device_id, BACNET_EVENT_NOTIFICATION_DATA *data) +{ + (void)device_id; + (void)data; + return 0; +} + +void Send_WhoIs(int32_t low_limit, int32_t high_limit) +{ + (void)low_limit; + (void)high_limit; +} + +bool datetime_local( + BACNET_DATE * bdate, + BACNET_TIME * btime, + int16_t * utc_offset_minutes, + bool * dst_active) +{ + (void)bdate; + (void)btime; + (void)utc_offset_minutes; + (void)dst_active; + return true; +} diff --git a/test/bacnet/basic/object/netport/CMakeLists.txt b/test/bacnet/basic/object/netport/CMakeLists.txt index f86c9c37..ff5eaaa9 100644 --- a/test/bacnet/basic/object/netport/CMakeLists.txt +++ b/test/bacnet/basic/object/netport/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/netport.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacerror.c ${SRC_DIR}/bacnet/bacint.c diff --git a/test/bacnet/basic/object/osv/CMakeLists.txt b/test/bacnet/basic/object/osv/CMakeLists.txt index a44b2909..b37a0f5c 100644 --- a/test/bacnet/basic/object/osv/CMakeLists.txt +++ b/test/bacnet/basic/object/osv/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/osv.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/piv/CMakeLists.txt b/test/bacnet/basic/object/piv/CMakeLists.txt index 26307ac3..2c16d761 100644 --- a/test/bacnet/basic/object/piv/CMakeLists.txt +++ b/test/bacnet/basic/object/piv/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/piv.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/basic/object/schedule/CMakeLists.txt b/test/bacnet/basic/object/schedule/CMakeLists.txt index 1059d26f..1d03f930 100644 --- a/test/bacnet/basic/object/schedule/CMakeLists.txt +++ b/test/bacnet/basic/object/schedule/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/basic/object/schedule.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/cov/CMakeLists.txt b/test/bacnet/cov/CMakeLists.txt index f6052ca1..4237d828 100644 --- a/test/bacnet/cov/CMakeLists.txt +++ b/test/bacnet/cov/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/cov.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/getalarm/CMakeLists.txt b/test/bacnet/getalarm/CMakeLists.txt index 0d1a6a90..8f2eb18b 100644 --- a/test/bacnet/getalarm/CMakeLists.txt +++ b/test/bacnet/getalarm/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/get_alarm_sum.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/getevent/CMakeLists.txt b/test/bacnet/getevent/CMakeLists.txt index 4495edbf..4c5a2c05 100644 --- a/test/bacnet/getevent/CMakeLists.txt +++ b/test/bacnet/getevent/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/getevent.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/list_element/CMakeLists.txt b/test/bacnet/list_element/CMakeLists.txt new file mode 100644 index 00000000..52dd322c --- /dev/null +++ b/test/bacnet/list_element/CMakeLists.txt @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) + +get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME) +project(test_${basename} + VERSION 1.0.0 + LANGUAGES C) + + +string(REGEX REPLACE + "/test/bacnet/[a-zA-Z_/-]*$" + "/src" + SRC_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/bacnet/[a-zA-Z_/-]*$" + "/test" + TST_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +set(ZTST_DIR "${TST_DIR}/ztest/src") + +add_compile_definitions( + BIG_ENDIAN=0 + CONFIG_ZTEST=1 + ) + +include_directories( + ${SRC_DIR} + ${TST_DIR}/ztest/include + ) + +add_executable(${PROJECT_NAME} + # File(s) under test + ${SRC_DIR}/bacnet/list_element.c + # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c + ${SRC_DIR}/bacnet/bacapp.c + ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c + ${SRC_DIR}/bacnet/bacdevobjpropref.c + ${SRC_DIR}/bacnet/bacerror.c + ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacreal.c + ${SRC_DIR}/bacnet/bacstr.c + ${SRC_DIR}/bacnet/bactext.c + ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/datetime.c + ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/hostnport.c + ${SRC_DIR}/bacnet/lighting.c + ${SRC_DIR}/bacnet/timestamp.c + ${SRC_DIR}/bacnet/memcopy.c + ${SRC_DIR}/bacnet/weeklyschedule.c + ${SRC_DIR}/bacnet/bactimevalue.c + ${SRC_DIR}/bacnet/dailyschedule.c + # Test and test library files + ./src/main.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) diff --git a/test/bacnet/list_element/src/main.c b/test/bacnet/list_element/src/main.c new file mode 100644 index 00000000..c330b1e2 --- /dev/null +++ b/test/bacnet/list_element/src/main.c @@ -0,0 +1,89 @@ +/** + * @file + * @brief Unit test for list-element services encode and decode + * @author Steve Karg + * @date December 2022 + * + * SPDX-License-Identifier: MIT + */ +#include +#include +#include + +static void test_ListElement(void) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + BACNET_LIST_ELEMENT_DATA list_element = { 0 }, test_list_element = { 0 }; + int len = 0, apdu_len = 0, null_len = 0, test_len = 0; + uint8_t application_data[MAX_APDU] = { 0 }, *test_application_data = NULL; + int application_data_len = 0, test_application_data_len = 0; + unsigned i = 0; + BACNET_DESTINATION destination[5] = { 0 }, test_destination[5] = { 0 }; + const int destination_size = sizeof(destination) / sizeof(destination[0]); + + list_element.application_data = NULL; + list_element.application_data_len = 0; + null_len = list_element_encode_service_request(NULL, &list_element); + apdu_len = list_element_encode_service_request(apdu, &list_element); + zassert_equal(apdu_len, null_len, NULL); + test_len = + list_element_decode_service_request(apdu, apdu_len, &test_list_element); + zassert_equal(apdu_len, test_len, NULL); + /* fill application_data with RecipientList default data */ + list_element.array_index = BACNET_ARRAY_ALL; + for (i = 0; i < destination_size; i++) { + bacnet_destination_default_init(&destination[i]); + application_data_len += bacnet_destination_encode( + &application_data[application_data_len], &destination[i]); + } + list_element.application_data = application_data; + list_element.application_data_len = application_data_len; + apdu_len = list_element_encode_service_request(apdu, &list_element); + test_len = + list_element_decode_service_request(apdu, apdu_len, &test_list_element); + zassert_equal(apdu_len, test_len, NULL); + zassert_equal(test_list_element.array_index, BACNET_ARRAY_ALL, NULL); + test_application_data = list_element.application_data; + test_application_data_len = list_element.application_data_len; + for (i = 0; i < destination_size; i++) { + test_len = bacnet_destination_decode(test_application_data, + test_application_data_len, &test_destination[i]); + zassert_true( + bacnet_destination_same(&destination[i], &test_destination[i]), + NULL); + zassert_not_equal(test_len, BACNET_STATUS_REJECT, NULL); + test_application_data_len -= test_len; + test_application_data += test_len; + } + zassert_equal(test_application_data_len, 0, NULL); +} + +static void test_ListElementError(void) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + BACNET_LIST_ELEMENT_DATA list_element = { 0 }, test_list_element = { 0 }; + int len = 0, apdu_len = 0, null_len = 0, test_len = 0; + + list_element.error_class = ERROR_CLASS_SERVICES; + list_element.error_code = ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE; + list_element.first_failed_element_number = 0; + null_len = list_element_error_ack_encode(NULL, &list_element); + apdu_len = list_element_error_ack_encode(apdu, &list_element); + zassert_equal(apdu_len, null_len, NULL); + test_len = + list_element_error_ack_decode(apdu, apdu_len, &test_list_element); + zassert_equal(apdu_len, test_len, NULL); + zassert_equal( + test_list_element.error_class, list_element.error_class, NULL); + zassert_equal(test_list_element.error_code, list_element.error_code, NULL); + zassert_equal(test_list_element.first_failed_element_number, + list_element.first_failed_element_number, NULL); +} + +void test_main(void) +{ + ztest_test_suite(list_element_tests, ztest_unit_test(test_ListElement), + ztest_unit_test(test_ListElementError)); + + ztest_run_test_suite(list_element_tests); +} diff --git a/test/bacnet/lso/CMakeLists.txt b/test/bacnet/lso/CMakeLists.txt index 00f18150..c8549d65 100644 --- a/test/bacnet/lso/CMakeLists.txt +++ b/test/bacnet/lso/CMakeLists.txt @@ -34,8 +34,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/lso.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacerror.c ${SRC_DIR}/bacnet/bacint.c diff --git a/test/bacnet/ptransfer/CMakeLists.txt b/test/bacnet/ptransfer/CMakeLists.txt index 489afaef..412d9b16 100644 --- a/test/bacnet/ptransfer/CMakeLists.txt +++ b/test/bacnet/ptransfer/CMakeLists.txt @@ -35,8 +35,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/ptransfer.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/test/bacnet/rpm/CMakeLists.txt b/test/bacnet/rpm/CMakeLists.txt index 17f819f7..01a717a7 100644 --- a/test/bacnet/rpm/CMakeLists.txt +++ b/test/bacnet/rpm/CMakeLists.txt @@ -35,8 +35,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/rpm.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacerror.c ${SRC_DIR}/bacnet/bacint.c diff --git a/test/bacnet/timesync/CMakeLists.txt b/test/bacnet/timesync/CMakeLists.txt index 98f60517..fcc09f27 100644 --- a/test/bacnet/timesync/CMakeLists.txt +++ b/test/bacnet/timesync/CMakeLists.txt @@ -35,8 +35,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/timesync.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacerror.c ${SRC_DIR}/bacnet/bacint.c diff --git a/test/bacnet/weeklyschedule/CMakeLists.txt b/test/bacnet/weeklyschedule/CMakeLists.txt index 4f5f3779..a52d740f 100644 --- a/test/bacnet/weeklyschedule/CMakeLists.txt +++ b/test/bacnet/weeklyschedule/CMakeLists.txt @@ -37,10 +37,12 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/bactimevalue.c ${SRC_DIR}/bacnet/dailyschedule.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c - ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c + ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c ${SRC_DIR}/bacnet/bacstr.c ${SRC_DIR}/bacnet/bactext.c diff --git a/test/bacnet/wp/CMakeLists.txt b/test/bacnet/wp/CMakeLists.txt index 2c2e0848..74fee63d 100644 --- a/test/bacnet/wp/CMakeLists.txt +++ b/test/bacnet/wp/CMakeLists.txt @@ -35,8 +35,10 @@ add_executable(${PROJECT_NAME} # File(s) under test ${SRC_DIR}/bacnet/wp.c # Support files and stubs (pathname alphabetical) + ${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacapp.c ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacdest.c ${SRC_DIR}/bacnet/bacdevobjpropref.c ${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacreal.c diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 56e27455..aedded9b 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -65,6 +65,8 @@ set(BACNETSTACK_SRCS ${BACNETSTACK_SRC}/bacnet/bacdcode.c ${BACNETSTACK_SRC}/bacnet/bacdcode.h ${BACNETSTACK_SRC}/bacnet/bacdef.h + ${BACNETSTACK_SRC}/bacnet/bacdest.c + ${BACNETSTACK_SRC}/bacnet/bacdest.h ${BACNETSTACK_SRC}/bacnet/bacdevobjpropref.c ${BACNETSTACK_SRC}/bacnet/bacdevobjpropref.h ${BACNETSTACK_SRC}/bacnet/bacenum.h @@ -191,6 +193,8 @@ set(BACNETSTACK_SRCS ${BACNETSTACK_SRC}/bacnet/basic/service/s_iam.h ${BACNETSTACK_SRC}/bacnet/basic/service/s_ihave.c ${BACNETSTACK_SRC}/bacnet/basic/service/s_ihave.h + ${BACNETSTACK_SRC}/bacnet/basic/service/s_list_element.c + ${BACNETSTACK_SRC}/bacnet/basic/service/s_list_element.h ${BACNETSTACK_SRC}/bacnet/basic/service/s_lso.h ${BACNETSTACK_SRC}/bacnet/basic/service/s_rd.h ${BACNETSTACK_SRC}/bacnet/basic/service/s_readrange.h @@ -273,6 +277,8 @@ set(BACNETSTACK_SRCS ${BACNETSTACK_SRC}/bacnet/indtext.h ${BACNETSTACK_SRC}/bacnet/lighting.c ${BACNETSTACK_SRC}/bacnet/lighting.h + ${BACNETSTACK_SRC}/bacnet/list_element.c + ${BACNETSTACK_SRC}/bacnet/list_element.h ${BACNETSTACK_SRC}/bacnet/lso.c ${BACNETSTACK_SRC}/bacnet/lso.h ${BACNETSTACK_SRC}/bacnet/memcopy.c @@ -362,6 +368,8 @@ set(BACNETSTACK_BASIC_SRCS ${BACNETSTACK_SRC}/bacnet/basic/service/h_get_alarm_sum.c ${BACNETSTACK_SRC}/bacnet/basic/service/h_getevent_a.c ${BACNETSTACK_SRC}/bacnet/basic/service/h_getevent.c + ${BACNETSTACK_SRC}/bacnet/basic/service/h_list_element.c + ${BACNETSTACK_SRC}/bacnet/basic/service/h_list_element.h ${BACNETSTACK_SRC}/bacnet/basic/service/h_lso.c ${BACNETSTACK_SRC}/bacnet/basic/service/h_rr_a.c ${BACNETSTACK_SRC}/bacnet/basic/service/h_rr.c diff --git a/zephyr/tests/bacnet/bacapp/CMakeLists.txt b/zephyr/tests/bacnet/bacapp/CMakeLists.txt index f315c764..b21608fd 100644 --- a/zephyr/tests/bacnet/bacapp/CMakeLists.txt +++ b/zephyr/tests/bacnet/bacapp/CMakeLists.txt @@ -30,7 +30,9 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_SRC ${BACNET_SRC_PATH} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/acc/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/acc/CMakeLists.txt index 40eac786..04ee0f61 100644 --- a/zephyr/tests/bacnet/basic/object/acc/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/acc/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/access_credential/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/access_credential/CMakeLists.txt index 5ad0a321..47f0e25f 100644 --- a/zephyr/tests/bacnet/basic/object/access_credential/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/access_credential/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/access_door/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/access_door/CMakeLists.txt index f3b6da3e..da4f3355 100644 --- a/zephyr/tests/bacnet/basic/object/access_door/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/access_door/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/access_point/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/access_point/CMakeLists.txt index 40eac786..04ee0f61 100644 --- a/zephyr/tests/bacnet/basic/object/access_point/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/access_point/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/access_rights/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/access_rights/CMakeLists.txt index 5b740b8e..b21edaef 100644 --- a/zephyr/tests/bacnet/basic/object/access_rights/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/access_rights/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/access_user/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/access_user/CMakeLists.txt index f3b6da3e..da4f3355 100644 --- a/zephyr/tests/bacnet/basic/object/access_user/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/access_user/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/access_zone/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/access_zone/CMakeLists.txt index f3b6da3e..da4f3355 100644 --- a/zephyr/tests/bacnet/basic/object/access_zone/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/access_zone/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/ai/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/ai/CMakeLists.txt index 8002e2a9..e1ad53a1 100644 --- a/zephyr/tests/bacnet/basic/object/ai/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/ai/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/ao/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/ao/CMakeLists.txt index f3b6da3e..da4f3355 100644 --- a/zephyr/tests/bacnet/basic/object/ao/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/ao/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/av/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/av/CMakeLists.txt index f3b6da3e..da4f3355 100644 --- a/zephyr/tests/bacnet/basic/object/av/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/av/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/bi/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/bi/CMakeLists.txt index f3b6da3e..da4f3355 100644 --- a/zephyr/tests/bacnet/basic/object/bi/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/bi/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/bo/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/bo/CMakeLists.txt index 6a90eac6..33a779e7 100644 --- a/zephyr/tests/bacnet/basic/object/bo/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/bo/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/bv/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/bv/CMakeLists.txt index f3b6da3e..da4f3355 100644 --- a/zephyr/tests/bacnet/basic/object/bv/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/bv/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/color_object/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/color_object/CMakeLists.txt index 74560010..50b92210 100644 --- a/zephyr/tests/bacnet/basic/object/color_object/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/color_object/CMakeLists.txt @@ -33,8 +33,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/color_temperature/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/color_temperature/CMakeLists.txt index 57f1e4fd..78d23cad 100644 --- a/zephyr/tests/bacnet/basic/object/color_temperature/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/color_temperature/CMakeLists.txt @@ -33,8 +33,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/command/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/command/CMakeLists.txt index f3b6da3e..da4f3355 100644 --- a/zephyr/tests/bacnet/basic/object/command/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/command/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/credential_data_input/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/credential_data_input/CMakeLists.txt index a474142e..54f55318 100644 --- a/zephyr/tests/bacnet/basic/object/credential_data_input/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/credential_data_input/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/device/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/device/CMakeLists.txt index 6670be12..afec99ce 100644 --- a/zephyr/tests/bacnet/basic/object/device/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/device/CMakeLists.txt @@ -33,8 +33,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/lc/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/lc/CMakeLists.txt index 254cf0e1..75620c82 100644 --- a/zephyr/tests/bacnet/basic/object/lc/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/lc/CMakeLists.txt @@ -33,8 +33,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/lo/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/lo/CMakeLists.txt index f3b6da3e..da4f3355 100644 --- a/zephyr/tests/bacnet/basic/object/lo/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/lo/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/lsp/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/lsp/CMakeLists.txt index f3b6da3e..da4f3355 100644 --- a/zephyr/tests/bacnet/basic/object/lsp/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/lsp/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/ms-input/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/ms-input/CMakeLists.txt index 82147662..f1ca535b 100644 --- a/zephyr/tests/bacnet/basic/object/ms-input/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/ms-input/CMakeLists.txt @@ -33,8 +33,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/mso/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/mso/CMakeLists.txt index f3b6da3e..da4f3355 100644 --- a/zephyr/tests/bacnet/basic/object/mso/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/mso/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/msv/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/msv/CMakeLists.txt index adf57f88..17306090 100644 --- a/zephyr/tests/bacnet/basic/object/msv/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/msv/CMakeLists.txt @@ -31,8 +31,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/netport/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/netport/CMakeLists.txt index 52ac10ee..86b203d5 100644 --- a/zephyr/tests/bacnet/basic/object/netport/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/netport/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/objects/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/objects/CMakeLists.txt index c9bc6632..f070a2af 100644 --- a/zephyr/tests/bacnet/basic/object/objects/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/objects/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/osv/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/osv/CMakeLists.txt index f3b6da3e..da4f3355 100644 --- a/zephyr/tests/bacnet/basic/object/osv/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/osv/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/piv/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/piv/CMakeLists.txt index f3b6da3e..da4f3355 100644 --- a/zephyr/tests/bacnet/basic/object/piv/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/piv/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/basic/object/schedule/CMakeLists.txt b/zephyr/tests/bacnet/basic/object/schedule/CMakeLists.txt index 7ca9ebb4..8d71505c 100644 --- a/zephyr/tests/bacnet/basic/object/schedule/CMakeLists.txt +++ b/zephyr/tests/bacnet/basic/object/schedule/CMakeLists.txt @@ -32,8 +32,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_BASIC_SRC ${BACNET_OBJECT_SRC} PATH) get_filename_component(BACNET_SRC ${BACNET_BASIC_SRC} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/cov/CMakeLists.txt b/zephyr/tests/bacnet/cov/CMakeLists.txt index 3690d160..a22e1a30 100644 --- a/zephyr/tests/bacnet/cov/CMakeLists.txt +++ b/zephyr/tests/bacnet/cov/CMakeLists.txt @@ -30,8 +30,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_SRC ${BACNET_SRC_PATH} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/ptransfer/CMakeLists.txt b/zephyr/tests/bacnet/ptransfer/CMakeLists.txt index b85163ff..36db48d6 100644 --- a/zephyr/tests/bacnet/ptransfer/CMakeLists.txt +++ b/zephyr/tests/bacnet/ptransfer/CMakeLists.txt @@ -30,8 +30,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_SRC ${BACNET_SRC_PATH} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/rpm/CMakeLists.txt b/zephyr/tests/bacnet/rpm/CMakeLists.txt index 97461a65..239677b4 100644 --- a/zephyr/tests/bacnet/rpm/CMakeLists.txt +++ b/zephyr/tests/bacnet/rpm/CMakeLists.txt @@ -30,8 +30,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_SRC ${BACNET_SRC_PATH} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c diff --git a/zephyr/tests/bacnet/wp/CMakeLists.txt b/zephyr/tests/bacnet/wp/CMakeLists.txt index 88acc1df..95b33908 100644 --- a/zephyr/tests/bacnet/wp/CMakeLists.txt +++ b/zephyr/tests/bacnet/wp/CMakeLists.txt @@ -30,8 +30,10 @@ if(BOARD STREQUAL unit_testing) get_filename_component(BACNET_SRC ${BACNET_SRC_PATH} PATH) list(APPEND SOURCES + ${BACNET_SRC}/bacaddr.c ${BACNET_SRC}/bacapp.c ${BACNET_SRC}/bacdcode.c + ${BACNET_SRC}/bacdest.c ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c