Feature/add create object service (#476)

Added BACnet CreateObject and DeleteObject services

* refactored codec for BACnetPropertyValue into bacapp module
* added unit tests for BACnetPropertyValue
* refactored COV and Events to use BACnetPropertyValue codec API
* added unit tests for COV
* added overrun safe decoders for tag numbers and boolean context
* added unit tests and codecs for CreateObject and DeleteObject services
* added APDU service handers and senders for CreateObject and DeleteObject services
* added command line apps bacco and bacdo for CreateObject and DeleteObject services
* added CreateObject and DeleteObject service handling in example server app and device object
* added new BACnetRejectReason, Error Class, and BACnetAbortReason enumerations and conversions

---------

Co-authored-by: Steve Karg <skarg@users.sourceforge.net>
This commit is contained in:
Steve Karg
2023-08-28 13:02:35 -05:00
committed by GitHub
parent 8184afea12
commit f61f4300be
60 changed files with 3946 additions and 942 deletions
+2
View File
@@ -111,4 +111,6 @@ apps/piface/libpifacedigital/
/apps/mstpcrc/mstpcrc
/apps/add-list-element/bacale
/apps/remove-list-element/bacrle
/apps/create-object/bacco
/apps/delete-object/bacdo
+18
View File
@@ -227,10 +227,14 @@ add_library(${PROJECT_NAME}
src/bacnet/basic/service/h_awf.h
src/bacnet/basic/service/h_ccov.c
src/bacnet/basic/service/h_ccov.h
src/bacnet/basic/service/h_create_object.c
src/bacnet/basic/service/h_create_object.h
src/bacnet/basic/service/h_cov.c
src/bacnet/basic/service/h_cov.h
src/bacnet/basic/service/h_dcc.c
src/bacnet/basic/service/h_dcc.h
src/bacnet/basic/service/h_delete_object.c
src/bacnet/basic/service/h_delete_object.h
src/bacnet/basic/service/h_gas_a.c
src/bacnet/basic/service/h_gas_a.h
src/bacnet/basic/service/h_get_alarm_sum.c
@@ -287,10 +291,14 @@ add_library(${PROJECT_NAME}
src/bacnet/basic/service/s_awfs.h
src/bacnet/basic/service/s_cevent.c
src/bacnet/basic/service/s_cevent.h
src/bacnet/basic/service/s_create_object.c
src/bacnet/basic/service/s_create_object.h
src/bacnet/basic/service/s_cov.c
src/bacnet/basic/service/s_cov.h
src/bacnet/basic/service/s_dcc.c
src/bacnet/basic/service/s_dcc.h
src/bacnet/basic/service/s_delete_object.c
src/bacnet/basic/service/s_delete_object.h
src/bacnet/basic/service/s_error.c
src/bacnet/basic/service/s_error.h
src/bacnet/basic/service/s_get_alarm_sum.c
@@ -358,6 +366,8 @@ add_library(${PROJECT_NAME}
src/bacnet/config.h
src/bacnet/cov.c
src/bacnet/cov.h
src/bacnet/create_object.c
src/bacnet/create_object.h
src/bacnet/credential_authentication_factor.c
src/bacnet/credential_authentication_factor.h
src/bacnet/datalink/arcnet.h
@@ -387,6 +397,8 @@ add_library(${PROJECT_NAME}
src/bacnet/datetime.h
src/bacnet/dcc.c
src/bacnet/dcc.h
src/bacnet/delete_object.c
src/bacnet/delete_object.h
src/bacnet/event.c
src/bacnet/event.h
src/bacnet/get_alarm_sum.c
@@ -560,9 +572,15 @@ if(BACNET_STACK_BUILD_APPS)
add_executable(add-list-element apps/add-list-element/main.c)
target_link_libraries(add-list-element PRIVATE ${PROJECT_NAME})
add_executable(create-object apps/create-object/main.c)
target_link_libraries(create-object PRIVATE ${PROJECT_NAME})
add_executable(dcc apps/dcc/main.c)
target_link_libraries(dcc PRIVATE ${PROJECT_NAME})
add_executable(delete-object apps/delete-object/main.c)
target_link_libraries(delete-object PRIVATE ${PROJECT_NAME})
add_executable(epics apps/epics/main.c)
target_link_libraries(epics PRIVATE ${PROJECT_NAME})
+2 -1
View File
@@ -177,7 +177,8 @@ 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 add-list-element remove-list-element
server-client add-list-element remove-list-element create-object \
delete-object
ifeq (${BACDL_DEFINE},-DBACDL_BIP=1)
SUBDIRS += whoisrouter iamrouter initrouter whatisnetnum netnumis
+39
View File
@@ -0,0 +1,39 @@
#Makefile to build BACnet Application using GCC compiler
# Executable file name
TARGET = bacco
# 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
+387
View File
@@ -0,0 +1,387 @@
/**
* @file
* @brief Application to send a BACnet CreateObject
* @author Steve Karg <skarg@users.sourceforge.net>
* @date August 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/create_object.h"
#include "bacnet/abort.h"
#include "bacnet/reject.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/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 = BACNET_MAX_INSTANCE;
/* 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 MyPrintHandler(
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_ERROR_CLASS error_class,
BACNET_ERROR_CODE error_code,
uint32_t first_failed_element_number)
{
printf("[{\n \"%s\": {\n"
" \"object-type\": \"%s\",\n \"object-instance\": %lu,\n"
" \"error-class\": \"%s\",\n \"error-code\": \"%s\"",
bactext_confirmed_service_name(SERVICE_CONFIRMED_CREATE_OBJECT),
bactext_object_type_name(object_type),
(unsigned long)object_instance,
bactext_error_class_name((int)error_class),
bactext_error_code_name((int)error_code));
if (first_failed_element_number > 0) {
printf(",\n \"first-failed-element-number\": %lu",
(unsigned long)first_failed_element_number);
}
printf("\n }\n}]\n");
}
static void MyCreateObjectErrorHandler(BACNET_ADDRESS *src,
uint8_t invoke_id,
uint8_t service_choice,
uint8_t *service_request,
uint16_t service_len)
{
int len = 0;
BACNET_CREATE_OBJECT_DATA data;
(void)service_choice;
if (address_match(&Target_Address, src) &&
(invoke_id == Request_Invoke_ID)) {
len = create_object_error_ack_service_decode(service_request,
service_len, &data);
if (len > 0) {
MyPrintHandler(data.object_type, data.object_instance,
data.error_class, data.error_code,
data.first_failed_element_number);
}
Error_Detected = true;
}
}
static void MyCreateObjectAckHandler(uint8_t *service_request,
uint16_t service_len,
BACNET_ADDRESS *src,
BACNET_CONFIRMED_SERVICE_ACK_DATA *service_data)
{
BACNET_CREATE_OBJECT_DATA data;
int len = 0;
if (address_match(&Target_Address, src) &&
(service_data->invoke_id == Request_Invoke_ID)) {
len = create_object_ack_service_decode(
service_request, service_len, &data);
if (len < 0) {
MyPrintHandler(Target_Object_Type, Target_Object_Instance,
ERROR_CLASS_SERVICES, ERROR_CODE_REJECT_OTHER, 0);
} else {
MyPrintHandler(data.object_type, data.object_instance,
ERROR_CLASS_SERVICES, ERROR_CODE_SUCCESS, 0);
}
} else {
if (Verbose) {
printf("CreateObjectACK - not matched\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)) {
MyPrintHandler(Target_Object_Type, Target_Object_Instance,
ERROR_CLASS_SERVICES, abort_convert_to_error_code(abort_reason), 0);
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)) {
MyPrintHandler(Target_Object_Type, Target_Object_Instance,
ERROR_CLASS_SERVICES,
reject_convert_to_error_code(reject_reason), 0);
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_ack_handler(
SERVICE_CONFIRMED_CREATE_OBJECT, MyCreateObjectAckHandler);
apdu_set_complex_error_handler(
SERVICE_CONFIRMED_CREATE_OBJECT, MyCreateObjectErrorHandler);
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]\n", filename);
printf(" [--dnet][--dadr][--mac]\n");
printf(" [--version][--help][--verbose]\n");
}
static void print_help(char *filename)
{
printf("Create an object 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 creating. 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 (optional):\n"
"This is the object instance number of the object that\n"
"you are creating. For example, if you were writing\n"
"Analog Output 2, the object-instance would be 2.\n");
printf("\n");
printf("Example:\n"
"If you want to CreateObject of an Analog Input 1\n"
"send the following command:\n"
"%s 123 0 1\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;
struct mstimer apdu_timer;
struct mstimer maintenance_timer;
bool found = false;
unsigned object_type = 0;
unsigned object_instance = 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,
"object-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++;
}
}
}
if (target_args < 2) {
print_usage(filename);
return 0;
}
/* 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 CreateObject to Device %u.\n",
Target_Device_Object_Instance);
}
Request_Invoke_ID =
Send_Create_Object_Request(Target_Device_Object_Instance,
Target_Object_Type, Target_Object_Instance);
} else if (tsm_invoke_id_free(Request_Invoke_ID)) {
break;
} else if (tsm_invoke_id_failed(Request_Invoke_ID)) {
MyPrintHandler(Target_Object_Type, Target_Object_Instance,
ERROR_CLASS_COMMUNICATION, ERROR_CODE_ABORT_TSM_TIMEOUT, 0);
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)) {
MyPrintHandler(Target_Object_Type, Target_Object_Instance,
ERROR_CLASS_COMMUNICATION,
ERROR_CODE_ABORT_APPLICATION_EXCEEDED_REPLY_TIME, 0);
Error_Detected = true;
}
if (Error_Detected) {
break;
}
}
if (Error_Detected) {
return 1;
}
return 0;
}
+39
View File
@@ -0,0 +1,39 @@
#Makefile to build BACnet Application using GCC compiler
# Executable file name
TARGET = bacdo
# 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
+347
View File
@@ -0,0 +1,347 @@
/**
* @file
* @brief Application to send a BACnet DeleteObject
* @author Steve Karg <skarg@users.sourceforge.net>
* @date August 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/create_object.h"
#include "bacnet/whois.h"
#include "bacnet/abort.h"
#include "bacnet/reject.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/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 = BACNET_MAX_INSTANCE;
/* 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 MyPrintHandler(
BACNET_ERROR_CLASS error_class,
BACNET_ERROR_CODE error_code)
{
printf("[{\n \"%s\": {\n"
" \"object-type\": \"%s\",\n \"object-instance\": %lu,\n"
" \"error-class\": \"%s\",\n \"error-code\": \"%s\"\n }\n}]\n",
bactext_confirmed_service_name(SERVICE_CONFIRMED_DELETE_OBJECT),
bactext_object_type_name(Target_Object_Type),
(unsigned long)Target_Object_Instance,
bactext_error_class_name((int)error_class),
bactext_error_code_name((int)error_code));
}
static void MyErrorHandler(BACNET_ADDRESS *src,
uint8_t invoke_id,
BACNET_ERROR_CLASS error_class,
BACNET_ERROR_CODE error_code)
{
if (address_match(&Target_Address, src) &&
(invoke_id == Request_Invoke_ID)) {
MyPrintHandler(error_class, error_code);
Error_Detected = true;
}
}
static void MySimpleAckHandler(
BACNET_ADDRESS *src, uint8_t invoke_id)
{
if (address_match(&Target_Address, src) &&
(invoke_id == Request_Invoke_ID)) {
MyPrintHandler(ERROR_CLASS_SERVICES, ERROR_CODE_SUCCESS);
}
}
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)) {
MyPrintHandler(ERROR_CLASS_SERVICES,
abort_convert_to_error_code(abort_reason));
Error_Detected = true;
}
}
static void MyRejectHandler(
BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t reject_reason)
{
if (address_match(&Target_Address, src) &&
(invoke_id == Request_Invoke_ID)) {
MyPrintHandler(ERROR_CLASS_SERVICES,
reject_convert_to_error_code(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_DELETE_OBJECT, MySimpleAckHandler);
apdu_set_error_handler(SERVICE_CONFIRMED_DELETE_OBJECT, MyErrorHandler);
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\n",
filename);
printf(" [--dnet][--dadr][--mac]\n");
printf(" [--version][--help][--verbose]\n");
}
static void print_help(char *filename)
{
printf("Create an object 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 deleting. 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 deleting. For example, if you were deleting\n"
"Analog Output 2, the object-instance would be 2.\n");
printf("\n");
printf("Example:\n"
"If you want to DeleteObject an Analog Input 1\n"
"send the following command:\n"
"%s 123 0 1\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;
struct mstimer apdu_timer;
struct mstimer maintenance_timer;
bool found = false;
unsigned object_type = 0;
unsigned object_instance = 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,
"object-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++;
}
}
}
if (target_args < 2) {
print_usage(filename);
return 0;
}
/* 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 DeleteObject to Device %u.\n",
Target_Device_Object_Instance);
}
Request_Invoke_ID = Send_Delete_Object_Request(
Target_Device_Object_Instance, Target_Object_Type,
Target_Object_Instance);
} else if (tsm_invoke_id_free(Request_Invoke_ID)) {
break;
} else if (tsm_invoke_id_failed(Request_Invoke_ID)) {
MyPrintHandler(ERROR_CLASS_COMMUNICATION,
ERROR_CODE_ABORT_TSM_TIMEOUT);
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)) {
MyPrintHandler(ERROR_CLASS_COMMUNICATION,
ERROR_CODE_ABORT_APPLICATION_EXCEEDED_REPLY_TIME);
Error_Detected = true;
}
if (Error_Detected) {
break;
}
}
if (Error_Detected) {
return 1;
}
return 0;
}
+4
View File
@@ -144,6 +144,10 @@ static void Init_Service_Handlers(void)
#if defined(BACNET_TIME_MASTER)
handler_timesync_init();
#endif
apdu_set_confirmed_handler(
SERVICE_CONFIRMED_CREATE_OBJECT, handler_create_object);
apdu_set_confirmed_handler(
SERVICE_CONFIRMED_DELETE_OBJECT, handler_delete_object);
}
static void print_usage(const char *filename)
+64 -2
View File
@@ -73,6 +73,21 @@ BACNET_ABORT_REASON abort_convert_error_code(BACNET_ERROR_CODE error_code)
case ERROR_CODE_ABORT_INSUFFICIENT_SECURITY:
abort_code = ABORT_REASON_INSUFFICIENT_SECURITY;
break;
case ERROR_CODE_ABORT_WINDOW_SIZE_OUT_OF_RANGE:
abort_code = ABORT_REASON_WINDOW_SIZE_OUT_OF_RANGE;
break;
case ERROR_CODE_ABORT_APPLICATION_EXCEEDED_REPLY_TIME:
abort_code = ABORT_REASON_APPLICATION_EXCEEDED_REPLY_TIME;
break;
case ERROR_CODE_ABORT_OUT_OF_RESOURCES:
abort_code = ABORT_REASON_OUT_OF_RESOURCES;
break;
case ERROR_CODE_ABORT_TSM_TIMEOUT:
abort_code = ABORT_REASON_TSM_TIMEOUT;
break;
case ERROR_CODE_ABORT_APDU_TOO_LONG:
abort_code = ABORT_REASON_APDU_TOO_LONG;
break;
case ERROR_CODE_ABORT_PROPRIETARY:
abort_code = ABORT_REASON_PROPRIETARY_FIRST;
break;
@@ -85,6 +100,38 @@ BACNET_ABORT_REASON abort_convert_error_code(BACNET_ERROR_CODE error_code)
return (abort_code);
}
/**
* @brief Determine if a BACnetErrorCode is a BACnetAbortReason
* @param error_code #BACNET_ERROR_CODE enumeration
* @return true if the BACnet Error Code is a BACnet abort reason
*/
bool abort_valid_error_code(BACNET_ERROR_CODE error_code)
{
bool status = false;
switch (error_code) {
case ERROR_CODE_ABORT_OTHER:
case ERROR_CODE_ABORT_BUFFER_OVERFLOW:
case ERROR_CODE_ABORT_INVALID_APDU_IN_THIS_STATE:
case ERROR_CODE_ABORT_PREEMPTED_BY_HIGHER_PRIORITY_TASK:
case ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED:
case ERROR_CODE_ABORT_SECURITY_ERROR:
case ERROR_CODE_ABORT_INSUFFICIENT_SECURITY:
case ERROR_CODE_ABORT_WINDOW_SIZE_OUT_OF_RANGE:
case ERROR_CODE_ABORT_APPLICATION_EXCEEDED_REPLY_TIME:
case ERROR_CODE_ABORT_OUT_OF_RESOURCES:
case ERROR_CODE_ABORT_TSM_TIMEOUT:
case ERROR_CODE_ABORT_APDU_TOO_LONG:
case ERROR_CODE_ABORT_PROPRIETARY:
status = true;
break;
default:
break;
}
return status;
}
/**
* @brief Convert error-code from abort-reason
*
@@ -101,6 +148,9 @@ BACNET_ERROR_CODE abort_convert_to_error_code(BACNET_ABORT_REASON abort_code)
BACNET_ERROR_CODE error_code = ERROR_CODE_ABORT_OTHER;
switch (abort_code) {
case ABORT_REASON_OTHER:
error_code = ERROR_CODE_ABORT_OTHER;
break;
case ABORT_REASON_BUFFER_OVERFLOW:
error_code = ERROR_CODE_ABORT_BUFFER_OVERFLOW;
break;
@@ -119,8 +169,20 @@ BACNET_ERROR_CODE abort_convert_to_error_code(BACNET_ABORT_REASON abort_code)
case ABORT_REASON_INSUFFICIENT_SECURITY:
error_code = ERROR_CODE_ABORT_INSUFFICIENT_SECURITY;
break;
case ABORT_REASON_OTHER:
error_code = ERROR_CODE_ABORT_OTHER;
case ABORT_REASON_WINDOW_SIZE_OUT_OF_RANGE:
error_code = ERROR_CODE_ABORT_WINDOW_SIZE_OUT_OF_RANGE;
break;
case ABORT_REASON_APPLICATION_EXCEEDED_REPLY_TIME:
error_code = ERROR_CODE_ABORT_APPLICATION_EXCEEDED_REPLY_TIME;
break;
case ABORT_REASON_OUT_OF_RESOURCES:
error_code = ERROR_CODE_ABORT_OUT_OF_RESOURCES;
break;
case ABORT_REASON_TSM_TIMEOUT:
error_code = ERROR_CODE_ABORT_TSM_TIMEOUT;
break;
case ABORT_REASON_APDU_TOO_LONG:
error_code = ERROR_CODE_ABORT_APDU_TOO_LONG;
break;
default:
if ((abort_code >= ABORT_REASON_PROPRIETARY_FIRST) &&
+5 -2
View File
@@ -21,8 +21,8 @@
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*********************************************************************/
#ifndef ABORT_H
#define ABORT_H
#ifndef BACNET_ABORT_H
#define BACNET_ABORT_H
#include <stdint.h>
#include <stdbool.h>
@@ -37,6 +37,9 @@ extern "C" {
BACNET_ABORT_REASON abort_convert_error_code(
BACNET_ERROR_CODE error_code);
BACNET_STACK_EXPORT
bool abort_valid_error_code(
BACNET_ERROR_CODE error_code);
BACNET_STACK_EXPORT
BACNET_ERROR_CODE abort_convert_to_error_code(
BACNET_ABORT_REASON abort_code);
+258 -29
View File
@@ -39,7 +39,7 @@
#include <ctype.h> /* for isalnum */
#include <errno.h>
#include <math.h>
#if (__STDC_VERSION__ >= 199901L) && defined (__STDC_ISO_10646__)
#if (__STDC_VERSION__ >= 199901L) && defined(__STDC_ISO_10646__)
#include <wchar.h>
#include <wctype.h>
#endif
@@ -198,8 +198,8 @@ int bacapp_encode_application_data(
break;
case BACNET_APPLICATION_TAG_DESTINATION:
/* BACnetDestination */
apdu_len = bacnet_destination_encode(
apdu, &value->type.Destination);
apdu_len =
bacnet_destination_encode(apdu, &value->type.Destination);
break;
#endif
default:
@@ -351,8 +351,8 @@ int bacapp_decode_data(uint8_t *apdu,
break;
case BACNET_APPLICATION_TAG_DESTINATION:
/* BACnetDestination */
len = bacnet_destination_decode(apdu, len_value_type,
&value->type.Destination);
len = bacnet_destination_decode(
apdu, len_value_type, &value->type.Destination);
break;
#endif
default:
@@ -684,8 +684,8 @@ int bacapp_encode_context_data_value(uint8_t *apdu,
break;
case BACNET_APPLICATION_TAG_DESTINATION:
/* BACnetDestination */
apdu_len = bacnet_destination_context_encode(apdu,
context_tag_number, &value->type.Destination);
apdu_len = bacnet_destination_context_encode(
apdu, context_tag_number, &value->type.Destination);
break;
#endif
default:
@@ -1535,8 +1535,8 @@ int bacapp_data_len(
}
do {
if (bacnet_is_opening_tag(apdu, apdu_len_max)) {
len = bacnet_tag_number_and_value_decode(apdu,
apdu_len_max - apdu_len, &tag_number, &value);
len = bacnet_tag_number_and_value_decode(
apdu, apdu_len_max - apdu_len, &tag_number, &value);
if (opening_tag_number_counter == 0) {
opening_tag_number = tag_number;
opening_tag_number_counter = 1;
@@ -1546,8 +1546,8 @@ int bacapp_data_len(
opening_tag_number_counter++;
}
} else if (bacnet_is_closing_tag(apdu, apdu_len_max)) {
len = bacnet_tag_number_and_value_decode(apdu,
apdu_len_max - apdu_len, &tag_number, &value);
len = bacnet_tag_number_and_value_decode(
apdu, apdu_len_max - apdu_len, &tag_number, &value);
if (tag_number == opening_tag_number) {
if (opening_tag_number_counter > 0) {
opening_tag_number_counter--;
@@ -1585,7 +1585,6 @@ int bacapp_data_len(
}
} while (opening_tag_number_counter > 0);
return total_len;
}
@@ -1603,13 +1602,14 @@ static int bacapp_snprintf_date(char *str, size_t str_len, BACNET_DATE *bdate)
{
int ret_val = 0;
int slen = 0;
const char *weekday_text, *month_text;
weekday_text = bactext_day_of_week_name(bdate->wday);
month_text = bactext_month_name(bdate->month);
/* false positive cppcheck - snprintf allows null pointers */
/* cppcheck-suppress nullPointer */
/* cppcheck-suppress ctunullpointer */
slen = snprintf(str, str_len, "%s, %s",
bactext_day_of_week_name(bdate->wday),
bactext_month_name(bdate->month));
slen = snprintf(str, str_len, "%s, %s", weekday_text, month_text);
if (str) {
str += slen;
if (str_len >= slen) {
@@ -1873,7 +1873,7 @@ int bacapp_snprintf_value(
#if defined(BACAPP_OCTET_STRING) || defined(BACAPP_TYPES_EXTRA)
uint8_t *octet_str;
#endif
#if (__STDC_VERSION__ >= 199901L) && defined (__STDC_ISO_10646__)
#if (__STDC_VERSION__ >= 199901L) && defined(__STDC_ISO_10646__)
/* Wide character (decoded from multi-byte character). */
wchar_t wc;
/* Wide character length in bytes. */
@@ -1953,7 +1953,7 @@ int bacapp_snprintf_value(
}
}
ret_val += slen;
#if (__STDC_VERSION__ >= 199901L) && defined (__STDC_ISO_10646__)
#if (__STDC_VERSION__ >= 199901L) && defined(__STDC_ISO_10646__)
if (characterstring_encoding(&value->type.Character_String) ==
CHARACTER_UTF8) {
while (len > 0) {
@@ -2201,14 +2201,16 @@ int bacapp_snprintf_value(
break;
case BACNET_APPLICATION_TAG_TIMESTAMP:
/*ISO 8601 format */
slen = snprintf(str, str_len, "%04u-%02u-%02uT%02u:%02u:%02u.%03u",
(unsigned) value->type.Time_Stamp.value.dateTime.date.year,
(unsigned) value->type.Time_Stamp.value.dateTime.date.month,
(unsigned) value->type.Time_Stamp.value.dateTime.date.day,
(unsigned) value->type.Time_Stamp.value.dateTime.time.hour,
(unsigned) value->type.Time_Stamp.value.dateTime.time.min,
(unsigned) value->type.Time_Stamp.value.dateTime.time.sec,
(unsigned) value->type.Time_Stamp.value.dateTime.time.hundredths);
slen = snprintf(str, str_len,
"%04u-%02u-%02uT%02u:%02u:%02u.%03u",
(unsigned)value->type.Time_Stamp.value.dateTime.date.year,
(unsigned)value->type.Time_Stamp.value.dateTime.date.month,
(unsigned)value->type.Time_Stamp.value.dateTime.date.day,
(unsigned)value->type.Time_Stamp.value.dateTime.time.hour,
(unsigned)value->type.Time_Stamp.value.dateTime.time.min,
(unsigned)value->type.Time_Stamp.value.dateTime.time.sec,
(unsigned)
value->type.Time_Stamp.value.dateTime.time.hundredths);
ret_val += slen;
break;
case BACNET_APPLICATION_TAG_LIGHTING_COMMAND:
@@ -2279,7 +2281,8 @@ int bacapp_snprintf_value(
break;
case BACNET_APPLICATION_TAG_DESTINATION:
/* BACnetWeeklySchedule */
ret_val = bacnet_destination_to_ascii(&value->type.Destination, str, str_len);
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) {
@@ -2803,8 +2806,8 @@ bool bacapp_parse_application_data(BACNET_APPLICATION_TAG tag_number,
}
break;
case BACNET_APPLICATION_TAG_DESTINATION:
status = bacnet_destination_from_ascii(&value->type.Destination,
argv);
status = bacnet_destination_from_ascii(
&value->type.Destination, argv);
break;
#endif
default:
@@ -2868,6 +2871,231 @@ void bacapp_property_value_list_init(BACNET_PROPERTY_VALUE *value, size_t count)
}
}
/**
* @brief Link an array of BACNET_PROPERTY_VALUE elements.
* The linked-list is used prior to encoding or decoding
* the APDU data into the structure.
*
* @param value_list - Pointer to the first BACNET_PROPERTY_VALUE element in
* an array
* @param count - number of BACNET_PROPERTY_VALUE elements to link
*/
void bacapp_property_value_list_link(
BACNET_PROPERTY_VALUE *value_list, size_t count)
{
BACNET_PROPERTY_VALUE *current_value_list = NULL;
if (value_list) {
while (count) {
if (count > 1) {
current_value_list = value_list;
value_list++;
current_value_list->next = value_list;
} else {
value_list->next = NULL;
}
count--;
}
}
}
/**
* @brief Encode one BACnetPropertyValue value
*
* BACnetPropertyValue ::= SEQUENCE {
* property-identifier [0] BACnetPropertyIdentifier,
* property-array-index [1] Unsigned OPTIONAL,
* -- used only with array datatypes
* -- if omitted with an array the entire array is referenced
* property-value [2] ABSTRACT-SYNTAX.&Type,
* -- any datatype appropriate for the specified property
* priority [3] Unsigned (1..16) OPTIONAL
* -- used only when property is commandable
* }
*
* @param apdu Pointer to the buffer for encoded values, or NULL for length
* @param value Pointer to the service data used for encoding values
*
* @return Bytes encoded or zero on error.
*/
int bacapp_property_value_encode(uint8_t *apdu, BACNET_PROPERTY_VALUE *value)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
BACNET_APPLICATION_DATA_VALUE *app_data = NULL;
if (value) {
/* tag 0 - propertyIdentifier */
len = encode_context_enumerated(apdu, 0, value->propertyIdentifier);
apdu_len += len;
if (apdu) {
apdu += len;
}
/* tag 1 - propertyArrayIndex OPTIONAL */
if (value->propertyArrayIndex != BACNET_ARRAY_ALL) {
len = encode_context_unsigned(apdu, 1, value->propertyArrayIndex);
apdu_len += len;
if (apdu) {
apdu += len;
}
}
/* tag 2 - value */
/* abstract syntax gets enclosed in a context tag */
len = encode_opening_tag(apdu, 2);
apdu_len += len;
if (apdu) {
apdu += len;
}
app_data = &value->value;
while (app_data != NULL) {
len = bacapp_encode_application_data(apdu, app_data);
apdu_len += len;
if (apdu) {
apdu += len;
}
app_data = app_data->next;
}
len = encode_closing_tag(apdu, 2);
apdu_len += len;
if (apdu) {
apdu += len;
}
/* tag 3 - priority OPTIONAL */
if (value->priority != BACNET_NO_PRIORITY) {
len = encode_context_unsigned(apdu, 3, value->priority);
apdu_len += len;
}
}
return apdu_len;
}
/**
* @brief Decode one BACnetPropertyValue value
*
* BACnetPropertyValue ::= SEQUENCE {
* property-identifier [0] BACnetPropertyIdentifier,
* property-array-index [1] Unsigned OPTIONAL,
* -- used only with array datatypes
* -- if omitted with an array the entire array is referenced
* property-value [2] ABSTRACT-SYNTAX.&Type,
* -- any datatype appropriate for the specified property
* priority [3] Unsigned (1..16) OPTIONAL
* -- used only when property is commandable
* }
*
* @param apdu Pointer to the buffer of encoded value
* @param apdu_size Size of the buffer holding the encode value
* @param value Pointer to the service data used for encoding values
*
* @return Bytes decoded or BACNET_STATUS_ERROR on error.
*/
int bacapp_property_value_decode(
uint8_t *apdu, uint32_t apdu_size, BACNET_PROPERTY_VALUE *value)
{
int len = 0;
int apdu_len = 0;
int tag_len = 0;
uint32_t enumerated_value = 0;
BACNET_UNSIGNED_INTEGER unsigned_value = 0;
BACNET_PROPERTY_ID property_identifier = PROP_ALL;
BACNET_APPLICATION_DATA_VALUE *app_data = NULL;
/* property-identifier [0] BACnetPropertyIdentifier */
len = bacnet_enumerated_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 0, &enumerated_value);
if (len == BACNET_STATUS_ERROR) {
return BACNET_STATUS_ERROR;
}
property_identifier = enumerated_value;
if (value) {
value->propertyIdentifier = property_identifier;
}
apdu_len += len;
/* property-array-index [1] Unsigned OPTIONAL */
if (bacnet_is_context_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, 1, NULL)) {
len = bacnet_unsigned_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 1, &unsigned_value);
if (len == BACNET_STATUS_ERROR) {
return BACNET_STATUS_ERROR;
} else if (unsigned_value > UINT32_MAX) {
return BACNET_STATUS_ERROR;
} else {
apdu_len += len;
if (value) {
value->propertyArrayIndex = unsigned_value;
}
}
} else {
if (value) {
value->propertyArrayIndex = BACNET_ARRAY_ALL;
}
}
/* property-value [2] ABSTRACT-SYNTAX.&Type */
if (bacnet_is_opening_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, 2, &len)) {
if (value) {
apdu_len += len;
app_data = &value->value;
while (app_data != NULL) {
len = bacapp_decode_application_data(
&apdu[apdu_len], apdu_size - apdu_len, app_data);
if (len < 0) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
if (bacnet_is_closing_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, 2, &len)) {
break;
}
app_data = app_data->next;
}
} else {
/* this len function needs to start at the opening tag
to match opening/closing tags like a stack.
However, it returns the len between the tags.
Therefore, store the length of the opening tag first */
tag_len = len;
len = bacapp_data_len(&apdu[apdu_len], apdu_size - apdu_len,
(BACNET_PROPERTY_ID)property_identifier);
apdu_len += len;
/* add the opening tag length to the totals */
apdu_len += tag_len;
}
if (bacnet_is_closing_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, 2, &len)) {
apdu_len += len;
} else {
return BACNET_STATUS_ERROR;
}
} else {
return BACNET_STATUS_ERROR;
}
/* priority [3] Unsigned (1..16) OPTIONAL */
if (bacnet_is_context_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, 3, NULL)) {
len = bacnet_unsigned_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 3, &unsigned_value);
if (len == BACNET_STATUS_ERROR) {
return BACNET_STATUS_ERROR;
} else if (unsigned_value > UINT8_MAX) {
return BACNET_STATUS_ERROR;
} else {
apdu_len += len;
if (value) {
value->priority = unsigned_value;
}
}
} else {
if (value) {
value->priority = BACNET_NO_PRIORITY;
}
}
return apdu_len;
}
/* generic - can be used by other unit tests
returns true if matching or same, false if different */
bool bacapp_same_value(BACNET_APPLICATION_DATA_VALUE *value,
@@ -2922,7 +3150,8 @@ bool bacapp_same_value(BACNET_APPLICATION_DATA_VALUE *value,
#endif
#if defined(BACAPP_DOUBLE)
case BACNET_APPLICATION_TAG_DOUBLE:
if (!islessgreater(test_value->type.Double,value->type.Double)) {
if (!islessgreater(
test_value->type.Double, value->type.Double)) {
status = true;
}
break;
+15
View File
@@ -155,6 +155,21 @@ extern "C" {
BACNET_PROPERTY_VALUE *value,
size_t count);
BACNET_STACK_EXPORT
void bacapp_property_value_list_link(
BACNET_PROPERTY_VALUE *value_list,
size_t count);
BACNET_STACK_EXPORT
int bacapp_property_value_encode(
uint8_t *apdu,
BACNET_PROPERTY_VALUE *value);
BACNET_STACK_EXPORT
int bacapp_property_value_decode(
uint8_t *apdu,
uint32_t apdu_size,
BACNET_PROPERTY_VALUE *value);
BACNET_STACK_EXPORT
int bacapp_encode_data(
uint8_t * apdu,
+153
View File
@@ -696,6 +696,39 @@ bool decode_is_context_tag_with_length(
return (bool)(IS_CONTEXT_SPECIFIC(*apdu) && (my_tag_number == tag_number));
}
/**
* @brief Returns true if the tag is context specific
* and matches, as defined in clause 20.2.1.3.2 Constructed
* Data. This function returns the tag length as well.
*
* @param apdu Pointer to the tag begin.
* @param apdu_size - number of bytes in the buffer
* @param tag_number Tag number, that has been decoded before.
* @param tag_length Pointer to a variable, or NULL.
* Returns the length of the tag in bytes if not NULL.
*
* @return true on a match, false otherwise.
*/
bool bacnet_is_context_tag_number(
uint8_t *apdu, uint32_t apdu_size, uint8_t tag_number, int *tag_length)
{
bool match = false;
uint8_t my_tag_number = 0;
int len;
len = bacnet_tag_number_decode(apdu, apdu_size, &my_tag_number);
if ((len > 0) && (my_tag_number == tag_number)) {
if (IS_CONTEXT_SPECIFIC(apdu[0])) {
if (tag_length) {
*tag_length = len;
}
match = true;
}
}
return match;
}
/**
* @brief Returns true if the tag does match and it
* is an opening tag as well.
@@ -715,6 +748,39 @@ bool decode_is_opening_tag_number(uint8_t *apdu, uint8_t tag_number)
return (bool)(IS_OPENING_TAG(apdu[0]) && (my_tag_number == tag_number));
}
/**
* @brief Returns true if the tag does match and it
* is an opening tag as well.
* As defined in clause 20.2.1.3.2 Constructed Data.
*
* @param apdu Pointer to the tag begin.
* @param apdu_size - number of bytes in the buffer
* @param tag_number Tag number, that has been decoded before.
* @param tag_length Pointer to a variable, or NULL.
* Returns the length of the tag in bytes if not NULL.
*
* @return true on a match, false otherwise.
*/
bool bacnet_is_opening_tag_number(
uint8_t *apdu, uint32_t apdu_size, uint8_t tag_number, int *tag_length)
{
bool match = false;
uint8_t my_tag_number = 0;
int len;
len = bacnet_tag_number_decode(apdu, apdu_size, &my_tag_number);
if ((len > 0) && (my_tag_number == tag_number)) {
if (IS_OPENING_TAG(apdu[0])) {
match = true;
if (tag_length) {
*tag_length = len;
}
}
}
return match;
}
/**
* @brief Returns true if the tag does match and it
* is an closing tag as well.
@@ -733,6 +799,39 @@ bool decode_is_closing_tag_number(uint8_t *apdu, uint8_t tag_number)
return (bool)(IS_CLOSING_TAG(apdu[0]) && (my_tag_number == tag_number));
}
/**
* @brief Returns true if the tag does match and it
* is a closing tag as well.
* As defined in clause 20.2.1.3.2 Constructed Data.
*
* @param apdu Pointer to the tag begin.
* @param apdu_size - number of bytes in the buffer
* @param tag_number Tag number, that has been decoded before.
* @param tag_length Pointer to a variable, or NULL.
* Returns the length of the tag in bytes if not NULL.
*
* @return true on a match, false otherwise.
*/
bool bacnet_is_closing_tag_number(
uint8_t *apdu, uint32_t apdu_size, uint8_t tag_number, int *tag_length)
{
bool match = false;
uint8_t my_tag_number = 0;
int len;
len = bacnet_tag_number_decode(apdu, apdu_size, &my_tag_number);
if ((len > 0) && (my_tag_number == tag_number)) {
if (IS_CLOSING_TAG(apdu[0])) {
match = true;
if (tag_length) {
*tag_length = len;
}
}
}
return match;
}
/**
* @brief Encode an boolean value.
* From clause 20.2.3 Encoding of a Boolean Value
@@ -852,6 +951,60 @@ bool decode_boolean(uint32_t len_value)
return boolean_value;
}
/**
* @brief Decode the Boolean Value when context encoded
* From clause 20.2.3 Encoding of a Boolean Value
* and 20.2.1 General Rules for Encoding BACnet Tags
*
* @note The Boolean datatype differs from the other datatypes
* in that the encoding of a context-tagged Boolean value is not the
* same as the encoding of an application-tagged Boolean value.
* This is done so that the application-tagged value may be encoded
* in a single octet, without a contents octet. While this same encoding
* could have been used for the context-tagged case, doing
* so would require that the context be known in order to distinguish
* between a length or a value in the length/value/type field.
* This was considered to be undesirable.
*
* @param apdu - buffer to hold the bytes
* @param apdu_size - number of bytes in the buffer to decode
* @param tag_value - context tag number expected
* @param boolean_value - decoded Boolean Value, if decoded
*
* @return number of bytes decoded, zero if wrong tag number,
* or #BACNET_STATUS_ERROR (-1) if malformed
*/
int bacnet_boolean_context_decode(
uint8_t *apdu, uint32_t apdu_size, uint8_t tag_value, bool *boolean_value)
{
int apdu_len = 0;
int len = 0;
if (bacnet_is_context_tag_number(
&apdu[apdu_len], apdu_size, tag_value, &len) &&
!bacnet_is_closing_tag(&apdu[apdu_len], apdu_size)) {
if (len > 0) {
apdu_len += len;
if (apdu_len < apdu_size) {
if (boolean_value) {
if (apdu[apdu_len]) {
*boolean_value = true;
} else {
*boolean_value = false;
}
}
apdu_len++;
} else {
return BACNET_STATUS_ERROR;
}
} else {
return BACNET_STATUS_ERROR;
}
}
return apdu_len;
}
/**
* @brief Encode a Null value.
* From clause 20.2.2 Encoding of a Null Value
+14
View File
@@ -62,6 +62,9 @@ extern "C" {
uint32_t len_value_type);
BACNET_STACK_EXPORT
bool bacnet_is_context_tag_number(
uint8_t *apdu, uint32_t apdu_size, uint8_t tag_number, int *tag_length);
BACNET_STACK_EXPORT
bool bacnet_is_opening_tag(uint8_t *apdu, uint32_t apdu_size);
BACNET_STACK_EXPORT
bool bacnet_is_closing_tag(uint8_t *apdu, uint32_t apdu_size);
@@ -96,6 +99,12 @@ bool bacnet_is_context_specific(uint8_t *apdu, uint32_t apdu_size);
uint32_t * value);
BACNET_STACK_EXPORT
bool bacnet_is_opening_tag_number(
uint8_t *apdu, uint32_t apdu_size, uint8_t tag_number, int *tag_length);
BACNET_STACK_EXPORT
bool bacnet_is_closing_tag_number(
uint8_t *apdu, uint32_t apdu_size, uint8_t tag_number, int *tag_length);
BACNET_STACK_EXPORT
int bacnet_tag_number_and_value_decode(
uint8_t * apdu,
uint32_t apdu_len_remaining,
@@ -162,6 +171,11 @@ bool bacnet_is_context_specific(uint8_t *apdu, uint32_t apdu_size);
uint8_t tag_number,
bool * boolean_value);
BACNET_STACK_EXPORT
int bacnet_boolean_context_decode(
uint8_t *apdu, uint32_t apdu_size, uint8_t tag_value,
bool *boolean_value);
/* from clause 20.2.10 Encoding of a Bit String Value */
/* returns the number of apdu bytes consumed */
BACNET_STACK_EXPORT
+8 -2
View File
@@ -1687,10 +1687,15 @@ typedef enum {
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED = 4,
ABORT_REASON_SECURITY_ERROR = 5,
ABORT_REASON_INSUFFICIENT_SECURITY = 6,
ABORT_REASON_WINDOW_SIZE_OUT_OF_RANGE = 7,
ABORT_REASON_APPLICATION_EXCEEDED_REPLY_TIME = 8,
ABORT_REASON_OUT_OF_RESOURCES = 9,
ABORT_REASON_TSM_TIMEOUT = 10,
ABORT_REASON_APDU_TOO_LONG = 11,
/* Enumerated values 0-63 are reserved for definition by ASHRAE. */
/* Enumerated values 64-255 may be used by others subject to */
/* the procedures and constraints described in Clause 23. */
MAX_BACNET_ABORT_REASON = 7,
MAX_BACNET_ABORT_REASON = 12,
ABORT_REASON_PROPRIETARY_FIRST = 64,
ABORT_REASON_PROPRIETARY_LAST = 255
} BACNET_ABORT_REASON;
@@ -1706,10 +1711,11 @@ typedef enum {
REJECT_REASON_TOO_MANY_ARGUMENTS = 7,
REJECT_REASON_UNDEFINED_ENUMERATION = 8,
REJECT_REASON_UNRECOGNIZED_SERVICE = 9,
REJECT_REASON_INVALID_DATA_ENCODING = 10,
/* Enumerated values 0-63 are reserved for definition by ASHRAE. */
/* Enumerated values 64-255 may be used by others subject to */
/* the procedures and constraints described in Clause 23. */
MAX_BACNET_REJECT_REASON = 10,
MAX_BACNET_REJECT_REASON = 11,
REJECT_REASON_PROPRIETARY_FIRST = 64,
REJECT_REASON_PROPRIETARY_LAST = 255
} BACNET_REJECT_REASON;
+9 -1
View File
@@ -1074,6 +1074,7 @@ INDTEXT_DATA bacnet_reject_reason_names[] = { { REJECT_REASON_OTHER, "Other" },
{ REJECT_REASON_TOO_MANY_ARGUMENTS, "Too Many Arguments" },
{ REJECT_REASON_UNDEFINED_ENUMERATION, "Undefined Enumeration" },
{ REJECT_REASON_UNRECOGNIZED_SERVICE, "Unrecognized Service" },
{ REJECT_REASON_INVALID_DATA_ENCODING, "invalid-data-encoding" },
{ REJECT_REASON_PROPRIETARY_FIRST, "Proprietary" }, { 0, NULL } };
const char *bactext_reject_reason_name(unsigned index)
@@ -1091,6 +1092,12 @@ INDTEXT_DATA bacnet_abort_reason_names[] = { { ABORT_REASON_OTHER, "Other" },
{ ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, "Segmentation Not Supported" },
{ ABORT_REASON_SECURITY_ERROR, "Security Error" },
{ ABORT_REASON_INSUFFICIENT_SECURITY, "Insufficient Security" },
{ ABORT_REASON_WINDOW_SIZE_OUT_OF_RANGE, "window-size-out-of-range" },
{ ABORT_REASON_APPLICATION_EXCEEDED_REPLY_TIME,
"application-exceeded-reply-time" },
{ ABORT_REASON_OUT_OF_RESOURCES, "out-of-resources" },
{ ABORT_REASON_TSM_TIMEOUT, "tsm-timeout" },
{ ABORT_REASON_APDU_TOO_LONG, "apdu-too-long" },
{ ABORT_REASON_PROPRIETARY_FIRST, "Proprietary" }, { 0, NULL } };
const char *bactext_abort_reason_name(unsigned index)
@@ -1104,7 +1111,8 @@ INDTEXT_DATA bacnet_error_class_names[] = { { ERROR_CLASS_DEVICE, "device" },
{ ERROR_CLASS_OBJECT, "object" }, { ERROR_CLASS_PROPERTY, "property" },
{ ERROR_CLASS_RESOURCES, "resources" },
{ ERROR_CLASS_SECURITY, "security" }, { ERROR_CLASS_SERVICES, "services" },
{ ERROR_CLASS_VT, "vt" }, { 0, NULL } };
{ ERROR_CLASS_VT, "vt" }, { ERROR_CLASS_COMMUNICATION, "communication" },
{ 0, NULL } };
const char *bactext_error_class_name(unsigned index)
{
+18 -5
View File
@@ -1196,15 +1196,24 @@ void Analog_Output_Write_Present_Value_Callback_Set(
/**
* @brief Creates a Analog Value object
* @param object_instance - object-instance number of the object
* @return true if the object-instance was created
* @return the object-instance that was created, or BACNET_MAX_INSTANCE
*/
bool Analog_Output_Create(uint32_t object_instance)
uint32_t Analog_Output_Create(uint32_t object_instance)
{
bool status = false;
struct object_data *pObject = NULL;
int index = 0;
unsigned priority = 0;
if (object_instance > BACNET_MAX_INSTANCE) {
return BACNET_MAX_INSTANCE;
} else if (object_instance == BACNET_MAX_INSTANCE) {
/* wildcard instance */
/* the Object_Identifier property of the newly created object
shall be initialized to a value that is unique within the
responding BACnet-user device. The method used to generate
the object identifier is a local matter.*/
object_instance = Keylist_Next_Empty_Key(Object_List, 1);
}
pObject = Keylist_Data(Object_List, object_instance);
if (!pObject) {
pObject = calloc(1, sizeof(struct object_data));
@@ -1227,13 +1236,17 @@ bool Analog_Output_Create(uint32_t object_instance)
/* add to list */
index = Keylist_Data_Add(Object_List, object_instance, pObject);
if (index >= 0) {
status = true;
Device_Inc_Database_Revision();
} else {
free(pObject);
return BACNET_MAX_INSTANCE;
}
} else {
return BACNET_MAX_INSTANCE;
}
}
return status;
return object_instance;
}
/**
+1 -1
View File
@@ -191,7 +191,7 @@ extern "C" {
BACNET_WRITE_PROPERTY_DATA * wp_data);
BACNET_STACK_EXPORT
bool Analog_Output_Create(
uint32_t Analog_Output_Create(
uint32_t object_instance);
BACNET_STACK_EXPORT
bool Analog_Output_Delete(
+19 -6
View File
@@ -977,16 +977,25 @@ bool bacfile_read_ack_record_data(
/**
* @brief Creates an object
* @brief Creates a File object
* @param object_instance - object-instance number of the object
* @return true if the object-instance was created
* @return the object-instance that was created, or BACNET_MAX_INSTANCE
*/
bool bacfile_create(uint32_t object_instance)
uint32_t bacfile_create(uint32_t object_instance)
{
bool status = false;
struct object_data *pObject = NULL;
int index = 0;
if (object_instance > BACNET_MAX_INSTANCE) {
return BACNET_MAX_INSTANCE;
} else if (object_instance == BACNET_MAX_INSTANCE) {
/* wildcard instance */
/* the Object_Identifier property of the newly created object
shall be initialized to a value that is unique within the
responding BACnet-user device. The method used to generate
the object identifier is a local matter.*/
object_instance = Keylist_Next_Empty_Key(Object_List, 1);
}
pObject = Keylist_Data(Object_List, object_instance);
if (!pObject) {
pObject = calloc(1, sizeof(struct object_data));
@@ -1002,13 +1011,17 @@ bool bacfile_create(uint32_t object_instance)
/* add to list */
index = Keylist_Data_Add(Object_List, object_instance, pObject);
if (index >= 0) {
status = true;
Device_Inc_Database_Revision();
} else {
free(pObject);
return BACNET_MAX_INSTANCE;
}
} else {
return BACNET_MAX_INSTANCE;
}
}
return status;
return object_instance;
}
/**
+1 -1
View File
@@ -175,7 +175,7 @@ extern "C" {
uint32_t buffer_size);
BACNET_STACK_EXPORT
bool bacfile_create(
uint32_t bacfile_create(
uint32_t object_instance);
BACNET_STACK_EXPORT
bool bacfile_delete(
+19 -6
View File
@@ -1170,16 +1170,25 @@ void Binary_Output_Write_Present_Value_Callback_Set(
}
/**
* @brief Determines a object write-enabled flag state
* @brief Creates a Binary Output object
* @param object_instance - object-instance number of the object
* @return write-enabled status flag
* @return the object-instance that was created, or BACNET_MAX_INSTANCE
*/
bool Binary_Output_Create(uint32_t object_instance)
uint32_t Binary_Output_Create(uint32_t object_instance)
{
bool status = false;
struct object_data *pObject = NULL;
int index = 0;
if (object_instance > BACNET_MAX_INSTANCE) {
return BACNET_MAX_INSTANCE;
} else if (object_instance == BACNET_MAX_INSTANCE) {
/* wildcard instance */
/* the Object_Identifier property of the newly created object
shall be initialized to a value that is unique within the
responding BACnet-user device. The method used to generate
the object identifier is a local matter.*/
object_instance = Keylist_Next_Empty_Key(Object_List, 1);
}
pObject = Keylist_Data(Object_List, object_instance);
if (!pObject) {
pObject = calloc(1, sizeof(struct object_data));
@@ -1194,13 +1203,17 @@ bool Binary_Output_Create(uint32_t object_instance)
/* add to list */
index = Keylist_Data_Add(Object_List, object_instance, pObject);
if (index >= 0) {
status = true;
Device_Inc_Database_Revision();
} else {
free(pObject);
return BACNET_MAX_INSTANCE;
}
} else {
return BACNET_MAX_INSTANCE;
}
}
return status;
return object_instance;
}
/**
+1 -1
View File
@@ -228,7 +228,7 @@ extern "C" {
BACNET_WRITE_PROPERTY_DATA * wp_data);
BACNET_STACK_EXPORT
bool Binary_Output_Create(
uint32_t Binary_Output_Create(
uint32_t object_instance);
BACNET_STACK_EXPORT
bool Binary_Output_Delete(
@@ -117,7 +117,8 @@ static object_functions_t Object_Table[] = {
Device_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */,
NULL /* Value_Lists */, NULL /* COV */, NULL /* COV Clear */,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
#if (BACNET_PROTOCOL_REVISION >= 17)
{ OBJECT_NETWORK_PORT, Network_Port_Init, Network_Port_Count,
Network_Port_Index_To_Instance, Network_Port_Valid_Instance,
@@ -125,7 +126,8 @@ static object_functions_t Object_Table[] = {
Network_Port_Write_Property, Network_Port_Property_Lists,
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
#endif
{ MAX_BACNET_OBJECT_TYPE, NULL /* Init */, NULL /* Count */,
NULL /* Index_To_Instance */, NULL /* Valid_Instance */,
@@ -134,7 +136,8 @@ static object_functions_t Object_Table[] = {
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
NULL /* COV */, NULL /* COV Clear */,
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
};
/** Glue function to let the Device object, when called by a handler,
+19 -5
View File
@@ -827,15 +827,25 @@ void Color_Write_Disable(uint32_t object_instance)
}
/**
* Creates a Color object
* @brief Creates a Color object
* @param object_instance - object-instance number of the object
* @return the object-instance that was created, or BACNET_MAX_INSTANCE
*/
bool Color_Create(uint32_t object_instance)
uint32_t Color_Create(uint32_t object_instance)
{
bool status = false;
struct object_data *pObject = NULL;
int index = 0;
if (object_instance > BACNET_MAX_INSTANCE) {
return BACNET_MAX_INSTANCE;
} else if (object_instance == BACNET_MAX_INSTANCE) {
/* wildcard instance */
/* the Object_Identifier property of the newly created object
shall be initialized to a value that is unique within the
responding BACnet-user device. The method used to generate
the object identifier is a local matter.*/
object_instance = Keylist_Next_Empty_Key(Object_List, 1);
}
pObject = Keylist_Data(Object_List, object_instance);
if (!pObject) {
pObject = calloc(1, sizeof(struct object_data));
@@ -856,13 +866,17 @@ bool Color_Create(uint32_t object_instance)
/* add to list */
index = Keylist_Data_Add(Object_List, object_instance, pObject);
if (index >= 0) {
status = true;
Device_Inc_Database_Revision();
} else {
free(pObject);
return BACNET_MAX_INSTANCE;
}
} else {
return BACNET_MAX_INSTANCE;
}
}
return status;
return object_instance;
}
/**
+1 -1
View File
@@ -119,7 +119,7 @@ BACNET_STACK_EXPORT
void Color_Write_Disable(uint32_t instance);
BACNET_STACK_EXPORT
bool Color_Create(uint32_t object_instance);
uint32_t Color_Create(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Color_Delete(uint32_t object_instance);
BACNET_STACK_EXPORT
+19 -5
View File
@@ -1021,15 +1021,25 @@ void Color_Temperature_Write_Disable(uint32_t object_instance)
}
/**
* Creates a Color object
* @brief Creates a Color Temperature object
* @param object_instance - object-instance number of the object
* @return the object-instance that was created, or BACNET_MAX_INSTANCE
*/
bool Color_Temperature_Create(uint32_t object_instance)
uint32_t Color_Temperature_Create(uint32_t object_instance)
{
bool status = false;
struct object_data *pObject = NULL;
int index = 0;
if (object_instance > BACNET_MAX_INSTANCE) {
return BACNET_MAX_INSTANCE;
} else if (object_instance == BACNET_MAX_INSTANCE) {
/* wildcard instance */
/* the Object_Identifier property of the newly created object
shall be initialized to a value that is unique within the
responding BACnet-user device. The method used to generate
the object identifier is a local matter.*/
object_instance = Keylist_Next_Empty_Key(Object_List, 1);
}
pObject = Keylist_Data(Object_List, object_instance);
if (!pObject) {
pObject = calloc(1, sizeof(struct object_data));
@@ -1051,13 +1061,17 @@ bool Color_Temperature_Create(uint32_t object_instance)
/* add to list */
index = Keylist_Data_Add(Object_List, object_instance, pObject);
if (index >= 0) {
status = true;
Device_Inc_Database_Revision();
} else {
free(pObject);
return BACNET_MAX_INSTANCE;
}
} else {
return BACNET_MAX_INSTANCE;
}
}
return status;
return object_instance;
}
/**
+1 -1
View File
@@ -151,7 +151,7 @@ BACNET_STACK_EXPORT
void Color_Temperature_Write_Disable(uint32_t instance);
BACNET_STACK_EXPORT
bool Color_Temperature_Create(uint32_t object_instance);
uint32_t Color_Temperature_Create(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Color_Temperature_Delete(uint32_t object_instance);
BACNET_STACK_EXPORT
+165 -39
View File
@@ -98,7 +98,8 @@ static object_functions_t My_Object_Table[] = {
Device_Property_Lists, DeviceGetRRInfo, NULL /* Iterator */,
NULL /* Value_Lists */, NULL /* COV */, NULL /* COV Clear */,
NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */,
NULL /* Remove_List_Element */ },
NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
#if (BACNET_PROTOCOL_REVISION >= 17)
{ OBJECT_NETWORK_PORT, Network_Port_Init, Network_Port_Count,
Network_Port_Index_To_Instance, Network_Port_Valid_Instance,
@@ -106,7 +107,8 @@ static object_functions_t My_Object_Table[] = {
Network_Port_Write_Property, Network_Port_Property_Lists,
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
#endif
{ OBJECT_ANALOG_INPUT, Analog_Input_Init, Analog_Input_Count,
Analog_Input_Index_To_Instance, Analog_Input_Valid_Instance,
@@ -115,14 +117,16 @@ static object_functions_t My_Object_Table[] = {
NULL /* ReadRangeInfo */, NULL /* Iterator */,
Analog_Input_Encode_Value_List, Analog_Input_Change_Of_Value,
Analog_Input_Change_Of_Value_Clear, Analog_Input_Intrinsic_Reporting,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
{ 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 /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
Analog_Output_Create, Analog_Output_Delete},
{ 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,
@@ -130,7 +134,8 @@ static object_functions_t My_Object_Table[] = {
NULL /* ReadRangeInfo */, NULL /* Iterator */,
Analog_Value_Encode_Value_List, Analog_Value_Change_Of_Value,
Analog_Value_Change_Of_Value_Clear, Analog_Value_Intrinsic_Reporting,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
{ 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,
@@ -138,21 +143,24 @@ static object_functions_t My_Object_Table[] = {
NULL /* ReadRangeInfo */, NULL /* Iterator */,
Binary_Input_Encode_Value_List, Binary_Input_Change_Of_Value,
Binary_Input_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
{ 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 /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
Binary_Output_Create, Binary_Output_Delete},
{ 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 /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
{ OBJECT_CHARACTERSTRING_VALUE, CharacterString_Value_Init,
CharacterString_Value_Count, CharacterString_Value_Index_To_Instance,
CharacterString_Value_Valid_Instance, CharacterString_Value_Object_Name,
@@ -163,20 +171,23 @@ static object_functions_t My_Object_Table[] = {
CharacterString_Value_Change_Of_Value,
CharacterString_Value_Change_Of_Value_Clear,
NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */,
NULL /* Remove_List_Element */ },
NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
{ 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 /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
{ 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 /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
#if defined(INTRINSIC_REPORTING)
{ OBJECT_NOTIFICATION_CLASS, Notification_Class_Init,
Notification_Class_Count, Notification_Class_Index_To_Instance,
@@ -186,7 +197,8 @@ static object_functions_t My_Object_Table[] = {
NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */,
NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
Notification_Class_Add_List_Element,
Notification_Class_Remove_List_Element },
Notification_Class_Remove_List_Element,
NULL /* Create */, NULL /* Delete */ },
#endif
{ OBJECT_LIFE_SAFETY_POINT, Life_Safety_Point_Init, Life_Safety_Point_Count,
Life_Safety_Point_Index_To_Instance, Life_Safety_Point_Valid_Instance,
@@ -194,21 +206,24 @@ static object_functions_t My_Object_Table[] = {
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 /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
{ 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 /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
{ 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 /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
{ OBJECT_MULTI_STATE_OUTPUT, Multistate_Output_Init,
Multistate_Output_Count, Multistate_Output_Index_To_Instance,
Multistate_Output_Valid_Instance, Multistate_Output_Object_Name,
@@ -216,7 +231,8 @@ static object_functions_t My_Object_Table[] = {
Multistate_Output_Property_Lists, NULL /* ReadRangeInfo */,
NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */,
NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
Multistate_Output_Create, Multistate_Output_Delete},
{ 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,
@@ -224,14 +240,16 @@ static object_functions_t My_Object_Table[] = {
NULL /* ReadRangeInfo */, NULL /* Iterator */,
Multistate_Value_Encode_Value_List, Multistate_Value_Change_Of_Value,
Multistate_Value_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
{ 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 /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
#if (BACNET_PROTOCOL_REVISION >= 14)
{ OBJECT_LIGHTING_OUTPUT, Lighting_Output_Init, Lighting_Output_Count,
Lighting_Output_Index_To_Instance, Lighting_Output_Valid_Instance,
@@ -239,13 +257,15 @@ static object_functions_t My_Object_Table[] = {
Lighting_Output_Write_Property, Lighting_Output_Property_Lists,
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
{ 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 /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
#endif
#if (BACNET_PROTOCOL_REVISION >= 24)
{ OBJECT_COLOR, Color_Init, Color_Count, Color_Index_To_Instance,
@@ -253,14 +273,16 @@ static object_functions_t My_Object_Table[] = {
Color_Write_Property, Color_Property_Lists, NULL /* ReadRangeInfo */,
NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */,
NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
Color_Create, Color_Delete},
{ 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 /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
Color_Temperature_Create, Color_Temperature_Delete},
#endif
#if defined(BACFILE)
{ OBJECT_FILE, bacfile_init, bacfile_count, bacfile_index_to_instance,
@@ -268,7 +290,8 @@ static object_functions_t My_Object_Table[] = {
bacfile_write_property, BACfile_Property_Lists,
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
bacfile_create, bacfile_delete},
#endif
{ OBJECT_OCTETSTRING_VALUE, OctetString_Value_Init, OctetString_Value_Count,
OctetString_Value_Index_To_Instance, OctetString_Value_Valid_Instance,
@@ -276,7 +299,8 @@ static object_functions_t My_Object_Table[] = {
OctetString_Value_Write_Property, OctetString_Value_Property_Lists,
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
{ OBJECT_POSITIVE_INTEGER_VALUE, PositiveInteger_Value_Init,
PositiveInteger_Value_Count, PositiveInteger_Value_Index_To_Instance,
PositiveInteger_Value_Valid_Instance, PositiveInteger_Value_Object_Name,
@@ -285,28 +309,32 @@ static object_functions_t My_Object_Table[] = {
PositiveInteger_Value_Property_Lists, NULL /* ReadRangeInfo */,
NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */,
NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
{ 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 /* Add_List_Element */,
NULL /* Remove_List_Element */ },
NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
{ 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 /* Add_List_Element */, NULL /* Remove_List_Element */ },
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
{ 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 /* Add_List_Element */, NULL /* Remove_List_Element */ }
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */ },
};
/** Glue function to let the Device object, when called by a handler,
@@ -1892,6 +1920,108 @@ void Device_COV_Clear(BACNET_OBJECT_TYPE object_type, uint32_t object_instance)
}
}
/**
* @brief Creates a child object, if supported
* @ingroup ObjHelpers
* @param data - CreateObject data, including error codes if failures
* @return true if object has been created
*/
bool Device_Create_Object(
BACNET_CREATE_OBJECT_DATA *data)
{
bool status = false;
struct object_functions *pObject = NULL;
uint32_t object_instance;
pObject = Device_Objects_Find_Functions(data->object_type);
if (pObject != NULL) {
if (!pObject->Object_Create) {
/* The device supports the object type and may have
sufficient space, but does not support the creation of the
object for some other reason.*/
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_DYNAMIC_CREATION_NOT_SUPPORTED;
} else if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(data->object_instance)) {
/* The object being created already exists */
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_OBJECT_IDENTIFIER_ALREADY_EXISTS;
} else {
if (data->list_of_initial_values) {
/* FIXME: add support for writing to list of initial values */
/* A property specified by the Property_Identifier in the
List of Initial Values does not support initialization
during the CreateObject service. */
data->first_failed_element_number = 1;
data->error_class = ERROR_CLASS_PROPERTY;
data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
/* and the object shall not be created */
} else {
object_instance = pObject->Object_Create(data->object_instance);
if (object_instance == BACNET_MAX_INSTANCE) {
/* The device cannot allocate the space needed
for the new object.*/
data->error_class = ERROR_CLASS_RESOURCES;
data->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT;
} else {
/* required by ACK */
data->object_instance = object_instance;
status = true;
}
}
}
} else {
/* The device does not support the specified object type. */
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE;
}
return status;
}
/**
* @brief Deletes a child object, if supported
* @ingroup ObjHelpers
* @param data - DeleteObject data, including error codes if failures
* @return true if object has been deleted
*/
bool Device_Delete_Object(
BACNET_DELETE_OBJECT_DATA *data)
{
bool status = false;
struct object_functions *pObject = NULL;
pObject = Device_Objects_Find_Functions(data->object_type);
if (pObject != NULL) {
if (!pObject->Object_Delete) {
/* The device supports the object type
but does not support the deletion of the
object for some reason.*/
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_OBJECT_DELETION_NOT_PERMITTED;
} else if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(data->object_instance)) {
/* The object being deleted must already exist */
status = pObject->Object_Delete(data->object_instance);
if (!status) {
/* The object exists but cannot be deleted. */
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_OBJECT_DELETION_NOT_PERMITTED;
}
} else {
/* The object to be deleted does not exist. */
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
} else {
/* The device does not support the specified object type. */
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE;
}
return status;
}
#if defined(INTRINSIC_REPORTING)
void Device_local_reporting(void)
{
@@ -1966,17 +2096,13 @@ void Device_Init(object_functions_t *object_table)
pObject++;
}
/* create some dynamically created objects as examples */
Analog_Output_Create(1);
Binary_Output_Create(1);
Multistate_Output_Create(1);
#if (BACNET_PROTOCOL_REVISION >= 24)
Color_Create(1);
Color_Temperature_Create(1);
#endif
#if defined(BACFILE)
bacfile_create(1);
bacfile_pathname_set(1, "temp_1.txt");
#endif
pObject = Object_Table;
while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) {
if (pObject->Object_Create) {
pObject->Object_Create(BACNET_MAX_INSTANCE);
}
pObject++;
}
}
bool DeviceGetRRInfo(BACNET_READ_RANGE_DATA *pRequest, /* Info on the request */
+11
View File
@@ -34,6 +34,8 @@
#include "bacnet/bacnet_stack_exports.h"
#include "bacnet/bacdef.h"
#include "bacnet/bacenum.h"
#include "bacnet/create_object.h"
#include "bacnet/delete_object.h"
#include "bacnet/list_element.h"
#include "bacnet/wp.h"
#include "bacnet/rd.h"
@@ -168,6 +170,8 @@ typedef struct object_functions {
object_intrinsic_reporting_function Object_Intrinsic_Reporting;
list_element_function Object_Add_List_Element;
list_element_function Object_Remove_List_Element;
create_object_function Object_Create;
delete_object_function Object_Delete;
} object_functions_t;
/* String Lengths - excluding any nul terminator */
@@ -316,6 +320,13 @@ extern "C" {
BACNET_ARRAY_INDEX array_index,
uint8_t *apdu);
BACNET_STACK_EXPORT
bool Device_Create_Object(
BACNET_CREATE_OBJECT_DATA *data);
BACNET_STACK_EXPORT
bool Device_Delete_Object(
BACNET_DELETE_OBJECT_DATA *data);
BACNET_STACK_EXPORT
unsigned Device_Count(
void);
+18 -5
View File
@@ -1152,15 +1152,24 @@ void Multistate_Output_Write_Present_Value_Callback_Set(
/**
* @brief Creates a new object and adds it to the object list
* @param object_instance - object-instance number of the object
* @return true if the object is created
* @return the object-instance that was created, or BACNET_MAX_INSTANCE
*/
bool Multistate_Output_Create(uint32_t object_instance)
uint32_t Multistate_Output_Create(uint32_t object_instance)
{
bool status = false;
struct object_data *pObject = NULL;
int index = 0;
unsigned priority = 0;
if (object_instance > BACNET_MAX_INSTANCE) {
return BACNET_MAX_INSTANCE;
} else if (object_instance == BACNET_MAX_INSTANCE) {
/* wildcard instance */
/* the Object_Identifier property of the newly created object
shall be initialized to a value that is unique within the
responding BACnet-user device. The method used to generate
the object identifier is a local matter.*/
object_instance = Keylist_Next_Empty_Key(Object_List, 1);
}
pObject = Keylist_Data(Object_List, object_instance);
if (!pObject) {
pObject = calloc(1, sizeof(struct object_data));
@@ -1178,13 +1187,17 @@ bool Multistate_Output_Create(uint32_t object_instance)
/* add to list */
index = Keylist_Data_Add(Object_List, object_instance, pObject);
if (index >= 0) {
status = true;
Device_Inc_Database_Revision();
} else {
free(pObject);
return BACNET_MAX_INSTANCE;
}
} else {
return BACNET_MAX_INSTANCE;
}
}
return status;
return object_instance;
}
/**
+1 -1
View File
@@ -174,7 +174,7 @@ extern "C" {
uint32_t value);
BACNET_STACK_EXPORT
bool Multistate_Output_Create(
uint32_t Multistate_Output_Create(
uint32_t object_instance);
BACNET_STACK_EXPORT
bool Multistate_Output_Delete(
+124
View File
@@ -0,0 +1,124 @@
/**
* @file
* @brief CreateObject service application handlers
* @author Steve Karg <skarg@users.sourceforge.net>
* @date August 2023
* @section LICENSE
*
* 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/reject.h"
#include "bacnet/create_object.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 CreateObject 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_Create_Object() succeeds
* - an Error if Device_Create_Object() 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_create_object(uint8_t *service_request,
uint16_t service_len,
BACNET_ADDRESS *src,
BACNET_CONFIRMED_SERVICE_DATA *service_data)
{
BACNET_CREATE_OBJECT_DATA data = { 0 };
BACNET_NPDU_DATA npdu_data = { 0 };
BACNET_ADDRESS my_address = { 0 };
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("CreateObject: 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("CreateObject: Segmented message. Sending Abort!\n");
status = false;
}
if (status) {
/* decode the service request only */
len = create_object_decode_service_request(
service_request, service_len, &data);
if (len > 0) {
debug_perror("CreateObject: type=%lu instance=%lu\n",
(unsigned long)data.object_type,
(unsigned long)data.object_instance);
} else {
debug_perror("CreateObject: Unable to decode request!\n");
}
if (len <= 0) {
/* bad decoding or something we didn't understand */
if (len == BACNET_STATUS_ABORT) {
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
abort_convert_error_code(data.error_code), true);
debug_perror("CreateObject: Sending Abort!\n");
} else if (len == BACNET_STATUS_REJECT) {
len = reject_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
reject_convert_error_code(data.error_code));
debug_perror("CreateObject: Sending Reject!\n");
}
} else {
if (Device_Create_Object(&data)) {
len =
create_object_ack_encode(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, &data);
debug_perror("CreateObject: Sending ACK!\n");
} else {
len = create_object_error_ack_encode(
&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, &data);
debug_perror("CreateObject: Sending Error!\n");
}
}
}
if (len > 0) {
/* 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(
"CreateObject: Failed to send PDU (%s)!\n", strerror(errno));
}
return;
}
@@ -0,0 +1,34 @@
/**
* @file
* @brief API for CreateObject service handlers
* @author Steve Karg <skarg@users.sourceforge.net>
* @date August 2023
* @section LICENSE
*
* SPDX-License-Identifier: MIT
*/
#ifndef HANDLER_CREATE_OBJECT_H
#define HANDLER_CREATE_OBJECT_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_create_object(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA *service_data);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+119
View File
@@ -0,0 +1,119 @@
/**
* @file
* @brief DeleteObject service application handlers
* @author Steve Karg <skarg@users.sourceforge.net>
* @date August 2023
* @section LICENSE
*
* 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/reject.h"
#include "bacnet/delete_object.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 DeleteObject 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_Delete_Object() succeeds
* - an Error if Device_Delete_Object() 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_delete_object(uint8_t *service_request,
uint16_t service_len,
BACNET_ADDRESS *src,
BACNET_CONFIRMED_SERVICE_DATA *service_data)
{
BACNET_DELETE_OBJECT_DATA data = { 0 };
BACNET_NPDU_DATA npdu_data = { 0 };
BACNET_ADDRESS my_address = { 0 };
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("DeleteObject: 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("DeleteObject: Segmented message. Sending Abort!\n");
status = false;
}
if (status) {
/* decode the service request only */
len = delete_object_decode_service_request(
service_request, service_len, &data);
if (len > 0) {
debug_perror("DeleteObject: type=%lu instance=%lu\n",
(unsigned long)data.object_type,
(unsigned long)data.object_instance);
} else {
debug_perror("DeleteObject: 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("DeleteObject: Bad Encoding. Sending Abort!\n");
status = false;
}
if (status) {
if (Device_Delete_Object(&data)) {
len = encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_DELETE_OBJECT);
debug_perror("DeleteObject: Sending Simple Ack!\n");
} else {
len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_DELETE_OBJECT,
data.error_class, data.error_code);
debug_perror("DeleteObject: Sending Error!\n");
}
}
}
if (len > 0) {
/* 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(
"DeleteObject: Failed to send PDU (%s)!\n", strerror(errno));
}
return;
}
@@ -0,0 +1,34 @@
/**
* @file
* @brief API for DeleteObject service handlers
* @author Steve Karg <skarg@users.sourceforge.net>
* @date August 2023
* @section LICENSE
*
* SPDX-License-Identifier: MIT
*/
#ifndef HANDLER_DELETE_OBJECT_H
#define HANDLER_DELETE_OBJECT_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_delete_object(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA *service_data);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+135
View File
@@ -0,0 +1,135 @@
/**
* @file
* @brief CreateObject service initiation
* @author Steve Karg <skarg@users.sourceforge.net>
* @date August 2023
* @section LICENSE
*
* 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/create_object.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 CreateObject 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.
* @return invoke id of outgoing message, or 0 on failure.
* @return the invoke ID for confirmed request, or zero on failure
*/
uint8_t Send_Create_Object_Request_Data(
uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_VALUE *values)
{
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_CREATE_OBJECT_DATA data = { 0 };
BACNET_NPDU_DATA npdu_data = { 0 };
uint8_t service = SERVICE_CONFIRMED_CREATE_OBJECT;
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.list_of_initial_values = values;
/* get the length of the APDU */
len = create_object_encode_service_request(NULL, &data);
pdu_len += len;
/* will it fit in the sender and our buffer?
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) &&
(pdu_len < sizeof(Handler_Transmit_Buffer))) {
/* shift back to the service portion of the buffer */
pdu_len -= len;
len = create_object_encode_service_request(
&Handler_Transmit_Buffer[pdu_len], &data);
pdu_len += len;
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 a CreateObject 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.
* @return invoke id of outgoing message, or 0 on failure.
* @return the invoke ID for confirmed request, or zero on failure
*/
uint8_t Send_Create_Object_Request(
uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance)
{
return Send_Create_Object_Request_Data(device_id, object_type, object_instance, NULL);
}
@@ -0,0 +1,43 @@
/**
* @file
* @brief CreateObject service initiation
* @author Steve Karg <skarg@users.sourceforge.net>
* @date August 2023
* @section LICENSE
*
* SPDX-License-Identifier: MIT
*/
#ifndef SEND_CREATE_OBJECT_H
#define SEND_CREATE_OBJECT_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_Create_Object_Request_Data(
uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_VALUE *values);
BACNET_STACK_EXPORT
uint8_t Send_Create_Object_Request(
uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+117
View File
@@ -0,0 +1,117 @@
/**
* @file
* @brief DeleteObject service initiation
* @author Steve Karg <skarg@users.sourceforge.net>
* @date August 2023
* @section LICENSE
*
* 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/delete_object.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 CreateObject 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.
* @return invoke id of outgoing message, or 0 on failure.
* @return the invoke ID for confirmed request, or zero on failure
*/
uint8_t Send_Delete_Object_Request(
uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance)
{
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_DELETE_OBJECT_DATA data = { 0 };
BACNET_NPDU_DATA npdu_data = { 0 };
uint8_t service = SERVICE_CONFIRMED_DELETE_OBJECT;
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;
/* get the length of the APDU */
len = delete_object_encode_service_request(NULL, &data);
pdu_len += len;
/* will it fit in the sender and our buffer?
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) &&
(pdu_len < sizeof(Handler_Transmit_Buffer))) {
/* shift back to the service portion of the buffer */
pdu_len -= len;
len = delete_object_encode_service_request(
&Handler_Transmit_Buffer[pdu_len], &data);
pdu_len += len;
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;
}
@@ -0,0 +1,36 @@
/**
* @file
* @brief DeleteObject service initiation
* @author Steve Karg <skarg@users.sourceforge.net>
* @date August 2023
* @section LICENSE
*
* SPDX-License-Identifier: MIT
*/
#ifndef SEND_DELETE_OBJECT_H
#define SEND_DELETE_OBJECT_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_Delete_Object_Request(
uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+4
View File
@@ -41,7 +41,9 @@
#include "bacnet/basic/service/h_awf.h"
#include "bacnet/basic/service/h_ccov.h"
#include "bacnet/basic/service/h_cov.h"
#include "bacnet/basic/service/h_create_object.h"
#include "bacnet/basic/service/h_dcc.h"
#include "bacnet/basic/service/h_delete_object.h"
#include "bacnet/basic/service/h_gas_a.h"
#include "bacnet/basic/service/h_get_alarm_sum.h"
#include "bacnet/basic/service/h_getevent.h"
@@ -73,7 +75,9 @@
#include "bacnet/basic/service/s_awfs.h"
#include "bacnet/basic/service/s_cevent.h"
#include "bacnet/basic/service/s_cov.h"
#include "bacnet/basic/service/s_create_object.h"
#include "bacnet/basic/service/s_dcc.h"
#include "bacnet/basic/service/s_delete_object.h"
#include "bacnet/basic/service/s_error.h"
#include "bacnet/basic/service/s_get_alarm_sum.h"
#include "bacnet/basic/service/s_get_event.h"
+5
View File
@@ -18,12 +18,17 @@
#ifndef BACNET_SYS_PLATFORM_H
#define BACNET_SYS_PLATFORM_H
#include <stddef.h>
#include <math.h>
#ifndef islessgreater
#define islessgreater(x, y) ((x) < (y) || (x) > (y))
#endif
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(array) ((size_t)(sizeof(array) / sizeof((array)[0])))
#endif
/* marking some code as 'deprecated' */
# if defined(_MSC_VER)
# define BACNET_STACK_DEPRECATED(message) __declspec(deprecated(message))
+238 -243
View File
@@ -50,88 +50,94 @@ Unconfirmed COV Notification
*/
/**
* Encode APDU for notification.
*
* @param apdu Pointer to the buffer.
* @brief Encode APDU for COV Notification.
* @param apdu Pointer to the buffer, or NULL for length
* @param data Pointer to the data to encode.
*
* @return bytes encoded or zero on error.
*/
static int notify_encode_apdu(
uint8_t *apdu, unsigned max_apdu_len, BACNET_COV_DATA *data)
int cov_notify_encode_apdu(uint8_t *apdu, BACNET_COV_DATA *data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
BACNET_PROPERTY_VALUE *value = NULL; /* value in list */
BACNET_APPLICATION_DATA_VALUE *app_data = NULL;
(void)max_apdu_len;
if (apdu) {
/* tag 0 - subscriberProcessIdentifier */
len = encode_context_unsigned(
&apdu[apdu_len], 0, data->subscriberProcessIdentifier);
len =
encode_context_unsigned(apdu, 0, data->subscriberProcessIdentifier);
apdu_len += len;
if (apdu) {
apdu += len;
}
/* tag 1 - initiatingDeviceIdentifier */
len = encode_context_object_id(&apdu[apdu_len], 1, OBJECT_DEVICE,
data->initiatingDeviceIdentifier);
len = encode_context_object_id(
apdu, 1, OBJECT_DEVICE, data->initiatingDeviceIdentifier);
apdu_len += len;
if (apdu) {
apdu += len;
}
/* tag 2 - monitoredObjectIdentifier */
len = encode_context_object_id(&apdu[apdu_len], 2,
len = encode_context_object_id(apdu, 2,
data->monitoredObjectIdentifier.type,
data->monitoredObjectIdentifier.instance);
apdu_len += len;
if (apdu) {
apdu += len;
}
/* tag 3 - timeRemaining */
len = encode_context_unsigned(&apdu[apdu_len], 3, data->timeRemaining);
len = encode_context_unsigned(apdu, 3, data->timeRemaining);
apdu_len += len;
if (apdu) {
apdu += len;
}
/* tag 4 - listOfValues */
len = encode_opening_tag(&apdu[apdu_len], 4);
len = encode_opening_tag(apdu, 4);
apdu_len += len;
if (apdu) {
apdu += len;
}
/* the first value includes a pointer to the next value, etc */
/* FIXME: for small implementations, we might try a partial
approach like the rpm.c where the values are encoded with
a separate function */
value = data->listOfValues;
while (value != NULL) {
/* tag 0 - propertyIdentifier */
len = encode_context_enumerated(
&apdu[apdu_len], 0, value->propertyIdentifier);
len = bacapp_property_value_encode(apdu, value);
apdu_len += len;
/* tag 1 - propertyArrayIndex OPTIONAL */
if (value->propertyArrayIndex != BACNET_ARRAY_ALL) {
len = encode_context_unsigned(
&apdu[apdu_len], 1, value->propertyArrayIndex);
apdu_len += len;
}
/* tag 2 - value */
/* abstract syntax gets enclosed in a context tag */
len = encode_opening_tag(&apdu[apdu_len], 2);
apdu_len += len;
app_data = &value->value;
while (app_data != NULL) {
len = bacapp_encode_application_data(&apdu[apdu_len], app_data);
apdu_len += len;
app_data = app_data->next;
}
len = encode_closing_tag(&apdu[apdu_len], 2);
apdu_len += len;
/* tag 3 - priority OPTIONAL */
if (value->priority != BACNET_NO_PRIORITY) {
len = encode_context_unsigned(
&apdu[apdu_len], 3, value->priority);
apdu_len += len;
if (apdu) {
apdu += len;
}
/* is there another one to encode? */
/* FIXME: check to see if there is room in the APDU */
value = value->next;
}
len = encode_closing_tag(&apdu[apdu_len], 4);
len = encode_closing_tag(apdu, 4);
apdu_len += len;
}
return apdu_len;
}
/**
* Encode APDU for notification.
*
* @param apdu Pointer to the buffer for encoding into
* @param apdu_size number of bytes available in the buffer
* @param data Pointer to the data to encode.
*
* @return bytes encoded or zero if unable to encode
*/
static int notify_encode_apdu(
uint8_t *apdu, unsigned apdu_size, BACNET_COV_DATA *data)
{
int apdu_len = 0; /* total length of the apdu, return value */
apdu_len = cov_notify_encode_apdu(NULL, data);
if (apdu_len > apdu_size) {
apdu_len = 0;
} else {
apdu_len = cov_notify_encode_apdu(apdu, data);
}
return apdu_len;
}
/**
* Encode APDU for confirmed notification.
*
@@ -158,7 +164,7 @@ int ccov_notify_encode_apdu(uint8_t *apdu,
apdu_len = 4;
len =
notify_encode_apdu(&apdu[apdu_len], max_apdu_len - apdu_len, data);
if (len < 0) {
if (len <= 0) {
/* return the error */
apdu_len = len;
} else {
@@ -190,7 +196,7 @@ int ucov_notify_encode_apdu(
apdu_len = 2;
len =
notify_encode_apdu(&apdu[apdu_len], max_apdu_len - apdu_len, data);
if (len < 0) {
if (len <= 0) {
/* return the error */
apdu_len = len;
} else {
@@ -202,164 +208,120 @@ int ucov_notify_encode_apdu(
}
/**
* Decode the COV-service request only.
* Note: COV and Unconfirmed COV are the same.
* @brief Decode the COV-service request only.
*
* ConfirmedCOVNotification-Request ::= SEQUENCE {
* subscriber-process-identifier [0] Unsigned32,
* initiating-device-identifier [1] BACnetObjectIdentifier,
* monitored-object-identifier [2] BACnetObjectIdentifier,
* time-remaining [3] Unsigned,
* list-of-values [4] SEQUENCE OF BACnetPropertyValue
* }
*
* @note: COV and Unconfirmed COV are the same.
* @param apdu Pointer to the buffer.
* @param apdu_len Count of valid bytes in the buffer.
* @param data Pointer to the data to store the decoded values.
* @param apdu_size Number of valid bytes in the buffer.
* @param data Pointer to the data to store the decoded values, or NULL
*
* @return Bytes decoded or Zero/BACNET_STATUS_ERROR on error.
* @return Bytes decoded or BACNET_STATUS_ERROR on error.
*/
int cov_notify_decode_service_request(
uint8_t *apdu, unsigned apdu_len, BACNET_COV_DATA *data)
uint8_t *apdu, unsigned apdu_size, BACNET_COV_DATA *data)
{
int len = 0; /* return value */
int app_len = 0;
uint8_t tag_number = 0;
uint32_t len_value = 0;
BACNET_UNSIGNED_INTEGER decoded_value = 0; /* for decoding */
BACNET_OBJECT_TYPE decoded_type = OBJECT_NONE; /* for decoding */
uint32_t property = 0; /* for decoding */
BACNET_PROPERTY_VALUE *value = NULL; /* value in list */
BACNET_APPLICATION_DATA_VALUE *app_data = NULL;
int value_len = 0, tag_len = 0;
BACNET_UNSIGNED_INTEGER decoded_value = 0;
BACNET_OBJECT_TYPE decoded_type = OBJECT_NONE;
uint32_t decoded_instance = 0;
BACNET_PROPERTY_ID property_identifier = PROP_ALL;
BACNET_PROPERTY_VALUE *value = NULL;
if ((apdu_len > 2) && data) {
/* tag 0 - subscriberProcessIdentifier */
if (decode_is_context_tag(&apdu[len], 0)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
/* subscriber-process-identifier [0] Unsigned32 */
value_len = bacnet_unsigned_context_decode(
&apdu[len], apdu_size - len, 0, &decoded_value);
if (value_len > 0) {
if (data) {
data->subscriberProcessIdentifier = decoded_value;
} else {
}
len += value_len;
} else {
return BACNET_STATUS_ERROR;
}
/* initiating-device-identifier [1] BACnetObjectIdentifier */
value_len = bacnet_object_id_context_decode(
&apdu[len], apdu_size - len, 1, &decoded_type, &decoded_instance);
if (value_len > 0) {
if (decoded_type != OBJECT_DEVICE) {
return BACNET_STATUS_ERROR;
}
/* tag 1 - initiatingDeviceIdentifier */
if (len >= (int)apdu_len) {
return BACNET_STATUS_ERROR;
if (data) {
data->initiatingDeviceIdentifier = decoded_instance;
}
if (decode_is_context_tag(&apdu[len], 1)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len += decode_object_id(
&apdu[len], &decoded_type, &data->initiatingDeviceIdentifier);
if (decoded_type != OBJECT_DEVICE) {
return BACNET_STATUS_ERROR;
}
} else {
return BACNET_STATUS_ERROR;
}
/* tag 2 - monitoredObjectIdentifier */
if (len >= (int)apdu_len) {
return BACNET_STATUS_ERROR;
}
if (decode_is_context_tag(&apdu[len], 2)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len += decode_object_id(&apdu[len], &decoded_type,
&data->monitoredObjectIdentifier.instance);
len += value_len;
} else {
return BACNET_STATUS_ERROR;
}
/* monitored-object-identifier [2] BACnetObjectIdentifier */
value_len = bacnet_object_id_context_decode(
&apdu[len], apdu_size - len, 2, &decoded_type, &decoded_instance);
if (value_len > 0) {
if (data) {
data->monitoredObjectIdentifier.type = decoded_type;
} else {
return BACNET_STATUS_ERROR;
data->monitoredObjectIdentifier.instance = decoded_instance;
}
/* tag 3 - timeRemaining */
if (len >= (int)apdu_len) {
return BACNET_STATUS_ERROR;
}
if (decode_is_context_tag(&apdu[len], 3)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
len += value_len;
} else {
return BACNET_STATUS_ERROR;
}
/* time-remaining [3] Unsigned */
value_len = bacnet_unsigned_context_decode(
&apdu[len], apdu_size - len, 3, &decoded_value);
if (value_len > 0) {
if (data) {
data->timeRemaining = decoded_value;
}
len += value_len;
} else {
return BACNET_STATUS_ERROR;
}
/* list-of-values [4] SEQUENCE OF BACnetPropertyValue */
if (bacnet_is_opening_tag_number(
&apdu[len], apdu_size - len, 4, &tag_len)) {
if (data) {
len += tag_len;
/* the first value includes a pointer to the next value, etc */
value = data->listOfValues;
while (value != NULL) {
value_len = bacapp_property_value_decode(
&apdu[len], apdu_size - len, value);
if (value_len == BACNET_STATUS_ERROR) {
return BACNET_STATUS_ERROR;
} else {
len += value_len;
}
/* end of list? */
if (bacnet_is_closing_tag_number(
&apdu[len], apdu_size - len, 4, &tag_len)) {
len += tag_len;
value->next = NULL;
break;
}
/* is there another one to decode? */
value = value->next;
if (value == NULL) {
/* out of room to store next value */
return BACNET_STATUS_ERROR;
}
}
} else {
return BACNET_STATUS_ERROR;
}
/* tag 4: opening context tag - listOfValues */
if (!decode_is_opening_tag_number(&apdu[len], 4)) {
return BACNET_STATUS_ERROR;
}
/* a tag number of 4 is not extended so only one octet */
len++;
/* the first value includes a pointer to the next value, etc */
value = data->listOfValues;
if (value == NULL) {
/* no space to store any values */
return BACNET_STATUS_ERROR;
}
while (value != NULL) {
/* tag 0 - propertyIdentifier */
if (len >= (int)apdu_len) {
return BACNET_STATUS_ERROR;
}
if (decode_is_context_tag(&apdu[len], 0)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len += decode_enumerated(&apdu[len], len_value, &property);
value->propertyIdentifier = (BACNET_PROPERTY_ID)property;
} else {
return BACNET_STATUS_ERROR;
}
/* tag 1 - propertyArrayIndex OPTIONAL */
if (len >= (int)apdu_len) {
return BACNET_STATUS_ERROR;
}
if (decode_is_context_tag(&apdu[len], 1)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
value->propertyArrayIndex = decoded_value;
} else {
value->propertyArrayIndex = BACNET_ARRAY_ALL;
}
/* tag 2: opening context tag - value */
if (len >= (int)apdu_len) {
return BACNET_STATUS_ERROR;
}
if (!decode_is_opening_tag_number(&apdu[len], 2)) {
return BACNET_STATUS_ERROR;
}
/* a tag number of 2 is not extended so only one octet */
len++;
app_data = &value->value;
while (!decode_is_closing_tag_number(&apdu[len], 2)) {
if (app_data == NULL) {
/* out of room to store more values */
return BACNET_STATUS_ERROR;
}
app_len = bacapp_decode_application_data(
&apdu[len], apdu_len - len, app_data);
if (app_len < 0) {
return BACNET_STATUS_ERROR;
}
len += app_len;
app_data = app_data->next;
}
/* a tag number of 2 is not extended so only one octet */
len++;
/* tag 3 - priority OPTIONAL */
if (len >= (int)apdu_len) {
return BACNET_STATUS_ERROR;
}
if (decode_is_context_tag(&apdu[len], 3)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
value->priority = (uint8_t)decoded_value;
} else {
value->priority = BACNET_NO_PRIORITY;
}
/* end of list? */
if (decode_is_closing_tag_number(&apdu[len], 4)) {
value->next = NULL;
break;
}
/* is there another one to decode? */
value = value->next;
if (value == NULL) {
/* out of room to store more values */
return BACNET_STATUS_ERROR;
}
/* this len function needs to start at the opening tag
to match opening/closing tags like a stack.
However, it returns the len between the tags. */
value_len = bacapp_data_len(&apdu[len], apdu_size - len,
(BACNET_PROPERTY_ID)property_identifier);
len += value_len;
/* add the opening tag length to the totals */
len += tag_len;
}
}
@@ -442,79 +404,112 @@ int cov_subscribe_encode_apdu(uint8_t *apdu,
}
/**
* Decode the subscribe-service request only.
* @brief Decode the subscribe-service request only.
*
* SubscribeCOV-Request ::= SEQUENCE {
* subscriberProcessIdentifier [0] Unsigned32,
* monitoredObjectIdentifier [1] BACnetObjectIdentifier,
* issueConfirmedNotifications [2] BOOLEAN OPTIONAL,
* lifetime [3] Unsigned OPTIONAL
* }
*
* @param apdu Pointer to the buffer.
* @param apdu_len Count of valid bytes in the buffer.
* @param apdu_size number of valid bytes in the buffer.
* @param data Pointer to the data to store the decoded values.
*
* @return Bytes decoded or Zero/BACNET_STATUS_ERROR on error.
*/
int cov_subscribe_decode_service_request(
uint8_t *apdu, unsigned apdu_len, BACNET_SUBSCRIBE_COV_DATA *data)
uint8_t *apdu, unsigned apdu_size, BACNET_SUBSCRIBE_COV_DATA *data)
{
int len = 0; /* return value */
uint8_t tag_number = 0;
uint32_t len_value = 0;
BACNET_UNSIGNED_INTEGER unsigned_value = 0;
int value_len = 0;
BACNET_UNSIGNED_INTEGER decoded_value = 0;
BACNET_OBJECT_TYPE decoded_type = OBJECT_NONE;
uint32_t decoded_instance = 0;
bool decoded_boolean = false;
if ((apdu_len > 2) && data) {
/* tag 0 - subscriberProcessIdentifier */
if (decode_is_context_tag(&apdu[len], 0)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len += decode_unsigned(&apdu[len], len_value, &unsigned_value);
data->subscriberProcessIdentifier = unsigned_value;
} else {
/* subscriberProcessIdentifier [0] Unsigned32 */
value_len = bacnet_unsigned_context_decode(
&apdu[len], apdu_size - len, 0, &decoded_value);
if (value_len > 0) {
if (data) {
data->subscriberProcessIdentifier = decoded_value;
}
len += value_len;
} else {
if (data) {
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
}
/* tag 1 - monitoredObjectIdentifier */
if ((unsigned)len >= apdu_len) {
return BACNET_STATUS_REJECT;
}
if (decode_is_context_tag(&apdu[len], 1)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len += decode_object_id(&apdu[len], &decoded_type,
&data->monitoredObjectIdentifier.instance);
return BACNET_STATUS_ERROR;
}
/* monitoredObjectIdentifier [1] BACnetObjectIdentifier */
value_len = bacnet_object_id_context_decode(
&apdu[len], apdu_size - len, 1, &decoded_type, &decoded_instance);
if (value_len > 0) {
if (data) {
data->monitoredObjectIdentifier.type = decoded_type;
} else {
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
data->monitoredObjectIdentifier.instance = decoded_instance;
}
/* optional parameters - if missing, means cancellation */
if ((unsigned)len < apdu_len) {
/* tag 2 - issueConfirmedNotifications - optional */
if (decode_is_context_tag(&apdu[len], 2)) {
data->cancellationRequest = false;
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
data->issueConfirmedNotifications =
decode_context_boolean(&apdu[len]);
len += len_value;
} else {
data->cancellationRequest = true;
len += value_len;
} else {
if (data) {
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
}
return BACNET_STATUS_ERROR;
}
if ((unsigned)len < apdu_size) {
if (data) {
/* does not indicate a cancellation request */
data->cancellationRequest = false;
}
/* issueConfirmedNotifications [2] BOOLEAN OPTIONAL */
value_len = bacnet_boolean_context_decode(
&apdu[len], apdu_size - len, 2, &decoded_boolean);
if (value_len > 0) {
if (data) {
data->issueConfirmedNotifications = decoded_boolean;
}
/* tag 3 - lifetime - optional */
if ((unsigned)len < apdu_len) {
if (decode_is_context_tag(&apdu[len], 3)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len +=
decode_unsigned(&apdu[len], len_value, &unsigned_value);
data->lifetime = unsigned_value;
} else {
data->lifetime = 0;
}
} else {
data->lifetime = 0;
len += value_len;
} else if (value_len == 0) {
/* invalid tag */
if (data) {
data->issueConfirmedNotifications = false;
}
} else {
if (data) {
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
}
return BACNET_STATUS_ERROR;
}
} else {
/* If both the 'Issue Confirmed Notifications' and
'Lifetime' parameters are absent, then this shall
indicate a cancellation request. */
if (data) {
data->cancellationRequest = true;
}
}
if ((unsigned)len < apdu_size) {
/* lifetime [3] Unsigned OPTIONAL */
value_len = bacnet_unsigned_context_decode(
&apdu[len], apdu_size - len, 3, &decoded_value);
if (value_len > 0) {
if (data) {
data->lifetime = decoded_value;
}
len += value_len;
} else {
if (data) {
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
}
return BACNET_STATUS_ERROR;
}
} else {
if (data) {
data->lifetime = 0;
}
}
return len;
}
+5
View File
@@ -67,6 +67,11 @@ typedef struct BACnet_COV_Notification {
extern "C" {
#endif /* __cplusplus */
BACNET_STACK_EXPORT
int cov_notify_encode_apdu(
uint8_t *apdu, BACNET_COV_DATA *data);
BACNET_STACK_EXPORT
int ucov_notify_encode_apdu(
uint8_t * apdu,
unsigned max_apdu_len,
+435
View File
@@ -0,0 +1,435 @@
/**
* @file
* @brief CreateObject service encode and decode
* @author Steve Karg <skarg@users.sourceforge.net>
* @date August 2023
* @section LICENSE
*
* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include "bacnet/bacapp.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/create_object.h"
/**
* @brief Encode the CreateObject service request
*
* CreateObject-Request ::= SEQUENCE {
* object-specifier [0] CHOICE {
* object-type [0] BACnetObjectType,
* object-identifier [1] BACnetObjectIdentifier
* },
* list-of-initial-values [1] SEQUENCE OF BACnetPropertyValue OPTIONAL
* }
*
* @param apdu Pointer to the buffer for encoded values
* @param data Pointer to the service data used for encoding values
*
* @return Bytes encoded or zero on error.
*/
int create_object_encode_service_request(
uint8_t *apdu, BACNET_CREATE_OBJECT_DATA *data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
BACNET_PROPERTY_VALUE *value = NULL; /* value in list */
if (data) {
/* object-specifier [0] */
len = encode_opening_tag(apdu, 0);
apdu_len += len;
if (apdu) {
apdu += len;
}
if (data->object_instance >= BACNET_MAX_INSTANCE) {
/* object-type [0] BACnetObjectType */
len = encode_context_enumerated(apdu, 0, data->object_type);
apdu_len += len;
if (apdu) {
apdu += len;
}
} else {
/* object-identifier [1] BACnetObjectIdentifier */
len = encode_context_object_id(
apdu, 1, data->object_type, data->object_instance);
apdu_len += len;
if (apdu) {
apdu += len;
}
}
len = encode_closing_tag(apdu, 0);
apdu_len += len;
if (apdu) {
apdu += len;
}
if (data->list_of_initial_values) {
/* list-of-initial-values [1] OPTIONAL */
len = encode_opening_tag(apdu, 1);
apdu_len += len;
if (apdu) {
apdu += len;
}
/* the first value includes a pointer to the next value, etc */
value = data->list_of_initial_values;
while (value != NULL) {
/* SEQUENCE OF BACnetPropertyValue */
len = bacapp_property_value_encode(apdu, value);
apdu_len += len;
if (apdu) {
apdu += len;
}
/* is there another one to encode? */
/* FIXME: check to see if there is room in the APDU */
value = value->next;
}
len = encode_closing_tag(apdu, 1);
apdu_len += len;
}
}
return apdu_len;
}
/**
* @brief Decode the CreateObject service request
*
* CreateObject-Request ::= SEQUENCE {
* object-specifier [0] CHOICE {
* object-type [0] BACnetObjectType,
* object-identifier [1] BACnetObjectIdentifier
* },
* list-of-initial-values [1] SEQUENCE OF BACnetPropertyValue OPTIONAL
* }
*
* @param apdu Pointer to the buffer for decoding.
* @param apdu_len Count of valid bytes in the buffer.
* @param data Pointer to the property decoded data to be stored
*
* @return Bytes decoded or BACNET_STATUS_REJECT on error.
*/
int create_object_decode_service_request(
uint8_t *apdu, uint32_t apdu_size, BACNET_CREATE_OBJECT_DATA *data)
{
int len = 0;
int apdu_len = 0;
BACNET_OBJECT_TYPE object_type = OBJECT_NONE;
uint32_t object_instance = 0;
uint32_t enumerated_value = 0;
BACNET_PROPERTY_VALUE *list_of_initial_values = NULL;
/* object-specifier [0] CHOICE */
if (!bacnet_is_opening_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, 0, &len)) {
if (data) {
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
}
return BACNET_STATUS_REJECT;
}
apdu_len += len;
/* CHOICE of Tag [0] or [1] */
/* object-identifier [1] BACnetObjectIdentifier */
len = bacnet_object_id_context_decode(&apdu[apdu_len], apdu_size - apdu_len,
1, &object_type, &object_instance);
if ((len != BACNET_STATUS_ERROR) && (len != 0)) {
if ((object_type >= MAX_BACNET_OBJECT_TYPE) ||
(object_instance >= BACNET_MAX_INSTANCE)) {
if (data) {
data->error_code = ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE;
}
return BACNET_STATUS_REJECT;
}
if (data) {
data->object_instance = object_instance;
data->object_type = object_type;
}
apdu_len += len;
} else {
/* object-type [0] BACnetObjectType */
len = bacnet_enumerated_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 0, &enumerated_value);
if ((len != BACNET_STATUS_ERROR) && (len != 0)) {
if (enumerated_value >= MAX_BACNET_OBJECT_TYPE) {
if (data) {
data->error_code = ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE;
}
return BACNET_STATUS_REJECT;
}
if (data) {
data->object_instance = BACNET_MAX_INSTANCE;
data->object_type = enumerated_value;
}
apdu_len += len;
} else {
if (data) {
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
}
return BACNET_STATUS_REJECT;
}
}
if (!bacnet_is_closing_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, 0, &len)) {
if (data) {
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
}
return BACNET_STATUS_REJECT;
}
apdu_len += len;
/* list-of-initial-values [1] SEQUENCE OF BACnetPropertyValue OPTIONAL */
if (bacnet_is_opening_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, 0, &len)) {
apdu_len += len;
if (data) {
list_of_initial_values = data->list_of_initial_values;
}
len = bacapp_property_value_decode(
&apdu[apdu_len], apdu_size - apdu_len, list_of_initial_values);
if (len == BACNET_STATUS_ERROR) {
if (data) {
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
}
return BACNET_STATUS_REJECT;
}
apdu_len += len;
if (!bacnet_is_closing_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, 0, &len)) {
if (data) {
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
}
return BACNET_STATUS_REJECT;
}
apdu_len += len;
}
return apdu_len;
}
/**
* @brief Encode a CreateObject-ACK APDU service data
*
* CreateObject-ACK ::= BACnetObjectIdentifier
*
* @param apdu Pointer to the buffer for encoding, or NULL for length
* @param data Pointer to the property data to be encoded.
* @return number of bytes encoded
*/
int create_object_ack_service_encode(
uint8_t *apdu, BACNET_CREATE_OBJECT_DATA *data)
{
/* BACnetObjectIdentifier */
return encode_application_object_id(
apdu, data->object_type, data->object_instance);
}
/**
* @brief Encode a CreateObject-ACK APDU
*
* CreateObject-ACK ::= BACnetObjectIdentifier
*
* @param apdu Pointer to the buffer for encoding, or NULL for length
* @param invoke_id original invoke id from request
* @param data Pointer to the property data to be encoded.
* @return number of bytes encoded
*/
int create_object_ack_encode(
uint8_t *apdu, uint8_t invoke_id, BACNET_CREATE_OBJECT_DATA *data)
{
int apdu_len = 3; /* total length of the apdu, return value */
if (apdu) {
/* service */
apdu[0] = PDU_TYPE_COMPLEX_ACK;
/* original invoke id from request */
apdu[1] = invoke_id;
/* service choice */
apdu[2] = SERVICE_CONFIRMED_CREATE_OBJECT;
apdu += apdu_len;
}
apdu_len += create_object_ack_service_encode(apdu, data);
return apdu_len;
}
/**
* @brief Decoding for CreateObject-ACK APDU service data
* CreateObject-ACK ::= BACnetObjectIdentifier
*
* @param apdu Pointer to the buffer for decoding.
* @param apdu_size size of the buffer for decoding.
* @param data Pointer to the property data to be encoded.
* @return Bytes encoded or BACNET_STATUS_REJECT on error.
*/
int create_object_ack_service_decode(
uint8_t *apdu, uint16_t apdu_size, BACNET_CREATE_OBJECT_DATA *data)
{
int apdu_len = 0;
BACNET_OBJECT_TYPE object_type = OBJECT_NONE;
uint32_t object_instance = 0;
apdu_len = bacnet_object_id_application_decode(
apdu, apdu_size, &object_type, &object_instance);
if (apdu_len > 0) {
if (data) {
data->object_instance = object_instance;
data->object_type = object_type;
}
}
return apdu_len;
}
/**
* @brief Encode a CreateObject-Error ACK APDU
*
* CreateObject-Error ::= SEQUENCE {
* error-type [0] Error,
* first-failed-element-number [1] Unsigned
* }
*
* @param apdu Pointer to the buffer for encoding.
* @param data Pointer to the property data to be encoded.
* @return Bytes encoded or zero on error.
*/
int create_object_error_ack_service_encode(
uint8_t *apdu, BACNET_CREATE_OBJECT_DATA *data)
{
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, data->error_class);
apdu_len += len;
if (apdu) {
apdu += len;
}
len = encode_application_enumerated(apdu, data->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, data->first_failed_element_number);
apdu_len += len;
return apdu_len;
}
/**
* @brief Encode an Error acknowledge in the APDU.
* @param apdu [in] The APDU buffer.
* @param invoke_id [in] Invoked service ID.
* @param data [in] Data of the invoked property.
* @return number of bytes encoded
*/
int create_object_error_ack_encode(
uint8_t *apdu, uint8_t invoke_id, BACNET_CREATE_OBJECT_DATA *data)
{
int len = 3;
if (apdu) {
apdu[0] = PDU_TYPE_ERROR;
apdu[1] = invoke_id;
apdu[2] = SERVICE_CONFIRMED_CREATE_OBJECT;
apdu += len;
}
len += create_object_error_ack_service_encode(apdu, data);
return len;
}
/**
* @brief Decode a CreateObject-Error ACK APDU
*
* CreateObject-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 data Pointer to the property data to be encoded.
* @return Bytes encoded or BACNET_STATUS_REJECT on error.
*/
int create_object_error_ack_service_decode(
uint8_t *apdu, uint16_t apdu_size, BACNET_CREATE_OBJECT_DATA *data)
{
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 BACNET_STATUS_REJECT;
}
if (data) {
data->first_failed_element_number = 0;
data->error_class = ERROR_CLASS_SERVICES;
data->error_code = ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE;
}
if (apdu_size < apdu_len) {
return BACNET_STATUS_REJECT;
}
/* 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 BACNET_STATUS_REJECT;
}
if (apdu_size < apdu_len) {
return BACNET_STATUS_REJECT;
}
len = bacerror_decode_error_class_and_code(
apdu, apdu_size - apdu_len, &error_class, &error_code);
if (len > 0) {
if (data) {
data->error_class = error_class;
data->error_code = error_code;
}
apdu_len += len;
apdu += len;
} else {
return BACNET_STATUS_REJECT;
}
if (apdu_size < apdu_len) {
return BACNET_STATUS_REJECT;
}
/* 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 BACNET_STATUS_REJECT;
}
if (apdu_size < apdu_len) {
return BACNET_STATUS_REJECT;
}
len = bacnet_unsigned_context_decode(
apdu, apdu_size - apdu_len, 1, &first_failed_element_number);
if (len > 0) {
if (data) {
data->first_failed_element_number = first_failed_element_number;
}
apdu_len += len;
} else {
return BACNET_STATUS_REJECT;
}
return apdu_len;
}
+93
View File
@@ -0,0 +1,93 @@
/**
* @file
* @brief API for CreateObject service encode and decode
* @author Steve Karg <skarg@users.sourceforge.net>
* @date August 2023
* @section LICENSE
*
* SPDX-License-Identifier: MIT
*/
#ifndef BACNET_CREATE_OBJECT_H
#define BACNET_CREATE_OBJECT_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"
#include "bacnet/bacapp.h"
/**
* CreateObject-Request ::= SEQUENCE {
* object-specifier [0] CHOICE {
* object-type [0] BACnetObjectType,
* object-identifier [1] BACnetObjectIdentifier
* },
* list-of-initial-values [1] SEQUENCE OF BACnetPropertyValue OPTIONAL
* }
*/
typedef struct BACnet_Create_Object_Data {
/* note: use BACNET_MAX_INSTANCE to choose CHOICE=[0] object_type */
uint32_t object_instance;
BACNET_OBJECT_TYPE object_type;
/* simple linked list of values */
BACNET_PROPERTY_VALUE *list_of_initial_values;
BACNET_ERROR_CLASS error_class;
BACNET_ERROR_CODE error_code;
BACNET_UNSIGNED_INTEGER first_failed_element_number;
} BACNET_CREATE_OBJECT_DATA;
/**
* @brief CreateObject service handler for an object
* @ingroup ObjHelpers
* @param object_instance [in] instance number of the object to create,
* or BACNET_MAX_INSTANCE to create the next free object instance
* @return object instance number created, or BACNET_MAX_INSTANCE if not
*/
typedef uint32_t (*create_object_function)(uint32_t object_instance);
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
BACNET_STACK_EXPORT
int create_object_encode_service_request(
uint8_t *apdu, BACNET_CREATE_OBJECT_DATA *data);
BACNET_STACK_EXPORT
int create_object_encode_service_ack_encode(
uint8_t *apdu, BACNET_CREATE_OBJECT_DATA *data);
BACNET_STACK_EXPORT
int create_object_decode_service_request(
uint8_t *apdu, uint32_t apdu_size, BACNET_CREATE_OBJECT_DATA *data);
BACNET_STACK_EXPORT
int create_object_ack_service_encode(
uint8_t *apdu, BACNET_CREATE_OBJECT_DATA *data);
BACNET_STACK_EXPORT
int create_object_ack_service_decode(
uint8_t *apdu, uint16_t apdu_size, BACNET_CREATE_OBJECT_DATA *data);
BACNET_STACK_EXPORT
int create_object_ack_encode(
uint8_t *apdu, uint8_t invoke_id, BACNET_CREATE_OBJECT_DATA *data);
BACNET_STACK_EXPORT
int create_object_error_ack_service_encode(
uint8_t *apdu, BACNET_CREATE_OBJECT_DATA *data);
BACNET_STACK_EXPORT
int create_object_error_ack_service_decode(
uint8_t *apdu, uint16_t apdu_size, BACNET_CREATE_OBJECT_DATA *data);
BACNET_STACK_EXPORT
int create_object_error_ack_encode(
uint8_t *apdu, uint8_t invoke_id, BACNET_CREATE_OBJECT_DATA *data);
BACNET_STACK_EXPORT
int create_object_ack_encode(
uint8_t *apdu, uint8_t invoke_id,
BACNET_CREATE_OBJECT_DATA *data);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+92
View File
@@ -0,0 +1,92 @@
/**
* @file
* @brief DeleteObject service encode and decode
* @author Steve Karg <skarg@users.sourceforge.net>
* @date August 2023
* @section LICENSE
*
* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include "bacnet/bacapp.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/delete_object.h"
/**
* @brief Encode the DeleteObject service request
*
* DeleteObject-Request ::= SEQUENCE {
* object-identifier BACnetObjectIdentifier
* }
*
* @param apdu Pointer to the buffer for encoded values, or NULL for length
* @param data Pointer to the service data used for encoding values
*
* @return Bytes encoded or zero on error.
*/
int delete_object_encode_service_request(
uint8_t *apdu, BACNET_DELETE_OBJECT_DATA *data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (data) {
/* object-identifier BACnetObjectIdentifier */
len = encode_application_object_id(
apdu, data->object_type, data->object_instance);
apdu_len += len;
}
return apdu_len;
}
/**
* @brief Decode the DeleteObject service request
*
* DeleteObject-Request ::= SEQUENCE {
* object-identifier BACnetObjectIdentifier
* }
*
* @param apdu Pointer to the buffer for decoding.
* @param apdu_size Count of valid bytes in the buffer.
* @param data Pointer to the property decoded data to be stored
*
* @return Bytes decoded or BACNET_STATUS_REJECT on error.
*/
int delete_object_decode_service_request(
uint8_t *apdu, uint32_t apdu_size, BACNET_DELETE_OBJECT_DATA *data)
{
int len = 0;
int apdu_len = 0;
BACNET_OBJECT_TYPE object_type = OBJECT_NONE;
uint32_t object_instance = 0;
/* object-identifier BACnetObjectIdentifier */
len = bacnet_object_id_application_decode(&apdu[apdu_len],
apdu_size - apdu_len, &object_type, &object_instance);
if (len == BACNET_STATUS_ERROR) {
if (data) {
data->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER;
}
return BACNET_STATUS_REJECT;
} else {
if ((object_type >= MAX_BACNET_OBJECT_TYPE) ||
(object_instance > BACNET_MAX_INSTANCE)) {
if (data) {
data->error_code = ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE;
}
return BACNET_STATUS_REJECT;
}
if (data) {
data->object_instance = object_instance;
data->object_type = object_type;
}
apdu_len += len;
}
return apdu_len;
}
+56
View File
@@ -0,0 +1,56 @@
/**
* @file
* @brief API for DeleteObject service encode and decode
* @author Steve Karg <skarg@users.sourceforge.net>
* @date August 2023
* @section LICENSE
*
* SPDX-License-Identifier: MIT
*/
#ifndef BACNET_DELETE_OBJECT_H
#define BACNET_DELETE_OBJECT_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"
#include "bacnet/bacapp.h"
/**
* DeleteObject-Request ::= SEQUENCE {
* object-identifier BACnetObjectIdentifier
*/
typedef struct BACnet_Delete_Object_Data {
uint32_t object_instance;
BACNET_OBJECT_TYPE object_type;
/* application layer stores specific abort/reject/error */
BACNET_ERROR_CLASS error_class;
BACNET_ERROR_CODE error_code;
} BACNET_DELETE_OBJECT_DATA;
/**
* @brief DeleteObject service handler for an object
* @ingroup ObjHelpers
* @param object_instance [in] instance number of the object to delete
* @return true if the object instance is deleted
*/
typedef bool (*delete_object_function)(uint32_t object_instance);
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
BACNET_STACK_EXPORT
int delete_object_encode_service_request(
uint8_t *apdu, BACNET_DELETE_OBJECT_DATA *data);
BACNET_STACK_EXPORT
int delete_object_decode_service_request(
uint8_t *apdu, uint32_t apdu_size, BACNET_DELETE_OBJECT_DATA *data);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+106 -216
View File
@@ -41,178 +41,52 @@
/** @file event.c Encode/Decode Event Notifications */
/**
* Parse the array of complex-event-type notification parameters
*
* @param len - parse position inside APDU
* @param apdu_len - APDU total length
* @brief Decode the array of complex-event-type notification parameters
* @param apdu - apdu buffer
* @param apdu_size - APDU total length
* @param data - the event data struct to store the results in
* @return new value of "len", or negative on error
* @return number of apdu bytes decoded, or BACNET_STATUS_ERROR on error.
*/
static int parse_complex_event_type_values(int len, unsigned apdu_len, uint8_t *apdu, BACNET_EVENT_NOTIFICATION_DATA *data)
static int complex_event_type_values_decode(
uint8_t *apdu, unsigned apdu_size, BACNET_EVENT_NOTIFICATION_DATA *data)
{
/*
BACnetPropertyValue ::= SEQUENCE {
property-identifier [0] BACnetPropertyIdentifier,
property-array-index [1] Unsigned OPTIONAL, -- used only with array datatypes
-- if omitted with an array the entire array is referenced
property-value [2] ABSTRACT-SYNTAX.&Type, -- any datatype appropriate for the specified property
priority [3] Unsigned (1..16) OPTIONAL -- used only when property is commandable
}
*/
/* TODO this is mostly copied from "cov_notify_decode_service_request" - extract to a common function? */
uint8_t tag_number = 0;
uint32_t len_value = 0;
uint32_t property = 0;
BACNET_UNSIGNED_INTEGER decoded_value = 0;
BACNET_APPLICATION_DATA_VALUE *app_data = NULL;
int app_len = 0;
int len = 0; /* return value */
BACNET_PROPERTY_VALUE *value;
int value_len = 0, tag_len = 0;
#if (BACNET_DECODE_COMPLEX_EVENT_TYPE_PARAMETERS == 1)
BACNET_PROPERTY_VALUE *values;
BACNET_PROPERTY_VALUE *value;
/* we want to extract the values */
values = data->notificationParams.complexEventType.values;
bacapp_property_value_list_init(values, BACNET_COMPLEX_EVENT_TYPE_MAX_PARAMETERS);
value = values;
for(;;) {
/* tag 0 - propertyIdentifier */
if (len >= (int)apdu_len) {
return -1;
}
if (decode_is_context_tag(&apdu[len], 0)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len += decode_enumerated(&apdu[len], len_value, &property);
value->propertyIdentifier = (BACNET_PROPERTY_ID)property;
} else {
return -1;
}
/* tag 1 - propertyArrayIndex OPTIONAL */
if (len >= (int)apdu_len) {
return -1;
}
if (decode_is_context_tag(&apdu[len], 1)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
value->propertyArrayIndex = decoded_value;
} else {
value->propertyArrayIndex = BACNET_ARRAY_ALL;
}
/* tag 2: opening context tag - value */
if (len >= (int)apdu_len) {
return -1;
}
if (!decode_is_opening_tag_number(&apdu[len], 2)) {
return -1;
}
/* a tag number of 2 is not extended so only one octet */
len++;
app_data = &value->value;
while (!decode_is_closing_tag_number(&apdu[len], 2)) {
if (app_data == NULL) {
/* out of room to store more values */
return -1;
}
app_len = bacapp_decode_application_data(
&apdu[len], apdu_len - len, app_data);
if (app_len < 0) {
return -1;
}
len += app_len;
app_data = app_data->next;
}
/* a tag number of 2 is not extended so only one octet */
len++;
/* tag 3 - priority OPTIONAL */
if (len >= (int)apdu_len) {
return -1;
}
if (decode_is_context_tag(&apdu[len], 3)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
value->priority = (uint8_t)decoded_value;
} else {
value->priority = BACNET_NO_PRIORITY;
}
/* end of list? */
if (decode_is_closing_tag_number(&apdu[len], 6)) {
break;
}
value = value->next;
if (value == NULL) {
/* out of room to store more values */
return BACNET_STATUS_ERROR;
}
}
value = data->notificationParams.complexEventType.values;
bacapp_property_value_list_init(
value, BACNET_COMPLEX_EVENT_TYPE_MAX_PARAMETERS);
#else
/* we just want to discard the complex values */
BACNET_PROPERTY_VALUE dummyValue;
BACNET_PROPERTY_VALUE *value;
bacapp_property_value_list_init(&dummyValue, 1);
value = &dummyValue;
for(;;) {
/* tag 0 - propertyIdentifier */
if (len >= (int)apdu_len) {
return -1;
}
if (decode_is_context_tag(&apdu[len], 0)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len += decode_enumerated(&apdu[len], len_value, &property);
#endif
while (value != NULL) {
value_len =
bacapp_property_value_decode(&apdu[len], apdu_size - len, value);
if (value_len == BACNET_STATUS_ERROR) {
return BACNET_STATUS_ERROR;
} else {
return -1;
}
/* tag 1 - propertyArrayIndex OPTIONAL */
if (len >= (int)apdu_len) {
return -1;
}
if (decode_is_context_tag(&apdu[len], 1)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
}
/* tag 2: opening context tag - value */
if (len >= (int)apdu_len) {
return -1;
}
if (!decode_is_opening_tag_number(&apdu[len], 2)) {
return -1;
}
/* a tag number of 2 is not extended so only one octet */
len++;
while (!decode_is_closing_tag_number(&apdu[len], 2)) {
app_data = &value->value;
app_len = bacapp_decode_application_data(
&apdu[len], apdu_len - len, app_data);
if (app_len < 0) {
return -1;
}
len += app_len;
}
/* a tag number of 2 is not extended so only one octet */
len++;
/* tag 3 - priority OPTIONAL */
if (len >= (int)apdu_len) {
return -1;
}
if (decode_is_context_tag(&apdu[len], 3)) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
len += value_len;
}
/* end of list? */
if (decode_is_closing_tag_number(&apdu[len], 6)) {
if (bacnet_is_closing_tag_number(
&apdu[len], apdu_size - len, 6, &tag_len)) {
len += tag_len;
value->next = NULL;
break;
}
/* is there another one to decode? */
value = value->next;
if (value == NULL) {
/* out of room to store next value */
return BACNET_STATUS_ERROR;
}
}
#endif
return len;
}
@@ -850,10 +724,10 @@ int event_notify_decode_service_request(
len += section_length;
if (-1 ==
(section_length =
decode_context_bitstring(&apdu[len], 1,
&data->notificationParams
.changeOfBitstring.statusFlags))) {
(section_length = decode_context_bitstring(
&apdu[len], 1,
&data->notificationParams.changeOfBitstring
.statusFlags))) {
return -1;
}
len += section_length;
@@ -865,8 +739,8 @@ int event_notify_decode_service_request(
(section_length =
bacapp_decode_context_property_state(
&apdu[len], 0,
&data->notificationParams.changeOfState
.newState))) {
&data->notificationParams
.changeOfState.newState))) {
return -1;
}
len += section_length;
@@ -874,8 +748,8 @@ int event_notify_decode_service_request(
if (-1 ==
(section_length =
decode_context_bitstring(&apdu[len], 1,
&data->notificationParams.changeOfState
.statusFlags))) {
&data->notificationParams
.changeOfState.statusFlags))) {
return -1;
}
len += section_length;
@@ -925,8 +799,8 @@ int event_notify_decode_service_request(
if (-1 ==
(section_length =
decode_context_bitstring(&apdu[len], 1,
&data->notificationParams.changeOfValue
.statusFlags))) {
&data->notificationParams
.changeOfValue.statusFlags))) {
return -1;
}
len += section_length;
@@ -948,8 +822,9 @@ int event_notify_decode_service_request(
switch (tag_number) {
case BACNET_APPLICATION_TAG_ENUMERATED:
if (-1 ==
(section_length = decode_enumerated(
&apdu[len], len_value, &enum_value))) {
(section_length =
decode_enumerated(&apdu[len],
len_value, &enum_value))) {
return -1;
}
data->notificationParams.commandFailure
@@ -978,10 +853,10 @@ int event_notify_decode_service_request(
len++;
if (-1 ==
(section_length =
decode_context_bitstring(&apdu[len], 1,
&data->notificationParams.commandFailure
.statusFlags))) {
(section_length = decode_context_bitstring(
&apdu[len], 1,
&data->notificationParams.commandFailure
.statusFlags))) {
return -1;
}
len += section_length;
@@ -1001,8 +876,9 @@ int event_notify_decode_service_request(
switch (tag_number) {
case BACNET_APPLICATION_TAG_ENUMERATED:
if (-1 ==
(section_length = decode_enumerated(
&apdu[len], len_value, &enum_value))) {
(section_length =
decode_enumerated(&apdu[len],
len_value, &enum_value))) {
return -1;
}
data->notificationParams.commandFailure
@@ -1034,7 +910,8 @@ int event_notify_decode_service_request(
case EVENT_FLOATING_LIMIT:
if (-1 ==
(section_length = decode_context_real(&apdu[len], 0,
(section_length = decode_context_real(
&apdu[len], 0,
&data->notificationParams.floatingLimit
.referenceValue))) {
return -1;
@@ -1044,13 +921,14 @@ int event_notify_decode_service_request(
if (-1 ==
(section_length =
decode_context_bitstring(&apdu[len], 1,
&data->notificationParams.floatingLimit
.statusFlags))) {
&data->notificationParams
.floatingLimit.statusFlags))) {
return -1;
}
len += section_length;
if (-1 ==
(section_length = decode_context_real(&apdu[len], 2,
(section_length = decode_context_real(
&apdu[len], 2,
&data->notificationParams.floatingLimit
.setPointValue))) {
return -1;
@@ -1058,9 +936,10 @@ int event_notify_decode_service_request(
len += section_length;
if (-1 ==
(section_length = decode_context_real(&apdu[len], 3,
&data->notificationParams.floatingLimit
.errorLimit))) {
(section_length =
decode_context_real(&apdu[len], 3,
&data->notificationParams
.floatingLimit.errorLimit))) {
return -1;
}
len += section_length;
@@ -1068,9 +947,10 @@ int event_notify_decode_service_request(
case EVENT_OUT_OF_RANGE:
if (-1 ==
(section_length = decode_context_real(&apdu[len], 0,
&data->notificationParams.outOfRange
.exceedingValue))) {
(section_length =
decode_context_real(&apdu[len], 0,
&data->notificationParams.outOfRange
.exceedingValue))) {
return -1;
}
len += section_length;
@@ -1084,17 +964,19 @@ int event_notify_decode_service_request(
}
len += section_length;
if (-1 ==
(section_length = decode_context_real(&apdu[len], 2,
&data->notificationParams.outOfRange
.deadband))) {
(section_length =
decode_context_real(&apdu[len], 2,
&data->notificationParams.outOfRange
.deadband))) {
return -1;
}
len += section_length;
if (-1 ==
(section_length = decode_context_real(&apdu[len], 3,
&data->notificationParams.outOfRange
.exceededLimit))) {
(section_length =
decode_context_real(&apdu[len], 3,
&data->notificationParams.outOfRange
.exceededLimit))) {
return -1;
}
len += section_length;
@@ -1106,7 +988,8 @@ int event_notify_decode_service_request(
&apdu[len], 0, &enum_value))) {
return -1;
}
data->notificationParams.changeOfLifeSafety.newState =
data->notificationParams.changeOfLifeSafety
.newState =
(BACNET_LIFE_SAFETY_STATE)enum_value;
len += section_length;
@@ -1115,15 +998,15 @@ int event_notify_decode_service_request(
&apdu[len], 1, &enum_value))) {
return -1;
}
data->notificationParams.changeOfLifeSafety.newMode =
(BACNET_LIFE_SAFETY_MODE)enum_value;
data->notificationParams.changeOfLifeSafety
.newMode = (BACNET_LIFE_SAFETY_MODE)enum_value;
len += section_length;
if (-1 ==
(section_length = decode_context_bitstring(
&apdu[len], 2,
&data->notificationParams.changeOfLifeSafety
.statusFlags))) {
&data->notificationParams
.changeOfLifeSafety.statusFlags))) {
return -1;
}
len += section_length;
@@ -1145,7 +1028,8 @@ int event_notify_decode_service_request(
(section_length =
bacapp_decode_context_device_obj_property_ref(
&apdu[len], 0,
&data->notificationParams.bufferReady
&data->notificationParams
.bufferReady
.bufferProperty))) {
return -1;
}
@@ -1190,7 +1074,8 @@ int event_notify_decode_service_request(
len += section_length;
if (unsigned_value <= UINT32_MAX) {
data->notificationParams.unsignedRange
.exceedingValue = (uint32_t)unsigned_value;
.exceedingValue =
(uint32_t)unsigned_value;
} else {
return BACNET_STATUS_ERROR;
}
@@ -1201,8 +1086,8 @@ int event_notify_decode_service_request(
if (-1 ==
(section_length =
decode_context_bitstring(&apdu[len], 1,
&data->notificationParams.unsignedRange
.statusFlags))) {
&data->notificationParams
.unsignedRange.statusFlags))) {
return -1;
}
len += section_length;
@@ -1213,7 +1098,8 @@ int event_notify_decode_service_request(
len += section_length;
if (unsigned_value <= UINT32_MAX) {
data->notificationParams.unsignedRange
.exceededLimit = (uint32_t)unsigned_value;
.exceededLimit =
(uint32_t)unsigned_value;
} else {
return BACNET_STATUS_ERROR;
}
@@ -1234,26 +1120,28 @@ int event_notify_decode_service_request(
if (-1 ==
(section_length =
decode_context_bitstring(&apdu[len], 1,
&data->notificationParams.accessEvent
.statusFlags))) {
&data->notificationParams
.accessEvent.statusFlags))) {
return -1;
}
len += section_length;
if (-1 ==
(section_length = decode_context_unsigned(
&apdu[len], 2,
&data->notificationParams.accessEvent
.accessEventTag))) {
return -1;
}
len += section_length;
if (-1 ==
(section_length =
decode_context_unsigned(&apdu[len], 2,
&data->notificationParams.accessEvent
.accessEventTag))) {
return -1;
}
len += section_length;
if (-1 ==
(section_length = bacapp_decode_context_timestamp(
&apdu[len], 3,
&data->notificationParams.accessEvent
.accessEventTime))) {
bacapp_decode_context_timestamp(
&apdu[len], 3,
&data->notificationParams
.accessEvent
.accessEventTime))) {
return -1;
}
len += section_length;
@@ -1262,7 +1150,8 @@ int event_notify_decode_service_request(
(section_length =
bacapp_decode_context_device_obj_ref(
&apdu[len], 4,
&data->notificationParams.accessEvent
&data->notificationParams
.accessEvent
.accessCredential))) {
return -1;
}
@@ -1290,7 +1179,8 @@ int event_notify_decode_service_request(
len++;
is_complex_event_type = true;
len = parse_complex_event_type_values(len, apdu_len, apdu, data);
len = complex_event_type_values_decode(
&apdu[len], apdu_len - len, data);
if (len < 0) {
return -1;
}
@@ -1298,8 +1188,8 @@ int event_notify_decode_service_request(
return -1;
}
if (decode_is_closing_tag_number(
&apdu[len], is_complex_event_type ? 6 : (uint8_t)data->eventType)) {
if (decode_is_closing_tag_number(&apdu[len],
is_complex_event_type ? 6 : (uint8_t)data->eventType)) {
len++;
} else {
return -1;
+40 -2
View File
@@ -77,6 +77,9 @@ BACNET_REJECT_REASON reject_convert_error_code(BACNET_ERROR_CODE error_code)
case ERROR_CODE_REJECT_UNRECOGNIZED_SERVICE:
reject_code = REJECT_REASON_UNRECOGNIZED_SERVICE;
break;
case ERROR_CODE_INVALID_DATA_ENCODING:
reject_code = REJECT_REASON_INVALID_DATA_ENCODING;
break;
case ERROR_CODE_REJECT_PROPRIETARY:
reject_code = REJECT_REASON_PROPRIETARY_FIRST;
break;
@@ -89,6 +92,38 @@ BACNET_REJECT_REASON reject_convert_error_code(BACNET_ERROR_CODE error_code)
return (reject_code);
}
/**
* @brief Determine if a BACnetErrorCode is a BACnetRejectReason
* @param error_code #BACNET_ERROR_CODE enumeration
* @return true if the BACnet Error Code is a BACnet abort reason
*/
bool reject_valid_error_code(BACNET_ERROR_CODE error_code)
{
bool status = false;
switch (error_code) {
case ERROR_CODE_REJECT_OTHER:
case ERROR_CODE_REJECT_BUFFER_OVERFLOW:
case ERROR_CODE_REJECT_INCONSISTENT_PARAMETERS:
case ERROR_CODE_REJECT_INVALID_PARAMETER_DATA_TYPE:
case ERROR_CODE_REJECT_INVALID_TAG:
case ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER:
case ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE:
case ERROR_CODE_REJECT_TOO_MANY_ARGUMENTS:
case ERROR_CODE_REJECT_UNDEFINED_ENUMERATION:
case ERROR_CODE_REJECT_UNRECOGNIZED_SERVICE:
case ERROR_CODE_INVALID_DATA_ENCODING:
case ERROR_CODE_REJECT_PROPRIETARY:
status = true;
break;
default:
break;
}
return status;
}
/**
* @brief Convert a reject code to BACnet Error code
* @param reject_code - code to be converted
@@ -100,6 +135,9 @@ BACNET_ERROR_CODE reject_convert_to_error_code(BACNET_REJECT_REASON reject_code)
BACNET_ERROR_CODE error_code = ERROR_CODE_REJECT_OTHER;
switch (reject_code) {
case REJECT_REASON_OTHER:
error_code = ERROR_CODE_REJECT_OTHER;
break;
case REJECT_REASON_BUFFER_OVERFLOW:
error_code = ERROR_CODE_REJECT_BUFFER_OVERFLOW;
break;
@@ -127,8 +165,8 @@ BACNET_ERROR_CODE reject_convert_to_error_code(BACNET_REJECT_REASON reject_code)
case REJECT_REASON_UNRECOGNIZED_SERVICE:
error_code = ERROR_CODE_REJECT_UNRECOGNIZED_SERVICE;
break;
case REJECT_REASON_OTHER:
error_code = ERROR_CODE_REJECT_OTHER;
case REJECT_REASON_INVALID_DATA_ENCODING:
error_code = ERROR_CODE_INVALID_DATA_ENCODING;
break;
default:
if ((reject_code >= REJECT_REASON_PROPRIETARY_FIRST) &&
+5 -2
View File
@@ -21,8 +21,8 @@
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*********************************************************************/
#ifndef REJECT_H
#define REJECT_H
#ifndef BACNET_REJECT_H
#define BACNET_REJECT_H
#include <stdint.h>
#include <stdbool.h>
@@ -37,6 +37,9 @@ extern "C" {
BACNET_REJECT_REASON reject_convert_error_code(
BACNET_ERROR_CODE error_code);
BACNET_STACK_EXPORT
bool reject_valid_error_code(
BACNET_ERROR_CODE error_code);
BACNET_STACK_EXPORT
BACNET_ERROR_CODE reject_convert_to_error_code(
BACNET_REJECT_REASON reject_code);
+3
View File
@@ -53,8 +53,10 @@ list(APPEND testdirs
bacnet/bacstr
bacnet/bactimevalue
bacnet/cov
bacnet/create_object
bacnet/datetime
bacnet/dcc
bacnet/delete_object
bacnet/event
bacnet/getalarm
bacnet/getevent
@@ -62,6 +64,7 @@ list(APPEND testdirs
bacnet/ihave
bacnet/indtext
bacnet/lighting
bacnet/list_element
bacnet/lso
bacnet/memcopy
bacnet/npdu
+1 -1
View File
@@ -7,7 +7,6 @@ project(test_${basename}
VERSION 1.0.0
LANGUAGES C)
string(REGEX REPLACE
"/test/bacnet/[a-zA-Z_/-]*$"
"/src"
@@ -25,6 +24,7 @@ add_compile_definitions(
CONFIG_ZTEST=1
PRINT_ENABLED=1
BACAPP_ALL=1
BACAPP_PRINT_ENABLED=1
)
include_directories(
+192 -137
View File
@@ -17,46 +17,46 @@
static const BACNET_APPLICATION_TAG tag_list[] = {
BACNET_APPLICATION_TAG_NULL,
#if defined(BACAPP_BOOLEAN)
#if defined(BACAPP_BOOLEAN)
BACNET_APPLICATION_TAG_BOOLEAN,
#endif
#if defined(BACAPP_UNSIGNED)
#endif
#if defined(BACAPP_UNSIGNED)
BACNET_APPLICATION_TAG_UNSIGNED_INT,
#endif
#if defined(BACAPP_SIGNED)
#endif
#if defined(BACAPP_SIGNED)
BACNET_APPLICATION_TAG_SIGNED_INT,
#endif
#if defined(BACAPP_REAL)
#endif
#if defined(BACAPP_REAL)
BACNET_APPLICATION_TAG_REAL,
#endif
#if defined(BACAPP_DOUBLE)
#endif
#if defined(BACAPP_DOUBLE)
BACNET_APPLICATION_TAG_DOUBLE,
#endif
#if defined(BACAPP_OCTET_STRING)
#endif
#if defined(BACAPP_OCTET_STRING)
BACNET_APPLICATION_TAG_OCTET_STRING,
#endif
#if defined(BACAPP_CHARACTER_STRING)
#endif
#if defined(BACAPP_CHARACTER_STRING)
BACNET_APPLICATION_TAG_CHARACTER_STRING,
#endif
#if defined(BACAPP_BIT_STRING)
#endif
#if defined(BACAPP_BIT_STRING)
BACNET_APPLICATION_TAG_BIT_STRING,
#endif
#if defined(BACAPP_ENUMERATED)
#endif
#if defined(BACAPP_ENUMERATED)
BACNET_APPLICATION_TAG_ENUMERATED,
#endif
#if defined(BACAPP_DATE)
#endif
#if defined(BACAPP_DATE)
BACNET_APPLICATION_TAG_DATE,
#endif
#if defined(BACAPP_TIME)
#endif
#if defined(BACAPP_TIME)
BACNET_APPLICATION_TAG_TIME,
#endif
#if defined(BACAPP_OBJECT_ID)
#endif
#if defined(BACAPP_OBJECT_ID)
BACNET_APPLICATION_TAG_OBJECT_ID,
#endif
#if defined(BACAPP_TYPES_EXTRA)
#endif
#if defined(BACAPP_TYPES_EXTRA)
BACNET_APPLICATION_TAG_LIGHTING_COMMAND,
BACNET_APPLICATION_TAG_HOST_N_PORT,
#endif
#endif
};
/**
@@ -71,15 +71,16 @@ static void test_bacapp_decode_application_data(void)
#endif
{
uint8_t apdu[128] = { 0 };
//unsigned max_apdu_len = sizeof(apdu);
// unsigned max_apdu_len = sizeof(apdu);
BACNET_APPLICATION_DATA_VALUE value = { 0 };
zassert_equal(bacapp_decode_application_data(NULL, sizeof(apdu), &value), 0, NULL);
zassert_equal(
bacapp_decode_application_data(NULL, sizeof(apdu), &value), 0, NULL);
zassert_equal(bacapp_decode_application_data(apdu, 0, &value), 0, NULL);
zassert_equal(bacapp_decode_application_data(apdu, sizeof(apdu), NULL), 0, NULL);
zassert_equal(
bacapp_decode_application_data(apdu, sizeof(apdu), NULL), 0, NULL);
}
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(bacapp_tests, test_bacapp_decode_data_len)
#else
@@ -90,58 +91,89 @@ static void test_bacapp_decode_data_len(void)
uint32_t len_value_type = 0;
int expected_value = 0;
zassert_equal(bacapp_decode_data_len(NULL, BACNET_APPLICATION_TAG_NULL, sizeof(apdu)), 0, NULL);
zassert_equal(bacapp_decode_data_len(apdu, UINT8_MAX, sizeof(apdu)), 0, NULL);
zassert_equal(
bacapp_decode_data_len(NULL, BACNET_APPLICATION_TAG_NULL, sizeof(apdu)),
0, NULL);
zassert_equal(
bacapp_decode_data_len(apdu, UINT8_MAX, sizeof(apdu)), 0, NULL);
expected_value = (int) (~0U >> 1); /* INT_MAX is not universally defined */
zassert_equal(bacapp_decode_data_len(apdu, BACNET_APPLICATION_TAG_UNSIGNED_INT, UINT32_MAX), expected_value, NULL);
expected_value = (int)(~0U >> 1); /* INT_MAX is not universally defined */
zassert_equal(bacapp_decode_data_len(
apdu, BACNET_APPLICATION_TAG_UNSIGNED_INT, UINT32_MAX),
expected_value, NULL);
zassert_equal(bacapp_decode_data_len(apdu, BACNET_APPLICATION_TAG_NULL, sizeof(apdu)), 0, NULL);
zassert_equal(bacapp_decode_data_len(apdu, BACNET_APPLICATION_TAG_BOOLEAN, sizeof(apdu)), 0, NULL);
zassert_equal(
bacapp_decode_data_len(apdu, BACNET_APPLICATION_TAG_NULL, sizeof(apdu)),
0, NULL);
zassert_equal(bacapp_decode_data_len(
apdu, BACNET_APPLICATION_TAG_BOOLEAN, sizeof(apdu)),
0, NULL);
len_value_type = INT32_MAX - 1;
expected_value = (int) len_value_type;
zassert_equal(bacapp_decode_data_len(apdu, BACNET_APPLICATION_TAG_UNSIGNED_INT, len_value_type), expected_value, NULL);
expected_value = (int)len_value_type;
zassert_equal(bacapp_decode_data_len(apdu,
BACNET_APPLICATION_TAG_UNSIGNED_INT, len_value_type),
expected_value, NULL);
len_value_type = INT32_MAX - 2;
expected_value = (int) len_value_type;
zassert_equal(bacapp_decode_data_len(apdu, BACNET_APPLICATION_TAG_SIGNED_INT, len_value_type), expected_value, NULL);
expected_value = (int)len_value_type;
zassert_equal(bacapp_decode_data_len(
apdu, BACNET_APPLICATION_TAG_SIGNED_INT, len_value_type),
expected_value, NULL);
len_value_type = INT32_MAX - 5;
expected_value = (int) len_value_type;
zassert_equal(bacapp_decode_data_len(apdu, BACNET_APPLICATION_TAG_REAL, len_value_type), expected_value, NULL);
expected_value = (int)len_value_type;
zassert_equal(bacapp_decode_data_len(
apdu, BACNET_APPLICATION_TAG_REAL, len_value_type),
expected_value, NULL);
len_value_type = INT32_MAX - 9;
expected_value = (int) len_value_type;
zassert_equal(bacapp_decode_data_len(apdu, BACNET_APPLICATION_TAG_DOUBLE, len_value_type), expected_value, NULL);
expected_value = (int)len_value_type;
zassert_equal(bacapp_decode_data_len(
apdu, BACNET_APPLICATION_TAG_DOUBLE, len_value_type),
expected_value, NULL);
len_value_type = INT32_MAX - 13;
expected_value = (int) len_value_type;
zassert_equal(bacapp_decode_data_len(apdu, BACNET_APPLICATION_TAG_OCTET_STRING, len_value_type), expected_value, NULL);
expected_value = (int)len_value_type;
zassert_equal(bacapp_decode_data_len(apdu,
BACNET_APPLICATION_TAG_OCTET_STRING, len_value_type),
expected_value, NULL);
len_value_type = INT32_MAX - 17;
expected_value = (int) len_value_type;
zassert_equal(bacapp_decode_data_len(apdu, BACNET_APPLICATION_TAG_CHARACTER_STRING, len_value_type), expected_value, NULL);
expected_value = (int)len_value_type;
zassert_equal(bacapp_decode_data_len(apdu,
BACNET_APPLICATION_TAG_CHARACTER_STRING, len_value_type),
expected_value, NULL);
len_value_type = INT32_MAX - 19;
expected_value = (int) len_value_type;
zassert_equal(bacapp_decode_data_len(apdu, BACNET_APPLICATION_TAG_BIT_STRING, len_value_type), expected_value, NULL);
expected_value = (int)len_value_type;
zassert_equal(bacapp_decode_data_len(
apdu, BACNET_APPLICATION_TAG_BIT_STRING, len_value_type),
expected_value, NULL);
len_value_type = INT32_MAX - 23;
expected_value = (int) len_value_type;
zassert_equal(bacapp_decode_data_len(apdu, BACNET_APPLICATION_TAG_ENUMERATED, len_value_type), expected_value, NULL);
expected_value = (int)len_value_type;
zassert_equal(bacapp_decode_data_len(
apdu, BACNET_APPLICATION_TAG_ENUMERATED, len_value_type),
expected_value, NULL);
len_value_type = INT32_MAX - 29;
expected_value = (int) len_value_type;
zassert_equal(bacapp_decode_data_len(apdu, BACNET_APPLICATION_TAG_DATE, len_value_type), expected_value, NULL);
expected_value = (int)len_value_type;
zassert_equal(bacapp_decode_data_len(
apdu, BACNET_APPLICATION_TAG_DATE, len_value_type),
expected_value, NULL);
len_value_type = INT32_MAX - 31;
expected_value = (int) len_value_type;
zassert_equal(bacapp_decode_data_len(apdu, BACNET_APPLICATION_TAG_TIME, len_value_type), expected_value, NULL);
expected_value = (int)len_value_type;
zassert_equal(bacapp_decode_data_len(
apdu, BACNET_APPLICATION_TAG_TIME, len_value_type),
expected_value, NULL);
len_value_type = INT32_MAX - 37;
expected_value = (int) len_value_type;
zassert_equal(bacapp_decode_data_len(apdu, BACNET_APPLICATION_TAG_OBJECT_ID, len_value_type), expected_value, NULL);
expected_value = (int)len_value_type;
zassert_equal(bacapp_decode_data_len(
apdu, BACNET_APPLICATION_TAG_OBJECT_ID, len_value_type),
expected_value, NULL);
}
#if defined(CONFIG_ZTEST_NEW_API)
@@ -164,16 +196,16 @@ static void test_bacapp_copy(void)
zassert_equal(dest_value.tag, src_value.tag, NULL);
zassert_equal(dest_value.next, src_value.next, NULL);
for (i = 0; i < sizeof(tag_list)/sizeof(tag_list[0]); ++i) {
for (i = 0; i < sizeof(tag_list) / sizeof(tag_list[0]); ++i) {
BACNET_APPLICATION_TAG tag = tag_list[i];
bool result;
bool expected_result = true;
#if ! defined(BACAPP_NULL)
#if !defined(BACAPP_NULL)
if (tag == BACNET_APPLICATION_TAG_NULL) {
expected_result = false;
}
#endif
#endif
memset(&src_value, 0, sizeof(src_value));
src_value.next = NULL;
@@ -210,7 +242,8 @@ static void test_bacapp_value_list_init(void)
bacapp_value_list_init(&value[0], 0);
zassert_equal(memcmp(&value[0], &value[1], sizeof(value[1])), 0, NULL);
/* Verify one structure is initialized correctly */
for (max_count = 1; max_count < sizeof(value)/sizeof(value[0]); ++max_count) {
for (max_count = 1; max_count < sizeof(value) / sizeof(value[0]);
++max_count) {
memset(value, 0, sizeof(value));
max_count = 1;
bacapp_value_list_init(&value[0], max_count);
@@ -219,20 +252,24 @@ static void test_bacapp_value_list_init(void)
zassert_equal(value[count].tag, BACNET_APPLICATION_TAG_NULL, NULL);
zassert_equal(value[count].context_specific, 0, NULL);
zassert_equal(value[count].context_tag, 0, NULL);
zassert_equal(value[count].next, ((count + 1 >= max_count) ? NULL : &value[count + 1]), NULL);
zassert_equal(value[count].next,
((count + 1 >= max_count) ? NULL : &value[count + 1]), NULL);
}
}
}
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(bacapp_tests, test_bacapp_property_value_list_init)
ZTEST(bacapp_tests, test_bacapp_property_value_list)
#else
static void test_bacapp_property_value_list_init(void)
static void test_bacapp_property_value_list(void)
#endif
{
BACNET_PROPERTY_VALUE value[2] = { { 0 } };
size_t max_count = 0;
size_t count = 0;
int len, test_len;
uint8_t apdu[480];
bool status;
/* Verify NULL ptr is properly handled */
bacapp_property_value_list_init(NULL, 1);
@@ -243,16 +280,50 @@ static void test_bacapp_property_value_list_init(void)
zassert_equal(memcmp(&value[0], &value[1], sizeof(value[1])), 0, NULL);
/* Verify one structure is initialized correctly */
for (max_count = 1; max_count < sizeof(value)/sizeof(value[0]); ++max_count) {
for (max_count = 1; max_count < ARRAY_SIZE(value); ++max_count) {
memset(value, 0, sizeof(value));
max_count = 1;
bacapp_property_value_list_init(&value[0], max_count);
for (count = 0; count < max_count; ++count) {
zassert_equal(value[count].propertyIdentifier, MAX_BACNET_PROPERTY_ID, NULL);
zassert_equal(value[count].propertyArrayIndex, BACNET_ARRAY_ALL, NULL);
zassert_equal(
value[count].propertyIdentifier, MAX_BACNET_PROPERTY_ID, NULL);
zassert_equal(
value[count].propertyArrayIndex, BACNET_ARRAY_ALL, NULL);
zassert_equal(value[count].priority, BACNET_NO_PRIORITY, NULL);
zassert_equal(value[count].next, ((count + 1 >= max_count) ? NULL : &value[count + 1]), NULL);
zassert_equal(value[count].next,
((count + 1 >= max_count) ? NULL : &value[count + 1]), NULL);
}
}
bacapp_property_value_list_link(value, ARRAY_SIZE(value));
value[0].propertyIdentifier = 1;
value[0].propertyArrayIndex = 1;
value[0].priority = 1;
status = bacapp_parse_application_data(
BACNET_APPLICATION_TAG_UNSIGNED_INT, "1", &value[0].value);
zassert_true(status, NULL);
test_len = bacapp_property_value_encode(NULL, &value[0]);
zassert_true(test_len > 0, NULL);
len = bacapp_property_value_encode(apdu, &value[0]);
zassert_true(len > 0, NULL);
test_len = bacapp_property_value_decode(apdu, sizeof(apdu), &value[1]);
zassert_equal(len, test_len, "len=%d test_len=%d", len, test_len);
test_len = bacapp_property_value_decode(apdu, sizeof(apdu), NULL);
zassert_equal(len, test_len, "len=%d test_len=%d", len, test_len);
while (len) {
len--;
test_len = bacapp_property_value_decode(apdu, len, &value[1]);
if (test_len != BACNET_STATUS_ERROR) {
/* shorter packet leaves off the OPTIONAL priority */
zassert_equal(len, test_len, "len=%d test_len=%d", len, test_len);
zassert_equal(value[1].priority, BACNET_NO_PRIORITY, "priority=%u",
(unsigned)value[1].priority);
} else {
zassert_equal(test_len, BACNET_STATUS_ERROR, "len=%d test_len=%d",
len, test_len);
test_len = bacapp_property_value_decode(apdu, len, NULL);
zassert_equal(test_len, BACNET_STATUS_ERROR, "len=%d test_len=%d",
len, test_len);
}
}
}
@@ -280,7 +351,6 @@ static void test_bacapp_same_value(void)
zassert_false(bacapp_same_value(&value, &test_value), NULL);
#endif
test_value.tag = BACNET_APPLICATION_TAG_BOOLEAN;
value.tag = test_value.tag;
#if defined(BACAPP_BOOLEAN)
@@ -291,10 +361,9 @@ static void test_bacapp_same_value(void)
value.type.Boolean = !test_value.type.Boolean;
zassert_false(bacapp_same_value(&value, &test_value), NULL);
memset(&test_value, 0, sizeof(test_value));
test_value.tag = BACNET_APPLICATION_TAG_UNSIGNED_INT;
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
#if defined(BACAPP_UNSIGNED)
zassert_true(bacapp_same_value(&value, &test_value), NULL);
#else
@@ -303,10 +372,9 @@ static void test_bacapp_same_value(void)
value.type.Unsigned_Int = ~test_value.type.Unsigned_Int;
zassert_false(bacapp_same_value(&value, &test_value), NULL);
memset(&test_value, 0, sizeof(test_value));
test_value.tag = BACNET_APPLICATION_TAG_SIGNED_INT;
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
#if defined(BACAPP_SIGNED)
zassert_true(bacapp_same_value(&value, &test_value), NULL);
#else
@@ -315,10 +383,9 @@ static void test_bacapp_same_value(void)
value.type.Signed_Int = test_value.type.Signed_Int + 1;
zassert_false(bacapp_same_value(&value, &test_value), NULL);
memset(&test_value, 0, sizeof(test_value));
test_value.tag = BACNET_APPLICATION_TAG_REAL;
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
#if defined(BACAPP_REAL)
zassert_true(bacapp_same_value(&value, &test_value), NULL);
#else
@@ -327,10 +394,9 @@ static void test_bacapp_same_value(void)
value.type.Real = test_value.type.Real + 1.0f;
zassert_false(bacapp_same_value(&value, &test_value), NULL);
memset(&test_value, 0, sizeof(test_value));
test_value.tag = BACNET_APPLICATION_TAG_DOUBLE;
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
#if defined(BACAPP_DOUBLE)
zassert_true(bacapp_same_value(&value, &test_value), NULL);
#else
@@ -339,10 +405,9 @@ static void test_bacapp_same_value(void)
value.type.Double = test_value.type.Double + 1.0;
zassert_false(bacapp_same_value(&value, &test_value), NULL);
memset(&test_value, 0, sizeof(test_value));
test_value.tag = BACNET_APPLICATION_TAG_ENUMERATED;
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
#if defined(BACAPP_ENUMERATED)
zassert_true(bacapp_same_value(&value, &test_value), NULL);
#else
@@ -351,17 +416,16 @@ static void test_bacapp_same_value(void)
value.type.Enumerated = test_value.type.Enumerated + 1;
zassert_false(bacapp_same_value(&value, &test_value), NULL);
memset(&test_value, 0, sizeof(test_value));
test_value.tag = BACNET_APPLICATION_TAG_DATE;
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
#if defined(BACAPP_DATE)
zassert_true(bacapp_same_value(&value, &test_value), NULL);
#else
zassert_false(bacapp_same_value(&value, &test_value), NULL);
#endif
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
value.type.Date.day = test_value.type.Date.day + 1;
zassert_false(bacapp_same_value(&value, &test_value), NULL);
@@ -371,104 +435,97 @@ static void test_bacapp_same_value(void)
zassert_false(bacapp_same_value(&value, &test_value), NULL);
#endif
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
value.type.Date.month = test_value.type.Date.month + 1;
zassert_false(bacapp_same_value(&value, &test_value), NULL);
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
value.type.Date.year = test_value.type.Date.year + 1;
zassert_false(bacapp_same_value(&value, &test_value), NULL);
memset(&test_value, 0, sizeof(test_value));
test_value.tag = BACNET_APPLICATION_TAG_TIME;
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
#if defined(BACAPP_TIME)
zassert_true(bacapp_same_value(&value, &test_value), NULL);
#else
zassert_false(bacapp_same_value(&value, &test_value), NULL);
#endif
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
value.type.Time.hour = test_value.type.Time.hour + 1;
zassert_false(bacapp_same_value(&value, &test_value), NULL);
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
value.type.Time.min = test_value.type.Time.min + 1;
zassert_false(bacapp_same_value(&value, &test_value), NULL);
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
value.type.Time.sec = test_value.type.Time.sec + 1;
zassert_false(bacapp_same_value(&value, &test_value), NULL);
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
value.type.Time.hundredths = test_value.type.Time.hundredths + 1;
zassert_false(bacapp_same_value(&value, &test_value), NULL);
memset(&test_value, 0, sizeof(test_value));
test_value.tag = BACNET_APPLICATION_TAG_OBJECT_ID;
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
#if defined(BACAPP_OBJECT_ID)
zassert_true(bacapp_same_value(&value, &test_value), NULL);
#else
zassert_false(bacapp_same_value(&value, &test_value), NULL);
#endif
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
value.type.Object_Id.type = test_value.type.Object_Id.type + 1;
zassert_false(bacapp_same_value(&value, &test_value), NULL);
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
value.type.Object_Id.instance = test_value.type.Object_Id.instance + 1;
zassert_false(bacapp_same_value(&value, &test_value), NULL);
memset(&test_value, 0, sizeof(test_value));
test_value.tag = BACNET_APPLICATION_TAG_CHARACTER_STRING;
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
#if defined(BACAPP_CHARACTER_STRING)
zassert_true(bacapp_same_value(&value, &test_value), NULL);
#else
zassert_false(bacapp_same_value(&value, &test_value), NULL);
#endif
//TODO: Verify .type.Character_String value compared
// TODO: Verify .type.Character_String value compared
memset(&test_value, 0, sizeof(test_value));
test_value.tag = BACNET_APPLICATION_TAG_OCTET_STRING;
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
#if defined(BACAPP_OCTET_STRING)
zassert_true(bacapp_same_value(&value, &test_value), NULL);
#else
zassert_false(bacapp_same_value(&value, &test_value), NULL);
#endif
//TODO: Verify .type.Octet_String value compared
// TODO: Verify .type.Octet_String value compared
memset(&test_value, 0, sizeof(test_value));
test_value.tag = BACNET_APPLICATION_TAG_BIT_STRING;
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
#if defined(BACAPP_BIT_STRING)
zassert_true(bacapp_same_value(&value, &test_value), NULL);
#else
zassert_false(bacapp_same_value(&value, &test_value), NULL);
#endif
//TODO: Verify .type.Bit_String value compared
// TODO: Verify .type.Bit_String value compared
memset(&test_value, 0, sizeof(test_value));
test_value.tag = BACNET_APPLICATION_TAG_LIGHTING_COMMAND;
value = test_value; /* Struct copy */
value = test_value; /* Struct copy */
#if defined(BACAPP_TYPES_EXTRA)
zassert_true(bacapp_same_value(&value, &test_value), NULL);
#else
zassert_false(bacapp_same_value(&value, &test_value), NULL);
#endif
//TODO: Verify .type.Lighting_Command value compared
// TODO: Verify .type.Lighting_Command value compared
}
/**
* @brief Test
*/
@@ -759,7 +816,8 @@ static void testBACnetApplicationDataLength(void)
/**
* @brief Test
*/
static bool verifyBACnetApplicationDataValue(BACNET_APPLICATION_DATA_VALUE *value)
static bool verifyBACnetApplicationDataValue(
BACNET_APPLICATION_DATA_VALUE *value)
{
uint8_t apdu[480] = { 0 };
int apdu_len = 0;
@@ -780,8 +838,7 @@ static bool verifyBACnetApplicationDataValue(BACNET_APPLICATION_DATA_VALUE *valu
* @brief Test
*/
static bool verifyBACnetComplexDataValue(
BACNET_APPLICATION_DATA_VALUE *value,
BACNET_PROPERTY_ID prop)
BACNET_APPLICATION_DATA_VALUE *value, BACNET_PROPERTY_ID prop)
{
uint8_t apdu[480] = { 0 };
int apdu_len = 0;
@@ -792,8 +849,8 @@ static bool verifyBACnetComplexDataValue(
zassert_true(apdu_len > 0, NULL);
null_len = bacapp_encode_application_data(NULL, value);
zassert_equal(apdu_len, null_len, NULL);
apdu_len = bacapp_decode_generic_property(&apdu[0], apdu_len,
&test_value, prop);
apdu_len =
bacapp_decode_generic_property(&apdu[0], apdu_len, &test_value, prop);
zassert_true(apdu_len != BACNET_STATUS_ERROR, NULL);
return bacapp_same_value(value, &test_value);
@@ -1053,8 +1110,8 @@ static void testBACnetApplicationData(void)
BACNET_APPLICATION_TAG_HOST_N_PORT, "192.168.1.1:47808", &value);
zassert_true(status, NULL);
status = verifyBACnetComplexDataValue(&value, PROP_FD_BBMD_ADDRESS);
status = verifyBACnetComplexDataValue(&value,
PROP_BACNET_IP_GLOBAL_ADDRESS);
status =
verifyBACnetComplexDataValue(&value, PROP_BACNET_IP_GLOBAL_ADDRESS);
return;
}
@@ -1074,16 +1131,16 @@ static void test_bacapp_context_data(void)
int apdu_len, null_len;
unsigned i = 0;
for (i = 0; i < sizeof(tag_list)/sizeof(tag_list[0]); i++) {
for (i = 0; i < sizeof(tag_list) / sizeof(tag_list[0]); i++) {
BACNET_APPLICATION_TAG tag = tag_list[i];
value.tag = tag;
null_len = bacapp_encode_context_data_value(NULL,
context_tag_number, &value);
apdu_len = bacapp_encode_context_data_value(apdu,
context_tag_number, &value);
null_len =
bacapp_encode_context_data_value(NULL, context_tag_number, &value);
apdu_len =
bacapp_encode_context_data_value(apdu, context_tag_number, &value);
if (apdu_len != null_len) {
printf("bacapp: NULL len=%d != APDU len=%d for tag=%s",
null_len, apdu_len, bactext_application_tag_name(tag));
printf("bacapp: NULL len=%d != APDU len=%d for tag=%s", null_len,
apdu_len, bactext_application_tag_name(tag));
}
zassert_equal(apdu_len, null_len, NULL);
}
@@ -1114,8 +1171,8 @@ static void test_bacapp_sprintf_data(void)
zassert_true(status, NULL);
str_len = bacapp_snprintf_value(NULL, 0, &object_value);
if (str_len > 0) {
char str[str_len+1];
bacapp_snprintf_value(str, str_len+1, &object_value);
char str[str_len + 1];
bacapp_snprintf_value(str, str_len + 1, &object_value);
zassert_mem_equal(str, "Null", str_len, NULL);
}
}
@@ -1124,25 +1181,23 @@ static void test_bacapp_sprintf_data(void)
* @}
*/
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST_SUITE(bacapp_tests, NULL, NULL, NULL, NULL, NULL);
#else
void test_main(void)
{
ztest_test_suite(bacapp_tests,
ztest_unit_test(test_bacapp_decode_application_data),
ztest_unit_test(test_bacapp_decode_data_len),
ztest_unit_test(test_bacapp_copy),
ztest_unit_test(test_bacapp_value_list_init),
ztest_unit_test(test_bacapp_property_value_list_init),
ztest_unit_test(test_bacapp_same_value),
ztest_unit_test(testBACnetApplicationData),
ztest_unit_test(testBACnetApplicationDataLength),
ztest_unit_test(testBACnetApplicationData_Safe),
ztest_unit_test(test_bacapp_context_data),
ztest_unit_test(test_bacapp_sprintf_data)
);
ztest_unit_test(test_bacapp_decode_application_data),
ztest_unit_test(test_bacapp_decode_data_len),
ztest_unit_test(test_bacapp_copy),
ztest_unit_test(test_bacapp_value_list_init),
ztest_unit_test(test_bacapp_property_value_list),
ztest_unit_test(test_bacapp_same_value),
ztest_unit_test(testBACnetApplicationData),
ztest_unit_test(testBACnetApplicationDataLength),
ztest_unit_test(testBACnetApplicationData_Safe),
ztest_unit_test(test_bacapp_context_data),
ztest_unit_test(test_bacapp_sprintf_data));
ztest_run_test_suite(bacapp_tests);
}
-171
View File
@@ -27,177 +27,6 @@
* @{
*/
#if 0
/* Mocks */
void bacapp_value_list_init(
BACNET_APPLICATION_DATA_VALUE *value,
size_t count)
{
}
void bacapp_property_value_list_init(
BACNET_PROPERTY_VALUE *value,
size_t count)
{
}
int bacapp_encode_data(
uint8_t * apdu,
BACNET_APPLICATION_DATA_VALUE * value)
{
return -1;
}
int bacapp_decode_data(
uint8_t * apdu,
uint8_t tag_data_type,
uint32_t len_value_type,
BACNET_APPLICATION_DATA_VALUE * value)
{
return -1;
}
int bacapp_decode_application_data(
uint8_t * apdu,
unsigned max_apdu_len,
BACNET_APPLICATION_DATA_VALUE * value)
{
return -1;
}
bool bacapp_decode_application_data_safe(
uint8_t * new_apdu,
uint32_t new_apdu_len,
BACNET_APPLICATION_DATA_VALUE * value)
{
return false;
}
int bacapp_encode_application_data(
uint8_t * apdu,
BACNET_APPLICATION_DATA_VALUE * value)
{
return -1;
}
int bacapp_decode_context_data(
uint8_t * apdu,
unsigned max_apdu_len,
BACNET_APPLICATION_DATA_VALUE * value,
BACNET_PROPERTY_ID property)
{
return -1;
}
int bacapp_encode_context_data(
uint8_t * apdu,
BACNET_APPLICATION_DATA_VALUE * value,
BACNET_PROPERTY_ID property)
{
return -1;
}
int bacapp_encode_context_data_value(
uint8_t * apdu,
uint8_t context_tag_number,
BACNET_APPLICATION_DATA_VALUE * value)
{
return -1;
}
BACNET_APPLICATION_TAG bacapp_context_tag_type(
BACNET_PROPERTY_ID property,
uint8_t tag_number)
{
return MAX_BACNET_APPLICATION_TAG;
}
bool bacapp_copy(
BACNET_APPLICATION_DATA_VALUE * dest_value,
BACNET_APPLICATION_DATA_VALUE * src_value)
{
return false;
}
int bacapp_data_len(
uint8_t * apdu,
unsigned max_apdu_len,
BACNET_PROPERTY_ID property)
{
return -1;
}
int bacapp_decode_data_len(
uint8_t * apdu,
uint8_t tag_data_type,
uint32_t len_value_type)
{
return -1;
}
int bacapp_decode_application_data_len(
uint8_t * apdu,
unsigned max_apdu_len)
{
return -1;
}
int bacapp_decode_context_data_len(
uint8_t * apdu,
unsigned max_apdu_len,
BACNET_PROPERTY_ID property)
{
return -1;
}
int bacapp_snprintf_value(
char *str,
size_t str_len,
BACNET_OBJECT_PROPERTY_VALUE * object_value)
{
return -1;
}
#ifdef BACAPP_PRINT_ENABLED
bool bacapp_parse_application_data(
BACNET_APPLICATION_TAG tag_number,
const char *argv,
BACNET_APPLICATION_DATA_VALUE * value)
{
return false;
}
bool bacapp_print_value(
FILE * stream,
BACNET_OBJECT_PROPERTY_VALUE * value)
{
return false;
}
#endif
bool bacapp_same_value(
BACNET_APPLICATION_DATA_VALUE * value,
BACNET_APPLICATION_DATA_VALUE * test_value)
{
return false;
}
#endif
/**
* @brief Test
*/
+41 -52
View File
@@ -9,7 +9,7 @@
*/
#include <zephyr/ztest.h>
//#include <bacnet/bacapp.h>
// #include <bacnet/bacapp.h>
#include <bacnet/cov.h>
/**
@@ -127,24 +127,19 @@ static int cov_subscribe_property_decode_apdu(uint8_t *apdu,
}
/* dummy function stubs */
static void testCOVNotifyData(
BACNET_COV_DATA *data, BACNET_COV_DATA *test_data)
static void testCOVNotifyData(BACNET_COV_DATA *data, BACNET_COV_DATA *test_data)
{
BACNET_PROPERTY_VALUE *value = NULL;
BACNET_PROPERTY_VALUE *test_value = NULL;
zassert_equal(
test_data->subscriberProcessIdentifier,
data->subscriberProcessIdentifier, NULL);
zassert_equal(
test_data->initiatingDeviceIdentifier,
data->initiatingDeviceIdentifier, NULL);
zassert_equal(
test_data->monitoredObjectIdentifier.type,
data->monitoredObjectIdentifier.type, NULL);
zassert_equal(
test_data->monitoredObjectIdentifier.instance,
data->monitoredObjectIdentifier.instance, NULL);
zassert_equal(test_data->subscriberProcessIdentifier,
data->subscriberProcessIdentifier, NULL);
zassert_equal(test_data->initiatingDeviceIdentifier,
data->initiatingDeviceIdentifier, NULL);
zassert_equal(test_data->monitoredObjectIdentifier.type,
data->monitoredObjectIdentifier.type, NULL);
zassert_equal(test_data->monitoredObjectIdentifier.instance,
data->monitoredObjectIdentifier.instance, NULL);
zassert_equal(test_data->timeRemaining, data->timeRemaining, NULL);
/* test the listOfValues in some clever manner */
value = data->listOfValues;
@@ -152,10 +147,12 @@ static void testCOVNotifyData(
while (value) {
zassert_not_null(test_value, NULL);
if (test_value) {
zassert_equal(
test_value->propertyIdentifier, value->propertyIdentifier, NULL);
zassert_equal(
test_value->propertyArrayIndex, value->propertyArrayIndex, NULL);
zassert_equal(test_value->propertyIdentifier,
value->propertyIdentifier, "property=%u test_property=%u",
(unsigned)value->propertyIdentifier,
(unsigned)test_value->propertyIdentifier);
zassert_equal(test_value->propertyArrayIndex,
value->propertyArrayIndex, NULL);
zassert_equal(test_value->priority, value->priority, NULL);
zassert_true(
bacapp_same_value(&test_value->value, &value->value), NULL);
@@ -170,14 +167,15 @@ static void testUCOVNotifyData(BACNET_COV_DATA *data)
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
BACNET_COV_DATA test_data;
BACNET_COV_DATA test_data = { 0 };
BACNET_PROPERTY_VALUE value_list[5] = { { 0 } };
len = ucov_notify_encode_apdu(&apdu[0], sizeof(apdu), data);
zassert_true(len > 0, NULL);
apdu_len = len;
cov_data_value_list_link(&test_data, &value_list[0], 5);
cov_data_value_list_link(
&test_data, &value_list[0], ARRAY_SIZE(value_list));
len = ucov_notify_decode_apdu(&apdu[0], apdu_len, &test_data);
zassert_not_equal(len, -1, NULL);
testCOVNotifyData(data, &test_data);
@@ -188,7 +186,7 @@ static void testCCOVNotifyData(uint8_t invoke_id, BACNET_COV_DATA *data)
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
BACNET_COV_DATA test_data;
BACNET_COV_DATA test_data = { 0 };
BACNET_PROPERTY_VALUE value_list[2] = { { 0 } };
uint8_t test_invoke_id = 0;
@@ -239,42 +237,36 @@ static void testCOVNotify(void)
}
static void testCOVSubscribeData(
BACNET_SUBSCRIBE_COV_DATA *data,
BACNET_SUBSCRIBE_COV_DATA *test_data)
BACNET_SUBSCRIBE_COV_DATA *data, BACNET_SUBSCRIBE_COV_DATA *test_data)
{
zassert_equal(test_data->subscriberProcessIdentifier,
data->subscriberProcessIdentifier, NULL);
zassert_equal(test_data->monitoredObjectIdentifier.type,
data->monitoredObjectIdentifier.type, NULL);
zassert_equal(test_data->monitoredObjectIdentifier.instance,
data->monitoredObjectIdentifier.instance, NULL);
zassert_equal(
test_data->subscriberProcessIdentifier,
data->subscriberProcessIdentifier, NULL);
zassert_equal(
test_data->monitoredObjectIdentifier.type,
data->monitoredObjectIdentifier.type, NULL);
zassert_equal(
test_data->monitoredObjectIdentifier.instance,
data->monitoredObjectIdentifier.instance, NULL);
zassert_equal(test_data->cancellationRequest, data->cancellationRequest, NULL);
test_data->cancellationRequest, data->cancellationRequest, NULL);
if (test_data->cancellationRequest != data->cancellationRequest) {
printf("cancellation request failed!\n");
}
if (!test_data->cancellationRequest) {
zassert_equal(
test_data->issueConfirmedNotifications,
data->issueConfirmedNotifications, NULL);
zassert_equal(test_data->issueConfirmedNotifications,
data->issueConfirmedNotifications, NULL);
zassert_equal(test_data->lifetime, data->lifetime, NULL);
}
}
static void testCOVSubscribePropertyData(
BACNET_SUBSCRIBE_COV_DATA *data,
BACNET_SUBSCRIBE_COV_DATA *test_data)
BACNET_SUBSCRIBE_COV_DATA *data, BACNET_SUBSCRIBE_COV_DATA *test_data)
{
testCOVSubscribeData(data, test_data);
zassert_equal(test_data->monitoredProperty.propertyIdentifier,
data->monitoredProperty.propertyIdentifier, NULL);
zassert_equal(test_data->monitoredProperty.propertyArrayIndex,
data->monitoredProperty.propertyArrayIndex, NULL);
zassert_equal(
test_data->monitoredProperty.propertyIdentifier,
data->monitoredProperty.propertyIdentifier, NULL);
zassert_equal(
test_data->monitoredProperty.propertyArrayIndex,
data->monitoredProperty.propertyArrayIndex, NULL);
zassert_equal(test_data->covIncrementPresent, data->covIncrementPresent, NULL);
test_data->covIncrementPresent, data->covIncrementPresent, NULL);
if (test_data->covIncrementPresent) {
zassert_equal(test_data->covIncrement, data->covIncrement, NULL);
}
@@ -286,7 +278,7 @@ static void testCOVSubscribeEncoding(
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
BACNET_SUBSCRIBE_COV_DATA test_data;
BACNET_SUBSCRIBE_COV_DATA test_data = { 0 };
uint8_t test_invoke_id = 0;
len = cov_subscribe_encode_apdu(&apdu[0], sizeof(apdu), invoke_id, data);
@@ -328,7 +320,7 @@ static void testCOVSubscribe(void)
#endif
{
uint8_t invoke_id = 12;
BACNET_SUBSCRIBE_COV_DATA data;
BACNET_SUBSCRIBE_COV_DATA data = { 0 };
data.subscriberProcessIdentifier = 1;
data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT;
@@ -375,17 +367,14 @@ static void testCOVSubscribeProperty(void)
* @}
*/
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST_SUITE(cov_tests, NULL, NULL, NULL, NULL, NULL);
#else
void test_main(void)
{
ztest_test_suite(cov_tests,
ztest_unit_test(testCOVNotify),
ztest_unit_test(testCOVSubscribe),
ztest_unit_test(testCOVSubscribeProperty)
);
ztest_test_suite(cov_tests, ztest_unit_test(testCOVNotify),
ztest_unit_test(testCOVSubscribe),
ztest_unit_test(testCOVSubscribeProperty));
ztest_run_test_suite(cov_tests);
}
+62
View File
@@ -0,0 +1,62 @@
# SPDX-License-Identifier: MIT
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME)
project(test_${basename}
VERSION 1.0.0
LANGUAGES C)
string(REGEX REPLACE
"/test/bacnet/[a-zA-Z_/-]*$"
"/src"
SRC_DIR
${CMAKE_CURRENT_SOURCE_DIR})
string(REGEX REPLACE
"/test/bacnet/[a-zA-Z_/-]*$"
"/test"
TST_DIR
${CMAKE_CURRENT_SOURCE_DIR})
set(ZTST_DIR "${TST_DIR}/ztest/src")
add_compile_definitions(
BIG_ENDIAN=0
CONFIG_ZTEST=1
)
include_directories(
${SRC_DIR}
${TST_DIR}/ztest/include
)
add_executable(${PROJECT_NAME}
# File(s) under test
${SRC_DIR}/bacnet/create_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/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
)
+139
View File
@@ -0,0 +1,139 @@
/**
* @file
* @brief Unit test for CreateObject service encode and decode API
* @author Steve Karg <skarg@users.sourceforge.net>
* @date August 2023
* @section LICENSE
*
* SPDX-License-Identifier: MIT
*/
#include <zephyr/ztest.h>
#include <bacnet/bacdest.h>
#include <bacnet/create_object.h>
static void test_CreateObjectCodec(BACNET_CREATE_OBJECT_DATA *data)
{
uint8_t apdu[MAX_APDU] = { 0 };
BACNET_CREATE_OBJECT_DATA test_data = { 0 };
int len = 0, apdu_len = 0, null_len = 0, test_len = 0;
null_len = create_object_encode_service_request(NULL, data);
apdu_len = create_object_encode_service_request(apdu, data);
zassert_equal(apdu_len, null_len, NULL);
zassert_true(apdu_len != BACNET_STATUS_ERROR, NULL);
null_len = create_object_decode_service_request(apdu, apdu_len, NULL);
test_len = create_object_decode_service_request(apdu, apdu_len, &test_data);
zassert_equal(test_len, null_len, NULL);
zassert_equal(
apdu_len, test_len, "apdu_len=%d test_len=%d", apdu_len, test_len);
while (test_len) {
test_len--;
len = create_object_decode_service_request(apdu, test_len, &test_data);
zassert_equal(
len, BACNET_STATUS_REJECT, "len=%d test_len=%d", len, test_len);
}
}
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(create_object_tests, test_CreateObject)
#else
static void test_CreateObject(void)
#endif
{
BACNET_CREATE_OBJECT_DATA data = { 0 };
int len = 0, apdu_len = 0, null_len = 0, test_len = 0;
test_CreateObjectCodec(&data);
data.object_instance = BACNET_MAX_INSTANCE;
test_CreateObjectCodec(&data);
}
static void test_CreateObjectAckCodec(BACNET_CREATE_OBJECT_DATA *data)
{
uint8_t apdu[MAX_APDU] = { 0 };
BACNET_CREATE_OBJECT_DATA test_data = { 0 };
int len = 0, apdu_len = 0, null_len = 0, test_len = 0;
uint8_t invoke_id = 0;
null_len = create_object_ack_service_encode(NULL, data);
apdu_len = create_object_ack_service_encode(apdu, data);
zassert_equal(apdu_len, null_len, NULL);
zassert_true(apdu_len != BACNET_STATUS_ERROR, NULL);
null_len = create_object_ack_service_decode(apdu, apdu_len, NULL);
test_len = create_object_ack_service_decode(apdu, apdu_len, &test_data);
zassert_equal(test_len, null_len, NULL);
zassert_equal(
apdu_len, test_len, "apdu_len=%d test_len=%d", apdu_len, test_len);
while (test_len) {
test_len--;
len = create_object_ack_service_decode(apdu, test_len, &test_data);
zassert_equal(
len, BACNET_STATUS_ERROR, "len=%d test_len=%d", len, test_len);
}
null_len = create_object_ack_encode(NULL, invoke_id, data);
apdu_len = create_object_ack_encode(apdu, invoke_id, data);
zassert_equal(apdu_len, null_len, NULL);
zassert_true(apdu_len > 0, NULL);
}
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(create_object_tests, test_CreateObject)
#else
static void test_CreateObjectACK(void)
#endif
{
BACNET_CREATE_OBJECT_DATA data = { 0 };
int len = 0, apdu_len = 0, null_len = 0, test_len = 0;
test_CreateObjectAckCodec(&data);
data.object_instance = BACNET_MAX_INSTANCE;
test_CreateObjectAckCodec(&data);
}
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(create_object_tests, test_CreateObjectError)
#else
static void test_CreateObjectError(void)
#endif
{
uint8_t apdu[MAX_APDU] = { 0 };
BACNET_CREATE_OBJECT_DATA data = { 0 }, test_data = { 0 };
int len = 0, apdu_len = 0, null_len = 0, test_len = 0;
uint8_t invoke_id = 0;
data.error_class = ERROR_CLASS_SERVICES;
data.error_code = ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE;
data.first_failed_element_number = 0;
null_len = create_object_error_ack_service_encode(NULL, &data);
apdu_len = create_object_error_ack_service_encode(apdu, &data);
zassert_equal(apdu_len, null_len, NULL);
test_len = create_object_error_ack_service_decode(apdu, apdu_len, &test_data);
zassert_equal(apdu_len, test_len, NULL);
zassert_equal(test_data.error_class, data.error_class, NULL);
zassert_equal(test_data.error_code, data.error_code, NULL);
zassert_equal(test_data.first_failed_element_number,
data.first_failed_element_number, NULL);
while (test_len) {
test_len--;
len = create_object_error_ack_service_decode(apdu, test_len, &test_data);
zassert_equal(
len, BACNET_STATUS_REJECT, "len=%d test_len=%d", len, test_len);
}
null_len = create_object_error_ack_encode(NULL, invoke_id, &data);
apdu_len = create_object_error_ack_encode(apdu, invoke_id, &data);
zassert_equal(apdu_len, null_len, NULL);
zassert_true(apdu_len > 0, NULL);
}
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST_SUITE(create_object_tests, NULL, NULL, NULL, NULL, NULL);
#else
void test_main(void)
{
ztest_test_suite(create_object_tests, ztest_unit_test(test_CreateObject),
ztest_unit_test(test_CreateObjectACK),
ztest_unit_test(test_CreateObjectError));
ztest_run_test_suite(create_object_tests);
}
#endif
+62
View File
@@ -0,0 +1,62 @@
# SPDX-License-Identifier: MIT
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME)
project(test_${basename}
VERSION 1.0.0
LANGUAGES C)
string(REGEX REPLACE
"/test/bacnet/[a-zA-Z_/-]*$"
"/src"
SRC_DIR
${CMAKE_CURRENT_SOURCE_DIR})
string(REGEX REPLACE
"/test/bacnet/[a-zA-Z_/-]*$"
"/test"
TST_DIR
${CMAKE_CURRENT_SOURCE_DIR})
set(ZTST_DIR "${TST_DIR}/ztest/src")
add_compile_definitions(
BIG_ENDIAN=0
CONFIG_ZTEST=1
)
include_directories(
${SRC_DIR}
${TST_DIR}/ztest/include
)
add_executable(${PROJECT_NAME}
# File(s) under test
${SRC_DIR}/bacnet/delete_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/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
)
+60
View File
@@ -0,0 +1,60 @@
/**
* @file
* @brief Unit test for DeleteObject service encode and decode API
* @author Steve Karg <skarg@users.sourceforge.net>
* @date August 2023
* @section LICENSE
*
* SPDX-License-Identifier: MIT
*/
#include <zephyr/ztest.h>
#include <bacnet/bacdest.h>
#include <bacnet/delete_object.h>
static void test_DeleteObjectCodec(BACNET_DELETE_OBJECT_DATA *data)
{
uint8_t apdu[MAX_APDU] = { 0 };
BACNET_DELETE_OBJECT_DATA test_data = { 0 };
int len = 0, apdu_len = 0, null_len = 0, test_len = 0;
null_len = delete_object_encode_service_request(NULL, data);
apdu_len = delete_object_encode_service_request(apdu, data);
zassert_equal(apdu_len, null_len, NULL);
zassert_true(apdu_len != BACNET_STATUS_ERROR, NULL);
null_len = delete_object_decode_service_request(apdu, apdu_len, NULL);
test_len = delete_object_decode_service_request(apdu, apdu_len, &test_data);
zassert_equal(test_len, null_len, NULL);
zassert_equal(
apdu_len, test_len, "apdu_len=%d test_len=%d", apdu_len, test_len);
while (test_len) {
test_len--;
len = delete_object_decode_service_request(apdu, test_len, &test_data);
zassert_equal(
len, BACNET_STATUS_REJECT, "len=%d test_len=%d", len, test_len);
}
}
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(delete_object_tests, test_DeleteObject)
#else
static void test_DeleteObject(void)
#endif
{
BACNET_DELETE_OBJECT_DATA data = { 0 };
int len = 0, apdu_len = 0, null_len = 0, test_len = 0;
test_DeleteObjectCodec(&data);
data.object_instance = BACNET_MAX_INSTANCE;
test_DeleteObjectCodec(&data);
}
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST_SUITE(delete_object_tests, NULL, NULL, NULL, NULL, NULL);
#else
void test_main(void)
{
ztest_test_suite(delete_object_tests, ztest_unit_test(test_DeleteObject));
ztest_run_test_suite(delete_object_tests);
}
#endif
+1 -1
View File
@@ -6,7 +6,7 @@
*
* SPDX-License-Identifier: MIT
*/
#include <ztest.h>
#include <zephyr/ztest.h>
#include <bacnet/bacdest.h>
#include <bacnet/list_element.h>