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:
@@ -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
|
||||
|
||||
|
||||
@@ -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})
|
||||
|
||||
|
||||
@@ -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
@@ -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 $@
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 \
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
+397
-402
@@ -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.
|
||||
for (i = 0; i < MAX_NOTIFICATION_CLASSES; i++) {
|
||||
notification = &NC_Info[i];
|
||||
for (j = 0; j < NC_MAX_RECIPIENTS; j++) {
|
||||
destination = ¬ification->Recipient_List[j];
|
||||
recipient = &destination->Recipient;
|
||||
if (bacnet_recipient_device_valid(recipient)) {
|
||||
device_id = recipient->type.device.instance;
|
||||
if (!address_bind_request(device_id, &max_apdu, &src)) {
|
||||
/* Send who_ is request only when
|
||||
address of device is unknown. */
|
||||
Send_WhoIs(device_id, device_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
if (DeviceID < BACNET_MAX_INSTANCE) {
|
||||
/* note: BACNET_MAX_INSTANCE = wildcard, not valid */
|
||||
if (!address_bind_request(DeviceID, &max_apdu, &src)) {
|
||||
Send_WhoIs(DeviceID, DeviceID);
|
||||
int Notification_Class_Add_List_Element(
|
||||
BACNET_LIST_ELEMENT_DATA * list_element)
|
||||
{
|
||||
NOTIFICATION_CLASS_INFO *notification = NULL;
|
||||
BACNET_DESTINATION recipient_list[NC_MAX_RECIPIENTS] = { 0 };
|
||||
uint8_t *application_data = NULL;
|
||||
int application_data_len = 0, len = 0;
|
||||
uint32_t notify_index = 0;
|
||||
unsigned index = 0;
|
||||
unsigned element_count = 0, new_element_count = 0;
|
||||
unsigned added_element_count = 0, same_element_count = 0;
|
||||
bool same_element = false;
|
||||
unsigned i = 0, j = 0;
|
||||
|
||||
if (!list_element) {
|
||||
return BACNET_STATUS_ABORT;
|
||||
}
|
||||
if (list_element->object_property != PROP_RECIPIENT_LIST) {
|
||||
list_element->error_class = ERROR_CLASS_SERVICES;
|
||||
list_element->error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST;
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
if (list_element->array_index != BACNET_ARRAY_ALL) {
|
||||
list_element->error_class = ERROR_CLASS_PROPERTY;
|
||||
list_element->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
notify_index =
|
||||
Notification_Class_Instance_To_Index(list_element->object_instance);
|
||||
if (notify_index < MAX_NOTIFICATION_CLASSES) {
|
||||
notification = &NC_Info[notify_index];
|
||||
} else {
|
||||
list_element->error_class = ERROR_CLASS_OBJECT;
|
||||
list_element->error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* get the current size of all entry of Recipient_List */
|
||||
for (index = 0; index < NC_MAX_RECIPIENTS; index++) {
|
||||
BACNET_DESTINATION *d1;
|
||||
BACNET_RECIPIENT *r1;
|
||||
d1 = ¬ification->Recipient_List[index];
|
||||
r1 = &d1->Recipient;
|
||||
if (!bacnet_recipient_device_wildcard(r1)) {
|
||||
element_count++;
|
||||
}
|
||||
}
|
||||
} else if (CurrentNotify->Recipient_List[idx]
|
||||
.Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) {
|
||||
/* decode the elements */
|
||||
new_element_count = 0;
|
||||
application_data = list_element->application_data;
|
||||
application_data_len = list_element->application_data_len;
|
||||
while (application_data_len > 0) {
|
||||
len = bacnet_destination_decode(application_data,
|
||||
application_data_len, &recipient_list[index]);
|
||||
if (len > 0) {
|
||||
new_element_count++;
|
||||
application_data_len -= len;
|
||||
} else {
|
||||
list_element->first_failed_element_number = new_element_count;
|
||||
list_element->error_class = ERROR_CLASS_PROPERTY;
|
||||
list_element->error_code = ERROR_CODE_INVALID_DATA_ENCODING;
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
if (new_element_count == 0) {
|
||||
return BACNET_STATUS_OK;
|
||||
}
|
||||
/* determine the same and added recipient counts */
|
||||
same_element_count = 0;
|
||||
added_element_count = 0;
|
||||
for (i = 0; i < new_element_count; i++) {
|
||||
BACNET_DESTINATION *d1;
|
||||
BACNET_RECIPIENT *r1;
|
||||
d1 = &recipient_list[i];
|
||||
r1 = &d1->Recipient;
|
||||
same_element = false;
|
||||
for (j = 0; j < NC_MAX_RECIPIENTS; j++) {
|
||||
BACNET_DESTINATION *d2;
|
||||
BACNET_RECIPIENT *r2;
|
||||
d2 = ¬ification->Recipient_List[j];
|
||||
r2 = &d2->Recipient;
|
||||
if (bacnet_recipient_same(r1, r2)) {
|
||||
same_element = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (same_element) {
|
||||
same_element_count++;
|
||||
} else {
|
||||
added_element_count++;
|
||||
if ((added_element_count+element_count) > NC_MAX_RECIPIENTS) {
|
||||
list_element->first_failed_element_number = 1 + i;
|
||||
list_element->error_class = ERROR_CLASS_RESOURCES;
|
||||
list_element->error_code =
|
||||
ERROR_CODE_NO_SPACE_TO_ADD_LIST_ELEMENT;
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* update existing and add new */
|
||||
for (i = 0; i < new_element_count; i++) {
|
||||
BACNET_DESTINATION *d1;
|
||||
BACNET_RECIPIENT *r1;
|
||||
d1 = &recipient_list[i];
|
||||
r1 = &d1->Recipient;
|
||||
same_element = false;
|
||||
for (j = 0; j < NC_MAX_RECIPIENTS; j++) {
|
||||
BACNET_DESTINATION *d2;
|
||||
BACNET_RECIPIENT *r2;
|
||||
d2 = ¬ification->Recipient_List[j];
|
||||
r2 = &d2->Recipient;
|
||||
if (bacnet_recipient_same(r1, r2)) {
|
||||
/* update existing element */
|
||||
same_element = true;
|
||||
bacnet_destination_copy(d2, d1);
|
||||
}
|
||||
}
|
||||
if (!same_element) {
|
||||
/* add new element to next free slot */
|
||||
for (j = 0; j < NC_MAX_RECIPIENTS; j++) {
|
||||
BACNET_DESTINATION *d2;
|
||||
BACNET_RECIPIENT *r2;
|
||||
d2 = ¬ification->Recipient_List[j];
|
||||
r2 = &d2->Recipient;
|
||||
if (!bacnet_recipient_device_wildcard(r2)) {
|
||||
bacnet_destination_copy(d2, d1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BACNET_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief RemoveListElement from an object list property
|
||||
* @ingroup ObjHelpers
|
||||
* @param list_element [in] Pointer to the BACnet_List_Element_Data structure,
|
||||
* which is packed with the information from the request.
|
||||
* @return The length of the apdu encoded or #BACNET_STATUS_ERROR or
|
||||
* #BACNET_STATUS_ABORT or #BACNET_STATUS_REJECT.
|
||||
* @note After verifying the validity of the request, the responding
|
||||
* BACnet-user shall attempt to modify the object identified in the
|
||||
* 'Object Identifier' parameter. If the identified object exists and it
|
||||
* has the property specified in the 'Property Identifier' parameter,
|
||||
* and if present has the array element specified in the 'Property Array Index'
|
||||
* parameter, an attempt shall be made to remove the
|
||||
* elements in the 'List of Elements' from the property or array element
|
||||
* of the property of the object.
|
||||
*
|
||||
* When comparing elements of the service with entries in the affected
|
||||
* BACnetLIST, the complete element shall be compared
|
||||
* unless the property description specifies otherwise. If one or more
|
||||
* of the elements does not exist or cannot be removed because
|
||||
* of insufficient authority, none of the elements shall be removed and
|
||||
* a 'Result(-)' response primitive shall be issued.
|
||||
*/
|
||||
int Notification_Class_Remove_List_Element(
|
||||
BACNET_LIST_ELEMENT_DATA * list_element)
|
||||
{
|
||||
NOTIFICATION_CLASS_INFO *notification = NULL;
|
||||
uint32_t notify_index = 0;
|
||||
unsigned index = 0;
|
||||
BACNET_DESTINATION recipient_list[NC_MAX_RECIPIENTS] = { 0 };
|
||||
uint8_t *application_data = NULL;
|
||||
int application_data_len = 0, len = 0;
|
||||
unsigned element_count = 0;
|
||||
unsigned remove_element_count = 0;
|
||||
bool same_element = false;
|
||||
unsigned i = 0, j = 0;
|
||||
|
||||
if (!list_element) {
|
||||
return BACNET_STATUS_ABORT;
|
||||
}
|
||||
if (list_element->object_property != PROP_RECIPIENT_LIST) {
|
||||
list_element->error_class = ERROR_CLASS_SERVICES;
|
||||
list_element->error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST;
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
if (list_element->array_index != BACNET_ARRAY_ALL) {
|
||||
list_element->error_class = ERROR_CLASS_PROPERTY;
|
||||
list_element->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
notify_index =
|
||||
Notification_Class_Instance_To_Index(list_element->object_instance);
|
||||
if (notify_index < MAX_NOTIFICATION_CLASSES) {
|
||||
notification = &NC_Info[notify_index];
|
||||
} else {
|
||||
list_element->error_class = ERROR_CLASS_OBJECT;
|
||||
list_element->error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* get the current size of all entry of Recipient_List */
|
||||
for (index = 0; index < NC_MAX_RECIPIENTS; index++) {
|
||||
BACNET_DESTINATION *d1;
|
||||
BACNET_RECIPIENT *r1;
|
||||
d1 = ¬ification->Recipient_List[index];
|
||||
r1 = &d1->Recipient;
|
||||
if (!bacnet_recipient_device_wildcard(r1)) {
|
||||
element_count++;
|
||||
}
|
||||
}
|
||||
/* decode the elements */
|
||||
application_data = list_element->application_data;
|
||||
application_data_len = list_element->application_data_len;
|
||||
while (application_data_len > 0) {
|
||||
len = bacnet_destination_decode(application_data,
|
||||
application_data_len, &recipient_list[index]);
|
||||
if (len > 0) {
|
||||
remove_element_count++;
|
||||
application_data_len -= len;
|
||||
} else {
|
||||
list_element->first_failed_element_number = remove_element_count;
|
||||
list_element->error_class = ERROR_CLASS_PROPERTY;
|
||||
list_element->error_code = ERROR_CODE_INVALID_DATA_ENCODING;
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
/* determine if one or more of the elements does not exist */
|
||||
for (i = 0; i < remove_element_count; i++) {
|
||||
BACNET_DESTINATION *d1;
|
||||
BACNET_RECIPIENT *r1;
|
||||
d1 = &recipient_list[i];
|
||||
r1 = &d1->Recipient;
|
||||
same_element = false;
|
||||
for (j = 0; j < NC_MAX_RECIPIENTS; j++) {
|
||||
BACNET_DESTINATION *d2;
|
||||
BACNET_RECIPIENT *r2;
|
||||
d2 = ¬ification->Recipient_List[j];
|
||||
r2 = &d2->Recipient;
|
||||
if (bacnet_recipient_same(r1, r2)) {
|
||||
same_element = true;
|
||||
}
|
||||
}
|
||||
if (!same_element) {
|
||||
list_element->first_failed_element_number = 1 + i;
|
||||
list_element->error_class = ERROR_CLASS_SERVICES;
|
||||
list_element->error_code = ERROR_CODE_LIST_ELEMENT_NOT_FOUND;
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
/* remove any matching elements */
|
||||
for (i = 0; i < remove_element_count; i++) {
|
||||
BACNET_DESTINATION *d1;
|
||||
BACNET_RECIPIENT *r1;
|
||||
d1 = &recipient_list[i];
|
||||
r1 = &d1->Recipient;
|
||||
for (j = 0; j < NC_MAX_RECIPIENTS; j++) {
|
||||
BACNET_DESTINATION *d2;
|
||||
BACNET_RECIPIENT *r2;
|
||||
d2 = ¬ification->Recipient_List[j];
|
||||
r2 = &d2->Recipient;
|
||||
if (bacnet_recipient_same(r1, r2)) {
|
||||
bacnet_destination_default_init(d2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BACNET_STATUS_OK;
|
||||
}
|
||||
#endif /* defined(INTRINSIC_REPORTING) */
|
||||
|
||||
@@ -26,127 +26,87 @@
|
||||
#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 */
|
||||
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 */
|
||||
BACNET_DESTINATION
|
||||
Recipient_List[NC_MAX_RECIPIENTS]; /* List of BACnetDestination */
|
||||
} NOTIFICATION_CLASS_INFO;
|
||||
|
||||
|
||||
/* Indicates whether the transaction has been confirmed */
|
||||
typedef struct Acked_info {
|
||||
bool bIsAcked; /* true when transitions is acked */
|
||||
BACNET_DATE_TIME Time_Stamp; /* time stamp of when a alarm was generated */
|
||||
} ACKED_INFO;
|
||||
|
||||
|
||||
/* Information needed to send AckNotification */
|
||||
typedef struct Ack_Notification {
|
||||
bool bSendAckNotify; /* true if need to send AckNotification */
|
||||
uint8_t EventState;
|
||||
} ACK_NOTIFICATION;
|
||||
|
||||
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void Notification_Class_Property_Lists(
|
||||
const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
const int **pRequired, const int **pOptional, const int **pProprietary);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void Notification_Class_Init(
|
||||
void);
|
||||
void Notification_Class_Init(void);
|
||||
|
||||
bool Notification_Class_Valid_Instance(
|
||||
uint32_t object_instance);
|
||||
bool Notification_Class_Valid_Instance(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
unsigned Notification_Class_Count(
|
||||
void);
|
||||
unsigned Notification_Class_Count(void);
|
||||
BACNET_STACK_EXPORT
|
||||
uint32_t Notification_Class_Index_To_Instance(
|
||||
unsigned index);
|
||||
uint32_t Notification_Class_Index_To_Instance(unsigned index);
|
||||
BACNET_STACK_EXPORT
|
||||
unsigned Notification_Class_Instance_To_Index(
|
||||
uint32_t object_instance);
|
||||
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);
|
||||
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int Notification_Class_Read_Property(
|
||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
||||
int Notification_Class_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Notification_Class_Write_Property(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
||||
bool Notification_Class_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int Notification_Class_Add_List_Element(BACNET_LIST_ELEMENT_DATA *list_element);
|
||||
|
||||
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);
|
||||
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_find_recipient(
|
||||
void);
|
||||
void Notification_Class_find_recipient(void);
|
||||
#endif /* defined(INTRINSIC_REPORTING) */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user