Added AddListElement and RemoveListElement services (#418)

* 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 <ondra@ondrovo.com>
Co-authored-by: Steve Karg <skarg@users.sourceforge.net>
This commit is contained in:
Steve Karg
2023-04-16 14:12:11 -05:00
committed by GitHub
parent e517df0d47
commit ced9dff0f2
116 changed files with 4615 additions and 563 deletions
+3
View File
@@ -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
+14
View File
@@ -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})
+8
View File
@@ -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 $@
+9 -1
View File
@@ -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 $@
+39
View File
@@ -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
+480
View File
@@ -0,0 +1,480 @@
/**
* @file
* @brief Application to send a BACnet AddListElement service request
* @author Steve Karg <skarg@users.sourceforge.net>
* @date January 2023
*
* SPDX-License-Identifier: MIT
*/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#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;
}
+39
View File
@@ -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
+480
View File
@@ -0,0 +1,480 @@
/**
* @file
* @brief Application to send a BACnet RemoveListElement service request
* @author Steve Karg <skarg@users.sourceforge.net>
* @date January 2023
*
* SPDX-License-Identifier: MIT
*/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#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;
}
+1
View File
@@ -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 \
+3
View File
@@ -1127,6 +1127,9 @@
<file>
<name>$PROJ_DIR$\..\..\src\bacnet\bacdcode.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\..\src\bacnet\bacdest.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\..\src\bacnet\bacdevobjpropref.c</name>
</file>
+1
View File
@@ -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 \
+3
View File
@@ -1057,6 +1057,9 @@
<file>
<name>$PROJ_DIR$\..\..\src\bacnet\bacdcode.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\..\src\bacnet\bacdest.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\..\src\bacnet\bacdevobjpropref.c</name>
</file>
+1
View File
@@ -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 \
+3
View File
@@ -1060,6 +1060,9 @@
<file>
<name>$PROJ_DIR$\..\..\src\bacnet\bacdcode.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\..\src\bacnet\bacdest.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\..\src\bacnet\bacdevobjpropref.c</name>
</file>
+1
View File
@@ -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 \
+4
View File
@@ -776,6 +776,10 @@
<SubType>compile</SubType>
<Link>bacnet-stack\bacdcode.c</Link>
</Compile>
<Compile Include="..\..\src\bacnet\bacdest.c">
<SubType>compile</SubType>
<Link>bacnet-stack\bacdcode.c</Link>
</Compile>
<Compile Include="..\..\src\bacnet\bacdevobjpropref.c">
<SubType>compile</SubType>
<Link>bacnet-stack\bacdevobjpropref.c</Link>
+30 -4
View File
@@ -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;
+2
View File
@@ -29,6 +29,7 @@
#include <stdio.h>
#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 */
+993
View File
@@ -0,0 +1,993 @@
/**
* @file
* @brief BACnetDestination complex data type encode and decode
* @author Steve Karg <skarg@users.sourceforge.net>
* @date December 2022
* @section LICENSE
*
* Copyright (C) 2022 Steve Karg <skarg@users.sourceforge.net>
*
* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#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;
}
+104
View File
@@ -0,0 +1,104 @@
/**
* @file
* @brief API for BACnetDestination complex data type encode and decode
* @author Steve Karg <skarg@users.sourceforge.net>
* @date May 2022
* @section LICENSE
*
* Copyright (C) 2022 Steve Karg <skarg@users.sourceforge.net>
*
* SPDX-License-Identifier: MIT
*/
#ifndef BACNET_DESTINATION_H
#define BACNET_DESTINATION_H
#include <stdint.h>
#include <stdbool.h>
#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
@@ -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,
+129 -32
View File
@@ -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);
+11
View File
@@ -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 */
+399 -404
View File
@@ -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 */
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);
}
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);
if (apdu_len > apdu_max) {
/* Abort response */
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
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);
/* 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 = &notification->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 = &notification->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 = &notification->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 = &notification->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 = &notification->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 = &notification->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 = &notification->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 = &notification->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) */
+69 -109
View File
@@ -1,152 +1,112 @@
/**************************************************************************
*
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
*
* 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 <malornykrzysztof@gmail.com>
*
* 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
/* 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 */
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;
BACNET_DESTINATION
Recipient_List[NC_MAX_RECIPIENTS]; /* List of BACnetDestination */
} NOTIFICATION_CLASS_INFO;
/* Indicates whether the transaction has been confirmed */
typedef struct 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;
} ACKED_INFO;
/* Information needed to send AckNotification */
typedef struct Ack_Notification {
typedef struct Ack_Notification {
bool bSendAckNotify; /* true if need to send AckNotification */
uint8_t EventState;
} ACK_NOTIFICATION;
} 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 */
+211
View File
@@ -0,0 +1,211 @@
/**
* @file
* @brief AddListElement and RemoveListElement service application handlers
* @author Steve Karg <skarg@users.sourceforge.net>
* @date December 2022
* @section LICENSE
*
* Copyright (C) 2022 Steve Karg <skarg@users.sourceforge.net>
*
* SPDX-License-Identifier: MIT
*/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#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;
}
+43
View File
@@ -0,0 +1,43 @@
/**
* @file
* @brief API for AddListElement and RemoveListElement application handlers
* @author Steve Karg <skarg@users.sourceforge.net>
* @date December 2022
* @section LICENSE
*
* Copyright (C) 2022 Steve Karg <skarg@users.sourceforge.net>
*
* SPDX-License-Identifier: MIT
*/
#ifndef HANDLER_LIST_ELEMENT_H
#define HANDLER_LIST_ELEMENT_H
#include <stdint.h>
#include <stdbool.h>
#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
+282
View File
@@ -0,0 +1,282 @@
/**
* @file
* @brief AddListElement and RemoveListElement service initiation
* @author Steve Karg <skarg@users.sourceforge.net>
* @date January 2023
* @section LICENSE
*
* Copyright (C) 2023 Steve Karg <skarg@users.sourceforge.net>
*
* SPDX-License-Identifier: MIT
*/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#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);
}
+75
View File
@@ -0,0 +1,75 @@
/**
* @file
* @brief AddListElement and RemoveListElement service initiation
* @author Steve Karg <skarg@users.sourceforge.net>
* @date January 2023
* @section LICENSE
*
* Copyright (C) 2023 Steve Karg <skarg@users.sourceforge.net>
*
* SPDX-License-Identifier: MIT
*/
#ifndef SEND_LIST_ELEMENT_H
#define SEND_LIST_ELEMENT_H
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdint.h>
#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
+2
View File
@@ -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"
+363
View File
@@ -0,0 +1,363 @@
/**
* @file
* @brief AddListElement and RemoveListElement service encode and decode
* @author Steve Karg <skarg@users.sourceforge.net>
* @date December 2022
* @section LICENSE
*
* Copyright (C) 2022 Steve Karg <skarg@users.sourceforge.net>
*
* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#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;
}
+83
View File
@@ -0,0 +1,83 @@
/**
* @file
* @brief API for AddListElement and RemoveListElement service codec
* @author Steve Karg <skarg@users.sourceforge.net>
* @date December 2022
* @section LICENSE
*
* Copyright (C) 2022 Steve Karg <skarg@users.sourceforge.net>
*
* SPDX-License-Identifier: MIT
*/
#ifndef BACNET_LIST_ELEMENT_H
#define BACNET_LIST_ELEMENT_H
#include <stdint.h>
#include <stdbool.h>
#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
+2
View File
@@ -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
+62
View File
@@ -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
)
+160
View File
@@ -0,0 +1,160 @@
/**
* @file
* @brief Unit test for BACnetDestination encode and decode
* @author Steve Karg <skarg@users.sourceforge.net>
* @date December 2022
*
* SPDX-License-Identifier: MIT
*/
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <ztest.h>
#include <bacnet/bacdest.h>
/**
* @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);
}
@@ -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
+2
View File
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
)
+61
View File
@@ -0,0 +1,61 @@
/**
* @file
* @brief Unit test for object
* @author Steve Karg <skarg@users.sourceforge.net>
* @date December 2022
*
* SPDX-License-Identifier: MIT
*/
#include <ztest.h>
#include <bacnet/bactext.h>
#include <bacnet/rp.h>
#include <bacnet/basic/object/nc.h>
/**
* @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);
}
+55
View File
@@ -0,0 +1,55 @@
/**
* @file
* @brief Stub functions for unit test of a BACnet object
* @author Steve Karg <skarg@users.sourceforge.net>
* @date December 2022
*
* SPDX-License-Identifier: MIT
*/
#include <stdbool.h>
#include <stdint.h>
#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;
}
@@ -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
@@ -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
@@ -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
@@ -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
+2
View File
@@ -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
+2
View File
@@ -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
+2
View File
@@ -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
+62
View File
@@ -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
)
+89
View File
@@ -0,0 +1,89 @@
/**
* @file
* @brief Unit test for list-element services encode and decode
* @author Steve Karg <skarg@users.sourceforge.net>
* @date December 2022
*
* SPDX-License-Identifier: MIT
*/
#include <ztest.h>
#include <bacnet/bacdest.h>
#include <bacnet/list_element.h>
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);
}
+2
View File
@@ -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
+2
View File
@@ -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
+2
View File
@@ -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
+2
View File
@@ -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
+3 -1
View File
@@ -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
+2
View File
@@ -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
+8
View File
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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

Some files were not shown because too many files have changed in this diff Show More