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:
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
|
||||
+249
-20
@@ -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) {
|
||||
@@ -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",
|
||||
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);
|
||||
(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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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))
|
||||
|
||||
+205
-210
@@ -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);
|
||||
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);
|
||||
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[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,165 +208,121 @@ 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;
|
||||
}
|
||||
len += value_len;
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* tag 1 - initiatingDeviceIdentifier */
|
||||
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_object_id(
|
||||
&apdu[len], &decoded_type, &data->initiatingDeviceIdentifier);
|
||||
/* 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;
|
||||
}
|
||||
if (data) {
|
||||
data->initiatingDeviceIdentifier = decoded_instance;
|
||||
}
|
||||
len += value_len;
|
||||
} 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);
|
||||
/* 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;
|
||||
data->monitoredObjectIdentifier.instance = decoded_instance;
|
||||
}
|
||||
len += value_len;
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* 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);
|
||||
/* 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;
|
||||
}
|
||||
/* 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++;
|
||||
/* 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;
|
||||
if (value == NULL) {
|
||||
/* no space to store any values */
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
while (value != NULL) {
|
||||
/* tag 0 - propertyIdentifier */
|
||||
if (len >= (int)apdu_len) {
|
||||
value_len = bacapp_property_value_decode(
|
||||
&apdu[len], apdu_size - len, value);
|
||||
if (value_len == BACNET_STATUS_ERROR) {
|
||||
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;
|
||||
len += value_len;
|
||||
}
|
||||
/* end of list? */
|
||||
if (decode_is_closing_tag_number(&apdu[len], 4)) {
|
||||
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 more values */
|
||||
/* out of room to store next value */
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
} 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. */
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
@@ -442,78 +404,111 @@ 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;
|
||||
/* 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;
|
||||
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_object_id(&apdu[len], &decoded_type,
|
||||
&data->monitoredObjectIdentifier.instance);
|
||||
/* 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;
|
||||
data->monitoredObjectIdentifier.instance = decoded_instance;
|
||||
}
|
||||
len += value_len;
|
||||
} else {
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
/* optional parameters - if missing, means cancellation */
|
||||
if ((unsigned)len < apdu_len) {
|
||||
/* tag 2 - issueConfirmedNotifications - optional */
|
||||
if (decode_is_context_tag(&apdu[len], 2)) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
if ((unsigned)len < apdu_size) {
|
||||
if (data) {
|
||||
/* does not indicate a cancellation request */
|
||||
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;
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
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 {
|
||||
data->lifetime = 0;
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
|
||||
}
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
} else {
|
||||
if (data) {
|
||||
data->lifetime = 0;
|
||||
}
|
||||
} else {
|
||||
data->cancellationRequest = true;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
+90
-200
@@ -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,8 +853,8 @@ int event_notify_decode_service_request(
|
||||
len++;
|
||||
|
||||
if (-1 ==
|
||||
(section_length =
|
||||
decode_context_bitstring(&apdu[len], 1,
|
||||
(section_length = decode_context_bitstring(
|
||||
&apdu[len], 1,
|
||||
&data->notificationParams.commandFailure
|
||||
.statusFlags))) {
|
||||
return -1;
|
||||
@@ -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,7 +947,8 @@ int event_notify_decode_service_request(
|
||||
|
||||
case EVENT_OUT_OF_RANGE:
|
||||
if (-1 ==
|
||||
(section_length = decode_context_real(&apdu[len], 0,
|
||||
(section_length =
|
||||
decode_context_real(&apdu[len], 0,
|
||||
&data->notificationParams.outOfRange
|
||||
.exceedingValue))) {
|
||||
return -1;
|
||||
@@ -1084,7 +964,8 @@ int event_notify_decode_service_request(
|
||||
}
|
||||
len += section_length;
|
||||
if (-1 ==
|
||||
(section_length = decode_context_real(&apdu[len], 2,
|
||||
(section_length =
|
||||
decode_context_real(&apdu[len], 2,
|
||||
&data->notificationParams.outOfRange
|
||||
.deadband))) {
|
||||
return -1;
|
||||
@@ -1092,7 +973,8 @@ int event_notify_decode_service_request(
|
||||
len += section_length;
|
||||
|
||||
if (-1 ==
|
||||
(section_length = decode_context_real(&apdu[len], 3,
|
||||
(section_length =
|
||||
decode_context_real(&apdu[len], 3,
|
||||
&data->notificationParams.outOfRange
|
||||
.exceededLimit))) {
|
||||
return -1;
|
||||
@@ -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,15 +1120,15 @@ 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,
|
||||
(section_length = decode_context_unsigned(
|
||||
&apdu[len], 2,
|
||||
&data->notificationParams.accessEvent
|
||||
.accessEventTag))) {
|
||||
return -1;
|
||||
@@ -1250,9 +1136,11 @@ int event_notify_decode_service_request(
|
||||
len += section_length;
|
||||
|
||||
if (-1 ==
|
||||
(section_length = bacapp_decode_context_timestamp(
|
||||
(section_length =
|
||||
bacapp_decode_context_timestamp(
|
||||
&apdu[len], 3,
|
||||
&data->notificationParams.accessEvent
|
||||
&data->notificationParams
|
||||
.accessEvent
|
||||
.accessEventTime))) {
|
||||
return -1;
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
+113
-58
@@ -74,12 +74,13 @@ static void test_bacapp_decode_application_data(void)
|
||||
// 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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
zassert_equal(bacapp_decode_data_len(
|
||||
apdu, BACNET_APPLICATION_TAG_OBJECT_ID, len_value_type),
|
||||
expected_value, NULL);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
@@ -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,7 +361,6 @@ 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 */
|
||||
@@ -303,7 +372,6 @@ 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 */
|
||||
@@ -315,7 +383,6 @@ 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 */
|
||||
@@ -327,7 +394,6 @@ 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 */
|
||||
@@ -339,7 +405,6 @@ 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 */
|
||||
@@ -351,7 +416,6 @@ 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 */
|
||||
@@ -379,7 +443,6 @@ static void test_bacapp_same_value(void)
|
||||
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 */
|
||||
@@ -405,7 +468,6 @@ static void test_bacapp_same_value(void)
|
||||
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 */
|
||||
@@ -423,7 +485,6 @@ static void test_bacapp_same_value(void)
|
||||
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 */
|
||||
@@ -434,7 +495,6 @@ static void test_bacapp_same_value(void)
|
||||
#endif
|
||||
// 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 */
|
||||
@@ -445,7 +505,6 @@ static void test_bacapp_same_value(void)
|
||||
#endif
|
||||
// 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 */
|
||||
@@ -456,7 +515,6 @@ static void test_bacapp_same_value(void)
|
||||
#endif
|
||||
// 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 */
|
||||
@@ -468,7 +526,6 @@ static void test_bacapp_same_value(void)
|
||||
// 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;
|
||||
}
|
||||
@@ -1077,13 +1134,13 @@ static void test_bacapp_context_data(void)
|
||||
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);
|
||||
}
|
||||
@@ -1124,7 +1181,6 @@ static void test_bacapp_sprintf_data(void)
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST_SUITE(bacapp_tests, NULL, NULL, NULL, NULL, NULL);
|
||||
#else
|
||||
@@ -1135,14 +1191,13 @@ void test_main(void)
|
||||
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_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_unit_test(test_bacapp_sprintf_data));
|
||||
|
||||
ztest_run_test_suite(bacapp_tests);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
+31
-42
@@ -127,23 +127,18 @@ 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,
|
||||
zassert_equal(test_data->subscriberProcessIdentifier,
|
||||
data->subscriberProcessIdentifier, NULL);
|
||||
zassert_equal(
|
||||
test_data->initiatingDeviceIdentifier,
|
||||
zassert_equal(test_data->initiatingDeviceIdentifier,
|
||||
data->initiatingDeviceIdentifier, NULL);
|
||||
zassert_equal(
|
||||
test_data->monitoredObjectIdentifier.type,
|
||||
zassert_equal(test_data->monitoredObjectIdentifier.type,
|
||||
data->monitoredObjectIdentifier.type, NULL);
|
||||
zassert_equal(
|
||||
test_data->monitoredObjectIdentifier.instance,
|
||||
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 */
|
||||
@@ -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,
|
||||
zassert_equal(test_data->subscriberProcessIdentifier,
|
||||
data->subscriberProcessIdentifier, NULL);
|
||||
zassert_equal(
|
||||
test_data->monitoredObjectIdentifier.type,
|
||||
zassert_equal(test_data->monitoredObjectIdentifier.type,
|
||||
data->monitoredObjectIdentifier.type, NULL);
|
||||
zassert_equal(
|
||||
test_data->monitoredObjectIdentifier.instance,
|
||||
zassert_equal(test_data->monitoredObjectIdentifier.instance,
|
||||
data->monitoredObjectIdentifier.instance, NULL);
|
||||
zassert_equal(test_data->cancellationRequest, data->cancellationRequest, NULL);
|
||||
zassert_equal(
|
||||
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,
|
||||
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,
|
||||
zassert_equal(test_data->monitoredProperty.propertyIdentifier,
|
||||
data->monitoredProperty.propertyIdentifier, NULL);
|
||||
zassert_equal(
|
||||
test_data->monitoredProperty.propertyArrayIndex,
|
||||
zassert_equal(test_data->monitoredProperty.propertyArrayIndex,
|
||||
data->monitoredProperty.propertyArrayIndex, NULL);
|
||||
zassert_equal(test_data->covIncrementPresent, data->covIncrementPresent, NULL);
|
||||
zassert_equal(
|
||||
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_test_suite(cov_tests, ztest_unit_test(testCOVNotify),
|
||||
ztest_unit_test(testCOVSubscribe),
|
||||
ztest_unit_test(testCOVSubscribeProperty)
|
||||
);
|
||||
ztest_unit_test(testCOVSubscribeProperty));
|
||||
|
||||
ztest_run_test_suite(cov_tests);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
@@ -6,7 +6,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <ztest.h>
|
||||
#include <zephyr/ztest.h>
|
||||
#include <bacnet/bacdest.h>
|
||||
#include <bacnet/list_element.h>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user