Added WriteGroup service and Channel object interfaces (#829)
This commit is contained in:
@@ -249,6 +249,8 @@ add_library(${PROJECT_NAME}
|
||||
src/bacnet/bactext.h
|
||||
src/bacnet/bactimevalue.c
|
||||
src/bacnet/bactimevalue.h
|
||||
src/bacnet/channel_value.c
|
||||
src/bacnet/channel_value.h
|
||||
src/bacnet/dailyschedule.c
|
||||
src/bacnet/dailyschedule.h
|
||||
src/bacnet/weeklyschedule.c
|
||||
@@ -418,6 +420,8 @@ add_library(${PROJECT_NAME}
|
||||
src/bacnet/basic/service/h_wp.h
|
||||
src/bacnet/basic/service/h_wpm.c
|
||||
src/bacnet/basic/service/h_wpm.h
|
||||
src/bacnet/basic/service/h_write_group.c
|
||||
src/bacnet/basic/service/h_write_group.h
|
||||
src/bacnet/basic/service/s_abort.c
|
||||
src/bacnet/basic/service/s_abort.h
|
||||
src/bacnet/basic/service/s_ack_alarm.c
|
||||
@@ -474,6 +478,8 @@ add_library(${PROJECT_NAME}
|
||||
src/bacnet/basic/service/s_wp.h
|
||||
src/bacnet/basic/service/s_wpm.c
|
||||
src/bacnet/basic/service/s_wpm.h
|
||||
src/bacnet/basic/service/s_write_group.c
|
||||
src/bacnet/basic/service/s_write_group.h
|
||||
src/bacnet/basic/services.h
|
||||
src/bacnet/basic/sys/bigend.c
|
||||
src/bacnet/basic/sys/bigend.h
|
||||
@@ -594,6 +600,8 @@ add_library(${PROJECT_NAME}
|
||||
src/bacnet/wp.h
|
||||
src/bacnet/wpm.c
|
||||
src/bacnet/wpm.h
|
||||
src/bacnet/write_group.c
|
||||
src/bacnet/write_group.h
|
||||
$<$<BOOL:${UCI}>:src/bacnet/basic/ucix/ucix.c>
|
||||
$<$<BOOL:${UCI}>:src/bacnet/basic/ucix/ucix.h>)
|
||||
target_sources(
|
||||
@@ -1001,6 +1009,9 @@ if(BACNET_STACK_BUILD_APPS)
|
||||
add_executable(writefile apps/writefile/main.c)
|
||||
target_link_libraries(writefile PRIVATE ${PROJECT_NAME})
|
||||
|
||||
add_executable(writegroup apps/writegroup/main.c)
|
||||
target_link_libraries(writegroup PRIVATE ${PROJECT_NAME})
|
||||
|
||||
add_executable(writeprop apps/writeprop/main.c)
|
||||
target_link_libraries(writeprop PRIVATE ${PROJECT_NAME})
|
||||
|
||||
|
||||
@@ -204,6 +204,10 @@ whois:
|
||||
writepropm:
|
||||
$(MAKE) -s -C apps $@
|
||||
|
||||
.PHONY: writegroup
|
||||
writegroup:
|
||||
$(MAKE) -s -C apps $@
|
||||
|
||||
.PHONY: router
|
||||
router:
|
||||
$(MAKE) -s -C apps $@
|
||||
|
||||
+5
-1
@@ -206,7 +206,7 @@ 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 create-object \
|
||||
delete-object server-discover apdu
|
||||
delete-object server-discover apdu writegroup
|
||||
|
||||
ifeq (${BACDL_DEFINE},-DBACDL_BIP=1)
|
||||
SUBDIRS += whoisrouter iamrouter initrouter whatisnetnum netnumis
|
||||
@@ -458,3 +458,7 @@ fuzz-afl: $(BACNET_LIB_TARGET)
|
||||
.PHONY: writepropm
|
||||
writepropm: $(BACNET_LIB_TARGET)
|
||||
$(MAKE) -B -C $@
|
||||
|
||||
.PHONY: writegroup
|
||||
writegroup: $(BACNET_LIB_TARGET)
|
||||
$(MAKE) -B -C $@
|
||||
|
||||
+7
-15
@@ -37,6 +37,7 @@
|
||||
/* objects that have tasks inside them */
|
||||
#if (BACNET_PROTOCOL_REVISION >= 14)
|
||||
#include "bacnet/basic/object/lo.h"
|
||||
#include "bacnet/basic/object/channel.h"
|
||||
#endif
|
||||
#if (BACNET_PROTOCOL_REVISION >= 24)
|
||||
#include "bacnet/basic/object/color_object.h"
|
||||
@@ -117,6 +118,7 @@ static BACNET_SUBORDINATE_DATA Lighting_Subordinate[] = {
|
||||
{ 0, OBJECT_COLOR_TEMPERATURE, 1, "color-temperature", 0, 0, NULL },
|
||||
#endif
|
||||
};
|
||||
static BACNET_WRITE_GROUP_NOTIFICATION Write_Group_Notification = { 0 };
|
||||
|
||||
/**
|
||||
* @brief Update the strcutured view static data with device ID and linked lists
|
||||
@@ -177,21 +179,6 @@ static void Init_Service_Handlers(void)
|
||||
/* we need to handle who-is to support dynamic device binding */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has);
|
||||
|
||||
#if 0
|
||||
/* BACnet Testing Observed Incident oi00107
|
||||
Server only devices should not indicate that they EXECUTE I-Am
|
||||
Revealed by BACnet Test Client v1.8.16 ( www.bac-test.com/bacnet-test-client-download )
|
||||
BITS: BIT00040
|
||||
Any discussions can be directed to edward@bac-test.com
|
||||
Please feel free to remove this comment when my changes accepted after suitable time for
|
||||
review by all interested parties. Say 6 months -> September 2016 */
|
||||
/* In this demo, we are the server only ( BACnet "B" device ) so we do not indicate
|
||||
that we can execute the I-Am message */
|
||||
/* handle i-am to support binding to other devices */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind);
|
||||
#endif
|
||||
|
||||
/* 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);
|
||||
@@ -231,6 +218,11 @@ static void Init_Service_Handlers(void)
|
||||
apdu_set_unconfirmed_handler(
|
||||
SERVICE_UNCONFIRMED_PRIVATE_TRANSFER,
|
||||
handler_unconfirmed_private_transfer);
|
||||
apdu_set_unconfirmed_handler(
|
||||
SERVICE_UNCONFIRMED_WRITE_GROUP, handler_write_group);
|
||||
/* add WriteGroup iterator to the Channel objects */
|
||||
Write_Group_Notification.callback = Channel_Write_Group;
|
||||
handler_write_group_notification_add(&Write_Group_Notification);
|
||||
#if defined(INTRINSIC_REPORTING)
|
||||
apdu_set_confirmed_handler(
|
||||
SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM, handler_alarm_ack);
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#Makefile to build BACnet Application using GCC compiler
|
||||
|
||||
# Executable file name
|
||||
TARGET = bacwg
|
||||
# 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,207 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief command line tool that sends a BACnet WriteGroup-Request message
|
||||
* to the network
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date October 2024
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h> /* for time */
|
||||
#include <errno.h>
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/iam.h"
|
||||
#include "bacnet/cov.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/channel_value.h"
|
||||
#include "bacnet/write_group.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/sys/filename.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/datalink/dlenv.h"
|
||||
|
||||
static BACNET_WRITE_GROUP_DATA Write_Group_Data;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void print_usage(const char *filename)
|
||||
{
|
||||
printf("Sends a BACnet WriteGroup-Reqeust to the network.\n");
|
||||
printf("\n");
|
||||
printf(
|
||||
"Usage: %s group-number priority <inhibit|delay>\n"
|
||||
"change-value [change-value]\n",
|
||||
filename);
|
||||
printf("\n");
|
||||
printf("group-number:\n"
|
||||
"parameter in the range 1-4294967295 that represents\n"
|
||||
" the control group to be affected by this request.\n");
|
||||
printf("\n");
|
||||
printf("priority:\n"
|
||||
"This Write_Priority parameter is an unsigned integer\n"
|
||||
"in the range 1..16 that represents the priority for writing\n"
|
||||
"that shall apply to any channel value changes that result\n"
|
||||
"in writes to properties of BACnet objects.\n");
|
||||
printf("\n");
|
||||
printf("change-value:\n"
|
||||
"This parameter shall specify a BACnetGroupChannelValue\n"
|
||||
"consisting of channel number, overridingPriority, value\n"
|
||||
"tuples representing each channel number whose value is\n"
|
||||
"to be updated.");
|
||||
printf("Since List_Of_Object_Property_References can include\n"
|
||||
"object properties of different data types, the value\n"
|
||||
"written to Present_Value may be coerced to another datatype.\n"
|
||||
"The rules governing how these coercions occur are\n"
|
||||
"defined in the BACnet standard.\n");
|
||||
printf("\n");
|
||||
printf("change-value: channel number\n");
|
||||
printf("Channel numbers shall range from 0 to 65535\n"
|
||||
"where the channel number corresponds directly to the\n"
|
||||
"Channel_Number property of a Channel object.");
|
||||
printf("\n");
|
||||
printf("change-value: overridingPriority\n"
|
||||
"The optional overridingPriority allows specific values\n"
|
||||
"to be written with some priority other than that specified\n"
|
||||
"by Write_Priority property. If overridingPriority 0 is given,\n"
|
||||
"no priority is sent.\n");
|
||||
printf("\n");
|
||||
printf("change-value: value\n"
|
||||
"BACnetChannelValue values that are any primitive application\n"
|
||||
"datatype or BACnetLightingCommand or BACnetColorCommand or\n"
|
||||
"BACnetXYColor constructed datatypes. The NULL value represents\n"
|
||||
"'relinquish control' as with commandable object properties.\n");
|
||||
printf("\n");
|
||||
printf("The numeric values are parsed in the following manner:\n"
|
||||
"null=Null, true or false=Boolean,\n"
|
||||
"numeric with negative sign=Signed Integer,\n"
|
||||
"numeric with decimal point=Real or Double\n"
|
||||
"Ltuple=BACnetLightingCommand\n"
|
||||
"Ctuple=BACnetColorCommand\n"
|
||||
"Xtuple=BACnetXYColor\n");
|
||||
printf("\n");
|
||||
printf(
|
||||
"Example:\n"
|
||||
"If you want generate a WriteGroup-Request,\n"
|
||||
"you could send one of the following command:\n"
|
||||
"%s 1 2 inhibit 3 0 100.0 4 0 null 5 0 -100 6 0 true 7 0 10\n"
|
||||
"where 1=group-number, 2=priority, 3=channel-number,\n"
|
||||
"0=overridingPriority, 5=channel-number, 6=channel-number,\n"
|
||||
"7=channel-number\n",
|
||||
filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cleanup the resources used by the application
|
||||
*/
|
||||
static void cleanup(void)
|
||||
{
|
||||
unsigned count;
|
||||
|
||||
count = bacnet_write_group_change_list_count(&Write_Group_Data);
|
||||
while (count) {
|
||||
BACNET_GROUP_CHANNEL_VALUE *value;
|
||||
value =
|
||||
bacnet_write_group_change_list_element(&Write_Group_Data, count);
|
||||
if (count == 0) {
|
||||
/* index=0 change-value is static head, not dynamic */
|
||||
value->next = NULL;
|
||||
} else {
|
||||
free(value);
|
||||
}
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send a WriteGroup-Request to the network
|
||||
* @param argc [in] The number of command line arguments
|
||||
* @param argv [in] The command line arguments
|
||||
* @return 0 on success, 1 on failure
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
BACNET_ADDRESS dest = { 0 /*broadcast*/ };
|
||||
BACNET_WRITE_GROUP_DATA *data;
|
||||
BACNET_GROUP_CHANNEL_VALUE *value;
|
||||
int argi = 0;
|
||||
int len = 0;
|
||||
|
||||
if (argc < 4) {
|
||||
print_usage(filename_remove_path(argv[0]));
|
||||
return 0;
|
||||
}
|
||||
data = &Write_Group_Data;
|
||||
/* decode the command line parameters */
|
||||
data->group_number = strtol(argv[1], NULL, 0);
|
||||
data->write_priority = strtol(argv[2], NULL, 0);
|
||||
if (strcasecmp(argv[3], "inhibit") == 0) {
|
||||
data->inhibit_delay = WRITE_GROUP_INHIBIT_DELAY_TRUE;
|
||||
} else if (strcasecmp(argv[3], "delay") == 0) {
|
||||
data->inhibit_delay = WRITE_GROUP_INHIBIT_DELAY_FALSE;
|
||||
} else {
|
||||
data->inhibit_delay = WRITE_GROUP_INHIBIT_DELAY_FALSE;
|
||||
}
|
||||
value = &data->change_list;
|
||||
for (argi = 4; argi < argc; argi++) {
|
||||
if (!value) {
|
||||
value = calloc(1, sizeof(BACNET_GROUP_CHANNEL_VALUE));
|
||||
bacnet_write_group_change_list_append(data, value);
|
||||
}
|
||||
if (value) {
|
||||
value->channel = strtol(argv[argi], NULL, 0);
|
||||
argi++;
|
||||
value->overriding_priority = strtol(argv[argi], NULL, 0);
|
||||
argi++;
|
||||
if (!bacnet_channel_value_from_ascii(&value->value, argv[argi])) {
|
||||
value->value.tag = BACNET_APPLICATION_TAG_NULL;
|
||||
}
|
||||
printf(
|
||||
"WriteGroup-Request added channel %u "
|
||||
"with priority %u value=%s tag=%s\n",
|
||||
value->channel, value->overriding_priority, argv[argi],
|
||||
bactext_application_tag_name(value->value.tag));
|
||||
value = value->next;
|
||||
}
|
||||
}
|
||||
atexit(cleanup);
|
||||
/* setup my info */
|
||||
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
|
||||
Init_Service_Handlers();
|
||||
dlenv_init();
|
||||
atexit(datalink_cleanup);
|
||||
len = Send_Write_Group(&dest, data);
|
||||
if (len <= 0) {
|
||||
fprintf(
|
||||
stderr, "Failed to Send WriteGroup-Request (%s)!\n",
|
||||
strerror(errno));
|
||||
} else {
|
||||
printf("Send WriteGroup-Request successful!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
+417
-11
@@ -33,6 +33,7 @@
|
||||
#include "bacnet/weeklyschedule.h"
|
||||
#include "bacnet/calendar_entry.h"
|
||||
#include "bacnet/special_event.h"
|
||||
#include "bacnet/channel_value.h"
|
||||
#include "bacnet/basic/sys/platform.h"
|
||||
|
||||
#if defined(BACAPP_SCALE)
|
||||
@@ -510,6 +511,13 @@ int bacapp_encode_application_data(
|
||||
apdu_len =
|
||||
bacapp_encode_access_rule(apdu, &value->type.Access_Rule);
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_CHANNEL_VALUE)
|
||||
case BACNET_APPLICATION_TAG_CHANNEL_VALUE:
|
||||
/* BACnetChannelValue */
|
||||
apdu_len = bacnet_channel_value_type_encode(
|
||||
apdu, &value->type.Channel_Value);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
@@ -1221,6 +1229,9 @@ int bacapp_known_property_tag(
|
||||
(object_type == OBJECT_DATETIME_VALUE)) {
|
||||
/* Properties using BACnetDateTime */
|
||||
return BACNET_APPLICATION_TAG_DATETIME;
|
||||
} else if (object_type == OBJECT_CHANNEL) {
|
||||
/* Properties using BACnetChannelValue */
|
||||
return BACNET_APPLICATION_TAG_CHANNEL_VALUE;
|
||||
}
|
||||
/* note: primitive application tagged present-values return '-1' */
|
||||
return -1;
|
||||
@@ -1607,6 +1618,13 @@ int bacapp_decode_application_tag_value(
|
||||
apdu_len = bacnet_access_rule_decode(
|
||||
apdu, apdu_size, &value->type.Access_Rule);
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_CHANNEL_VALUE)
|
||||
case BACNET_APPLICATION_TAG_CHANNEL_VALUE:
|
||||
/* BACnetChannelValue */
|
||||
apdu_len = bacnet_channel_value_decode(
|
||||
apdu, apdu_size, &value->type.Channel_Value);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
@@ -2565,6 +2583,255 @@ static int bacapp_snprintf_access_rule(
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BACAPP_COLOR_COMMAND)
|
||||
/**
|
||||
* @brief Print a value to a string for EPICS
|
||||
* @param str - destination string, or NULL for length only
|
||||
* @param str_len - length of the destination string, or 0 for length only
|
||||
* @param value - value to be printed
|
||||
* @return number of characters written to the string
|
||||
*/
|
||||
static int bacapp_snprintf_color_command(
|
||||
char *str, size_t str_len, const BACNET_COLOR_COMMAND *value)
|
||||
{
|
||||
int slen;
|
||||
int ret_val = 0;
|
||||
|
||||
slen = bacapp_snprintf(str, str_len, "{");
|
||||
ret_val += bacapp_snprintf_shift(slen, &str, &str_len);
|
||||
slen = bacapp_snprintf(str, str_len, "(");
|
||||
ret_val += bacapp_snprintf_shift(slen, &str, &str_len);
|
||||
slen = bacapp_snprintf(
|
||||
str, str_len, "%s", bactext_color_operation_name(value->operation));
|
||||
ret_val += bacapp_snprintf_shift(slen, &str, &str_len);
|
||||
slen = bacapp_snprintf(str, str_len, ")");
|
||||
ret_val += bacapp_snprintf_shift(slen, &str, &str_len);
|
||||
|
||||
/* FIXME: add the Color Command optional values */
|
||||
|
||||
slen = bacapp_snprintf(str, str_len, "}");
|
||||
ret_val += bacapp_snprintf_shift(slen, &str, &str_len);
|
||||
return ret_val;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BACAPP_CHANNEL_VALUE)
|
||||
/**
|
||||
* @brief Print a value to a string for EPICS
|
||||
* @param str - destination string, or NULL for length only
|
||||
* @param str_len - length of the destination string, or 0 for length only
|
||||
* @param value - value to be printed
|
||||
* @return number of characters written to the string
|
||||
*/
|
||||
static int bacapp_snprintf_channel_value(
|
||||
char *str, size_t str_len, const BACNET_CHANNEL_VALUE *value)
|
||||
{
|
||||
int ret_val = 0;
|
||||
|
||||
switch (value->tag) {
|
||||
case BACNET_APPLICATION_TAG_NULL:
|
||||
ret_val = bacapp_snprintf_null(str, str_len);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_BOOLEAN:
|
||||
#if defined(CHANNEL_BOOLEAN)
|
||||
ret_val =
|
||||
bacapp_snprintf_boolean(str, str_len, value->type.Boolean);
|
||||
#endif
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_UNSIGNED_INT:
|
||||
#if defined(CHANNEL_UNSIGNED)
|
||||
ret_val = bacapp_snprintf_unsigned_integer(
|
||||
str, str_len, value->type.Unsigned_Int);
|
||||
#endif
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_SIGNED_INT:
|
||||
#if defined(CHANNEL_SIGNED)
|
||||
ret_val = bacapp_snprintf_signed_integer(
|
||||
str, str_len, value->type.Signed_Int);
|
||||
#endif
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_REAL:
|
||||
#if defined(CHANNEL_REAL)
|
||||
ret_val = bacapp_snprintf_real(str, str_len, value->type.Real);
|
||||
#endif
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_DOUBLE:
|
||||
#if defined(CHANNEL_DOUBLE)
|
||||
ret_val = bacapp_snprintf_double(str, str_len, value->type.Double);
|
||||
#endif
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_ENUMERATED:
|
||||
#if defined(CHANNEL_ENUMERATED)
|
||||
ret_val = bacapp_snprintf_enumerated(
|
||||
str, str_len, OBJECT_COMMAND, PROP_ACTION,
|
||||
value->type.Enumerated);
|
||||
#endif
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_LIGHTING_COMMAND:
|
||||
#if defined(CHANNEL_LIGHTING_COMMAND)
|
||||
ret_val = lighting_command_to_ascii(
|
||||
&value->type.Lighting_Command, str, str_len);
|
||||
#endif
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_COLOR_COMMAND:
|
||||
#if defined(CHANNEL_COLOR_COMMAND)
|
||||
ret_val = bacapp_snprintf_color_command(
|
||||
str, str_len, &value->type.Color_Command);
|
||||
#endif
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_XY_COLOR:
|
||||
#if defined(CHANNEL_XY_COLOR)
|
||||
ret_val = xy_color_to_ascii(&value->type.XY_Color, str, str_len);
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BACAPP_CHANNEL)
|
||||
/**
|
||||
* @brief For a given application value, copy to the channel value
|
||||
* @param cvalue - BACNET_CHANNEL_VALUE value
|
||||
* @param value - BACNET_APPLICATION_DATA_VALUE value
|
||||
* @return true if values are able to be copied
|
||||
*/
|
||||
bool bacapp_channel_value_copy(
|
||||
BACNET_CHANNEL_VALUE *cvalue, const BACNET_APPLICATION_DATA_VALUE *value)
|
||||
{
|
||||
bool status = false;
|
||||
|
||||
if (!value || !cvalue) {
|
||||
return false;
|
||||
}
|
||||
switch (value->tag) {
|
||||
#if defined(BACAPP_NULL)
|
||||
case BACNET_APPLICATION_TAG_NULL:
|
||||
cvalue->tag = value->tag;
|
||||
status = true;
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_BOOLEAN) && defined(CHANNEL_BOOLEAN)
|
||||
case BACNET_APPLICATION_TAG_BOOLEAN:
|
||||
cvalue->tag = value->tag;
|
||||
cvalue->type.Boolean = value->type.Boolean;
|
||||
status = true;
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_UNSIGNED) && defined(CHANNEL_UNSIGNED)
|
||||
case BACNET_APPLICATION_TAG_UNSIGNED_INT:
|
||||
cvalue->tag = value->tag;
|
||||
cvalue->type.Unsigned_Int = value->type.Unsigned_Int;
|
||||
status = true;
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_SIGNED) && defined(CHANNEL_SIGNED)
|
||||
case BACNET_APPLICATION_TAG_SIGNED_INT:
|
||||
cvalue->tag = value->tag;
|
||||
cvalue->type.Signed_Int = value->type.Signed_Int;
|
||||
status = true;
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_REAL) && defined(CHANNEL_REAL)
|
||||
case BACNET_APPLICATION_TAG_REAL:
|
||||
cvalue->tag = value->tag;
|
||||
cvalue->type.Real = value->type.Real;
|
||||
status = true;
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_DOUBLE) && defined(CHANNEL_DOUBLE)
|
||||
case BACNET_APPLICATION_TAG_DOUBLE:
|
||||
cvalue->tag = value->tag;
|
||||
cvalue->type.Double = value->type.Double;
|
||||
status = true;
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_OCTET_STRING) && defined(CHANNEL_OCTET_STRING)
|
||||
case BACNET_APPLICATION_TAG_OCTET_STRING:
|
||||
cvalue->tag = value->tag;
|
||||
octetstring_copy(
|
||||
&cvalue->type.Octet_String, &value->type.Octet_String);
|
||||
status = true;
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_CHARACTER_STRING) && defined(CHANNEL_CHARACTER_STRING)
|
||||
case BACNET_APPLICATION_TAG_CHARACTER_STRING:
|
||||
cvalue->tag = value->tag;
|
||||
characterstring_copy(
|
||||
&cvalue->type.Character_String, &value->type.Character_String);
|
||||
status = true;
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_BIT_STRING) && defined(CHANNEL_BIT_STRING)
|
||||
case BACNET_APPLICATION_TAG_BIT_STRING:
|
||||
cvalue->tag = value->tag;
|
||||
bitstring_copy(&cvalue->type.Bit_String, &value->type.Bit_String);
|
||||
status = true;
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_ENUMERATED) && defined(CHANNEL_ENUMERATED)
|
||||
case BACNET_APPLICATION_TAG_ENUMERATED:
|
||||
cvalue->tag = value->tag;
|
||||
cvalue->type.Enumerated = value->type.Enumerated;
|
||||
status = true;
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_DATE) && defined(CHANNEL_DATE)
|
||||
case BACNET_APPLICATION_TAG_DATE:
|
||||
cvalue->tag = value->tag;
|
||||
datetime_date_copy(&cvalue->type.Date, &value->type.Date);
|
||||
apdu_len = encode_application_date(apdu, &value->type.Date);
|
||||
status = true;
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_TIME) && defined(CHANNEL_TIME)
|
||||
case BACNET_APPLICATION_TAG_TIME:
|
||||
cvalue->tag = value->tag;
|
||||
datetime_time_copy(&cvalue->type.Time, &value->type.Time);
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_OBJECT_ID) && defined(CHANNEL_OBJECT_ID)
|
||||
case BACNET_APPLICATION_TAG_OBJECT_ID:
|
||||
cvalue->tag = value->tag;
|
||||
cvalue->type.Object_Id.type = value->type.Object_Id.type;
|
||||
cvalue->type.Object_Id.instance = value->type.Object_Id.instance;
|
||||
status = true;
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_LIGHTING_COMMAND) && defined(CHANNEL_LIGHTING_COMMAND)
|
||||
case BACNET_APPLICATION_TAG_LIGHTING_COMMAND:
|
||||
cvalue->tag = value->tag;
|
||||
lighting_command_copy(
|
||||
&cvalue->type.Lighting_Command, &value->type.Lighting_Command);
|
||||
status = true;
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_COLOR_COMMAND) && defined(CHANNEL_COLOR_COMMAND)
|
||||
case BACNET_APPLICATION_TAG_COLOR_COMMAND:
|
||||
cvalue->tag = value->tag;
|
||||
color_command_copy(
|
||||
&cvalue->type.Color_Command, &value->type.Color_Command);
|
||||
status = true;
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_COLOR_COMMAND) && defined(CHANNEL_XY_COLOR)
|
||||
case BACNET_APPLICATION_TAG_XY_COLOR:
|
||||
cvalue->tag = value->tag;
|
||||
xy_color_copy(&cvalue->type.XY_Color, &value->type.XY_Color);
|
||||
status = true;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BACAPP_WEEKLY_SCHEDULE)
|
||||
/**
|
||||
* @brief Print a weekly schedule value to a string for EPICS
|
||||
@@ -3235,16 +3502,8 @@ int bacapp_snprintf_value(
|
||||
#if defined(BACAPP_COLOR_COMMAND)
|
||||
case BACNET_APPLICATION_TAG_COLOR_COMMAND:
|
||||
/* BACnetColorCommand */
|
||||
slen = bacapp_snprintf(str, str_len, "(");
|
||||
ret_val += bacapp_snprintf_shift(slen, &str, &str_len);
|
||||
slen = bacapp_snprintf(
|
||||
str, str_len, "%s",
|
||||
bactext_color_operation_name(
|
||||
value->type.Color_Command.operation));
|
||||
ret_val += bacapp_snprintf_shift(slen, &str, &str_len);
|
||||
/* FIXME: add the Lighting Command optional values */
|
||||
slen = bacapp_snprintf(str, str_len, ")");
|
||||
ret_val += slen;
|
||||
ret_val = bacapp_snprintf_color_command(
|
||||
str, str_len, &value->type.Color_Command);
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_WEEKLY_SCHEDULE)
|
||||
@@ -3350,6 +3609,12 @@ int bacapp_snprintf_value(
|
||||
ret_val = bacapp_snprintf_access_rule(
|
||||
str, str_len, &value->type.Access_Rule);
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_CHANNEL_VALUE)
|
||||
case BACNET_APPLICATION_TAG_CHANNEL_VALUE:
|
||||
ret_val = bacapp_snprintf_channel_value(
|
||||
str, str_len, &value->type.Channel_Value);
|
||||
break;
|
||||
#endif
|
||||
case BACNET_APPLICATION_TAG_EMPTYLIST:
|
||||
ret_val = bacapp_snprintf(str, str_len, "{}");
|
||||
@@ -3722,6 +3987,117 @@ bacnet_shed_level_from_ascii(BACNET_SHED_LEVEL *value, const char *argv)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BACAPP_DEVICE_OBJECT_PROPERTY_REFERENCE)
|
||||
/**
|
||||
* @brief Parse a string into a BACnetDeviceObjectPropertyReference value
|
||||
* @param value [out] The BACnetObjectPropertyReference value
|
||||
* @param argv [in] The string to parse
|
||||
* @return true on success, else false
|
||||
*/
|
||||
static bool device_object_property_reference_from_ascii(
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *value, const char *argv)
|
||||
{
|
||||
bool status = false;
|
||||
unsigned long object_type, object_instance;
|
||||
unsigned long property_id;
|
||||
long array_index;
|
||||
unsigned long device_type, device_instance;
|
||||
int count;
|
||||
|
||||
if (!value || !argv) {
|
||||
return false;
|
||||
}
|
||||
/* analog-output:4194303,present-value,-1,device:4194303 */
|
||||
count = sscanf(
|
||||
argv, "%4lu:%7lu,%lu,%ld,%4lu:%7lu", &object_type, &object_instance,
|
||||
&property_id, &array_index, &device_type, &device_instance);
|
||||
if (count == 6) {
|
||||
value->objectIdentifier.type = object_type;
|
||||
value->objectIdentifier.instance = object_instance;
|
||||
value->propertyIdentifier = property_id;
|
||||
if (array_index < 0) {
|
||||
value->arrayIndex = BACNET_ARRAY_ALL;
|
||||
} else {
|
||||
value->arrayIndex = array_index;
|
||||
}
|
||||
value->arrayIndex = array_index;
|
||||
value->deviceIdentifier.type = device_type;
|
||||
value->deviceIdentifier.instance = device_instance;
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
#if defined(BACAPP_DEVICE_OBJECT_REFERENCE)
|
||||
/**
|
||||
* @brief Parse a string into a BACnetDeviceObjectReference value
|
||||
* @param value [out] The BACnetObjectPropertyReference value
|
||||
* @param argv [in] The string to parse
|
||||
* @return true on success, else false
|
||||
*/
|
||||
static bool device_object_reference_from_ascii(
|
||||
BACNET_DEVICE_OBJECT_REFERENCE *value, const char *argv)
|
||||
{
|
||||
bool status = false;
|
||||
unsigned long object_type, object_instance;
|
||||
unsigned long device_type, device_instance;
|
||||
int count;
|
||||
|
||||
if (!value || !argv) {
|
||||
return false;
|
||||
}
|
||||
/* analog-output:4194303,device:4194303 */
|
||||
count = sscanf(
|
||||
argv, "%4lu:%7lu,%4lu:%7lu", &object_type, &object_instance,
|
||||
&device_type, &device_instance);
|
||||
if (count == 4) {
|
||||
value->objectIdentifier.type = object_type;
|
||||
value->objectIdentifier.instance = object_instance;
|
||||
value->deviceIdentifier.type = device_type;
|
||||
value->deviceIdentifier.instance = device_instance;
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(BACAPP_OBJECT_PROPERTY_REFERENCE)
|
||||
/**
|
||||
* @brief Parse a string into a BACnetObjectPropertyReference value
|
||||
* @param value [out] The BACnetObjectPropertyReference value
|
||||
* @param argv [in] The string to parse
|
||||
* @return true on success, else false
|
||||
*/
|
||||
static bool object_property_reference_from_ascii(
|
||||
BACNET_OBJECT_PROPERTY_REFERENCE *value, const char *argv)
|
||||
{
|
||||
bool status = false;
|
||||
unsigned long object_type, object_instance;
|
||||
unsigned long property_id;
|
||||
long array_index;
|
||||
int count;
|
||||
|
||||
if (!value || !argv) {
|
||||
return false;
|
||||
}
|
||||
/* analog-output:4194303,present-value,-1,device:4194303 */
|
||||
count = sscanf(
|
||||
argv, "%4lu:%7lu,%lu,%ld", &object_type, &object_instance, &property_id,
|
||||
&array_index);
|
||||
if (count == 4) {
|
||||
value->object_identifier.type = object_type;
|
||||
value->object_identifier.instance = object_instance;
|
||||
value->property_identifier = property_id;
|
||||
value->property_array_index = array_index;
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* used to load the app data struct with the proper data
|
||||
converted from a command line argument.
|
||||
"argv" is not const to allow using strtok internally. It MAY be modified. */
|
||||
@@ -3930,6 +4306,24 @@ bool bacapp_parse_application_data(
|
||||
host_n_port_from_ascii(&value->type.Host_Address, argv);
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_DEVICE_OBJECT_PROPERTY_REFERENCE)
|
||||
case BACNET_APPLICATION_TAG_DEVICE_OBJECT_PROPERTY_REFERENCE:
|
||||
status = device_object_property_reference_from_ascii(
|
||||
&value->type.Device_Object_Property_Reference, argv);
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_DEVICE_OBJECT_REFERENCE)
|
||||
case BACNET_APPLICATION_TAG_DEVICE_OBJECT_REFERENCE:
|
||||
status = device_object_reference_from_ascii(
|
||||
&value->type.Device_Object_Reference, argv);
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_OBJECT_PROPERTY_REFERENCE)
|
||||
case BACNET_APPLICATION_TAG_OBJECT_PROPERTY_REFERENCE:
|
||||
status = object_property_reference_from_ascii(
|
||||
&value->type.Object_Property_Reference, argv);
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_DESTINATION)
|
||||
case BACNET_APPLICATION_TAG_DESTINATION:
|
||||
status = bacnet_destination_from_ascii(
|
||||
@@ -3966,9 +4360,14 @@ bool bacapp_parse_application_data(
|
||||
#endif
|
||||
#if defined(BACAPP_ACCESS_RULE)
|
||||
case BACNET_APPLICATION_TAG_ACCESS_RULE:
|
||||
/* BACnetAccessRule - not implemented */
|
||||
bacnet_access_rule_from_ascii(&value->type.Access_Rule, argv);
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_CHANNEL_VALUE)
|
||||
case BACNET_APPLICATION_TAG_CHANNEL_VALUE:
|
||||
bacnet_channel_value_from_ascii(
|
||||
&value->type.Channel_Value, argv);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
@@ -4527,6 +4926,13 @@ bool bacapp_same_value(
|
||||
status = bacnet_access_rule_same(
|
||||
&value->type.Access_Rule, &test_value->type.Access_Rule);
|
||||
break;
|
||||
#endif
|
||||
#if defined(BACAPP_CHANNEL_VALUE)
|
||||
case BACNET_APPLICATION_TAG_CHANNEL_VALUE:
|
||||
status = bacnet_channel_value_same(
|
||||
&value->type.Channel_Value,
|
||||
&test_value->type.Channel_Value);
|
||||
break;
|
||||
#endif
|
||||
case BACNET_APPLICATION_TAG_EMPTYLIST:
|
||||
status = true;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "bacnet/weeklyschedule.h"
|
||||
#include "bacnet/calendar_entry.h"
|
||||
#include "bacnet/special_event.h"
|
||||
#include "bacnet/channel_value.h"
|
||||
|
||||
#ifndef BACAPP_PRINT_ENABLED
|
||||
#if PRINT_ENABLED
|
||||
@@ -163,6 +164,9 @@ typedef struct BACnet_Application_Data_Value {
|
||||
#endif
|
||||
#if defined(BACAPP_ACCESS_RULE)
|
||||
BACNET_ACCESS_RULE Access_Rule;
|
||||
#endif
|
||||
#if defined(BACAPP_CHANNEL_VALUE)
|
||||
BACNET_CHANNEL_VALUE Channel_Value;
|
||||
#endif
|
||||
} type;
|
||||
/* simple linked list if needed */
|
||||
@@ -354,6 +358,10 @@ int bacapp_snprintf_value(
|
||||
size_t str_len,
|
||||
const BACNET_OBJECT_PROPERTY_VALUE *object_value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bacapp_channel_value_copy(
|
||||
BACNET_CHANNEL_VALUE *cvalue, const BACNET_APPLICATION_DATA_VALUE *value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bacapp_parse_application_data(
|
||||
BACNET_APPLICATION_TAG tag_number,
|
||||
|
||||
@@ -1610,7 +1610,9 @@ typedef enum {
|
||||
/* BACnetShedLevel */
|
||||
BACNET_APPLICATION_TAG_SHED_LEVEL,
|
||||
/* BACnetAccessRule */
|
||||
BACNET_APPLICATION_TAG_ACCESS_RULE
|
||||
BACNET_APPLICATION_TAG_ACCESS_RULE,
|
||||
/* BACnetChannelValue */
|
||||
BACNET_APPLICATION_TAG_CHANNEL_VALUE
|
||||
} BACNET_APPLICATION_TAG;
|
||||
|
||||
/* note: these are not the real values, */
|
||||
|
||||
+501
-790
File diff suppressed because it is too large
Load Diff
@@ -15,89 +15,9 @@
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
#include "bacnet/write_group.h"
|
||||
#include "bacnet/basic/object/lo.h"
|
||||
|
||||
/* BACNET_CHANNEL_VALUE decodes WriteProperty service requests
|
||||
Choose the datatypes that your application supports */
|
||||
#if !( \
|
||||
defined(CHANNEL_NUMERIC) || defined(CHANNEL_NULL) || \
|
||||
defined(CHANNEL_BOOLEAN) || defined(CHANNEL_UNSIGNED) || \
|
||||
defined(CHANNEL_SIGNED) || defined(CHANNEL_REAL) || \
|
||||
defined(CHANNEL_DOUBLE) || defined(CHANNEL_OCTET_STRING) || \
|
||||
defined(CHANNEL_CHARACTER_STRING) || defined(CHANNEL_BIT_STRING) || \
|
||||
defined(CHANNEL_ENUMERATED) || defined(CHANNEL_DATE) || \
|
||||
defined(CHANNEL_TIME) || defined(CHANNEL_OBJECT_ID) || \
|
||||
defined(CHANNEL_LIGHTING_COMMAND) || defined(CHANNEL_XY_COLOR) || \
|
||||
defined(CHANNEL_COLOR_COMMAND))
|
||||
#define CHANNEL_NUMERIC
|
||||
#endif
|
||||
|
||||
#if defined(CHANNEL_NUMERIC)
|
||||
#define CHANNEL_NULL
|
||||
#define CHANNEL_BOOLEAN
|
||||
#define CHANNEL_UNSIGNED
|
||||
#define CHANNEL_SIGNED
|
||||
#define CHANNEL_REAL
|
||||
#define CHANNEL_DOUBLE
|
||||
#define CHANNEL_ENUMERATED
|
||||
#define CHANNEL_LIGHTING_COMMAND
|
||||
#define CHANNEL_COLOR_COMMAND
|
||||
#define CHANNEL_XY_COLOR
|
||||
#endif
|
||||
|
||||
typedef struct BACnet_Channel_Value_t {
|
||||
uint8_t tag;
|
||||
union {
|
||||
/* NULL - not needed as it is encoded in the tag alone */
|
||||
#if defined(CHANNEL_BOOLEAN)
|
||||
bool Boolean;
|
||||
#endif
|
||||
#if defined(CHANNEL_UNSIGNED)
|
||||
uint32_t Unsigned_Int;
|
||||
#endif
|
||||
#if defined(CHANNEL_SIGNED)
|
||||
int32_t Signed_Int;
|
||||
#endif
|
||||
#if defined(CHANNEL_REAL)
|
||||
float Real;
|
||||
#endif
|
||||
#if defined(CHANNEL_DOUBLE)
|
||||
double Double;
|
||||
#endif
|
||||
#if defined(CHANNEL_OCTET_STRING)
|
||||
BACNET_OCTET_STRING Octet_String;
|
||||
#endif
|
||||
#if defined(CHANNEL_CHARACTER_STRING)
|
||||
BACNET_CHARACTER_STRING Character_String;
|
||||
#endif
|
||||
#if defined(CHANNEL_BIT_STRING)
|
||||
BACNET_BIT_STRING Bit_String;
|
||||
#endif
|
||||
#if defined(CHANNEL_ENUMERATED)
|
||||
uint32_t Enumerated;
|
||||
#endif
|
||||
#if defined(CHANNEL_DATE)
|
||||
BACNET_DATE Date;
|
||||
#endif
|
||||
#if defined(CHANNEL_TIME)
|
||||
BACNET_TIME Time;
|
||||
#endif
|
||||
#if defined(CHANNEL_OBJECT_ID)
|
||||
BACNET_OBJECT_ID Object_Id;
|
||||
#endif
|
||||
#if defined(CHANNEL_LIGHTING_COMMAND)
|
||||
BACNET_LIGHTING_COMMAND Lighting_Command;
|
||||
#endif
|
||||
#if defined(CHANNEL_COLOR_COMMAND)
|
||||
BACNET_COLOR_COMMAND Color_Command;
|
||||
#endif
|
||||
#if defined(CHANNEL_XY_COLOR)
|
||||
BACNET_XY_COLOR XY_Color;
|
||||
#endif
|
||||
} type;
|
||||
/* simple linked list if needed */
|
||||
struct BACnet_Channel_Value_t *next;
|
||||
} BACNET_CHANNEL_VALUE;
|
||||
#include "bacnet/channel_value.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -128,13 +48,19 @@ BACNET_STACK_EXPORT
|
||||
int Channel_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Channel_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data);
|
||||
BACNET_STACK_EXPORT
|
||||
void Channel_Write_Group(
|
||||
BACNET_WRITE_GROUP_DATA *data,
|
||||
uint32_t change_list_index,
|
||||
BACNET_GROUP_CHANNEL_VALUE *change_list);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_CHANNEL_VALUE *Channel_Present_Value(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Channel_Present_Value_Set(
|
||||
BACNET_WRITE_PROPERTY_DATA *wp_data,
|
||||
const BACNET_APPLICATION_DATA_VALUE *value);
|
||||
uint32_t object_instance,
|
||||
uint8_t priority,
|
||||
const BACNET_CHANNEL_VALUE *value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Channel_Out_Of_Service(uint32_t object_instance);
|
||||
@@ -169,22 +95,10 @@ Channel_Control_Groups_Element(uint32_t object_instance, int32_t array_index);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Channel_Control_Groups_Element_Set(
|
||||
uint32_t object_instance, int32_t array_index, uint16_t value);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Channel_Value_Copy(
|
||||
BACNET_CHANNEL_VALUE *cvalue, const BACNET_APPLICATION_DATA_VALUE *value);
|
||||
BACNET_STACK_EXPORT
|
||||
int Channel_Value_Encode(
|
||||
uint8_t *apdu, int apdu_max, const BACNET_CHANNEL_VALUE *value);
|
||||
BACNET_STACK_EXPORT
|
||||
int Channel_Coerce_Data_Encode(
|
||||
uint8_t *apdu,
|
||||
size_t apdu_size,
|
||||
const BACNET_APPLICATION_DATA_VALUE *value,
|
||||
BACNET_APPLICATION_TAG tag);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Channel_Write_Member_Value(
|
||||
BACNET_WRITE_PROPERTY_DATA *wp_data,
|
||||
const BACNET_APPLICATION_DATA_VALUE *value);
|
||||
BACNET_WRITE_PROPERTY_DATA *wp_data, const BACNET_CHANNEL_VALUE *value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void Channel_Write_Property_Internal_Callback_Set(write_property_function cb);
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief The WriteGroup-Request service handler
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date October 2024
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/abort.h"
|
||||
#include "bacnet/write_group.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/sys/debug.h"
|
||||
|
||||
/* WriteGroup-Request notification callbacks list */
|
||||
static BACNET_WRITE_GROUP_NOTIFICATION Write_Group_Notification_Head;
|
||||
|
||||
/**
|
||||
* @brief Print the contents of a WriteGroup-Request
|
||||
* @param data [in] The decoded WriteGroup-Request message
|
||||
*/
|
||||
void handler_write_group_print_data(BACNET_WRITE_GROUP_DATA *data)
|
||||
{
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
debug_printf(
|
||||
"WriteGroup:group-number=%lu\r\n", (unsigned long)data->group_number);
|
||||
debug_printf(
|
||||
"WriteGroup:write-priority=%lu\r\n",
|
||||
(unsigned long)data->write_priority);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief generic callback for WriteGroup-Request iterator
|
||||
* @param data [in] The contents of the WriteGroup-Request message
|
||||
* @param change_list_index [in] The index of the current value in the change
|
||||
* list
|
||||
* @param change_list [in] The current value in the change list
|
||||
*/
|
||||
static void handler_write_group_notification_callback(
|
||||
BACNET_WRITE_GROUP_DATA *data,
|
||||
uint32_t change_list_index,
|
||||
BACNET_GROUP_CHANNEL_VALUE *change_list)
|
||||
{
|
||||
BACNET_WRITE_GROUP_NOTIFICATION *head;
|
||||
|
||||
handler_write_group_print_data(data);
|
||||
head = &Write_Group_Notification_Head;
|
||||
do {
|
||||
if (head->callback) {
|
||||
head->callback(data, change_list_index, change_list);
|
||||
}
|
||||
head = head->next;
|
||||
} while (head);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add a WriteGroup notification callback
|
||||
* @param cb - WriteGroup notification callback to be added
|
||||
*/
|
||||
void handler_write_group_notification_add(BACNET_WRITE_GROUP_NOTIFICATION *cb)
|
||||
{
|
||||
BACNET_WRITE_GROUP_NOTIFICATION *head;
|
||||
|
||||
head = &Write_Group_Notification_Head;
|
||||
do {
|
||||
if (head->next == cb) {
|
||||
/* already here! */
|
||||
break;
|
||||
} else if (!head->next) {
|
||||
/* first available free node */
|
||||
head->next = cb;
|
||||
break;
|
||||
}
|
||||
head = head->next;
|
||||
} while (head);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A basic WriteGroup-Request service handler
|
||||
* @param service_request [in] The contents of the service request
|
||||
* @param service_len [in] The length of the service request
|
||||
* @param src [in] The source of the message
|
||||
*/
|
||||
void handler_write_group(
|
||||
uint8_t *service_request, uint16_t service_len, BACNET_ADDRESS *src)
|
||||
{
|
||||
BACNET_WRITE_GROUP_DATA data = { 0 };
|
||||
int len = 0;
|
||||
|
||||
(void)src;
|
||||
debug_printf("Received WriteGroup-Request!\n");
|
||||
|
||||
len = bacnet_write_group_service_request_decode_iterate(
|
||||
service_request, service_len, &data,
|
||||
handler_write_group_notification_callback);
|
||||
if (len <= 0) {
|
||||
debug_printf("WriteGroup-Request failed to decode!\n");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief The WriteGroup-Request service handler
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date October 2024
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef HANDLER_WRITE_GROUP_H
|
||||
#define HANDLER_WRITE_GROUP_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/write_group.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void handler_write_group(
|
||||
uint8_t *service_request, uint16_t service_len, BACNET_ADDRESS *src);
|
||||
BACNET_STACK_EXPORT
|
||||
void handler_write_group_print_data(BACNET_WRITE_GROUP_DATA *data);
|
||||
BACNET_STACK_EXPORT
|
||||
void handler_write_group_notification_add(BACNET_WRITE_GROUP_NOTIFICATION *cb);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Header file for a basic BACnet WriteGroup-Reqeust service send
|
||||
* @author Steve Karg
|
||||
* @date October 2024
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/dcc.h"
|
||||
#include "bacnet/write_group.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
|
||||
int Send_Write_Group(BACNET_ADDRESS *dest, const BACNET_WRITE_GROUP_DATA *data)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS my_address;
|
||||
size_t apdu_size;
|
||||
|
||||
if (!dcc_communication_enabled()) {
|
||||
return bytes_sent;
|
||||
}
|
||||
datalink_get_my_address(&my_address);
|
||||
/* encode the NPDU portion of the packet */
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len = npdu_encode_pdu(
|
||||
&Handler_Transmit_Buffer[0], dest, &my_address, &npdu_data);
|
||||
|
||||
/* encode the APDU portion of the packet */
|
||||
Handler_Transmit_Buffer[pdu_len++] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST;
|
||||
Handler_Transmit_Buffer[pdu_len++] = SERVICE_UNCONFIRMED_WRITE_GROUP;
|
||||
apdu_size = sizeof(Handler_Transmit_Buffer) - pdu_len;
|
||||
len = bacnet_write_group_service_request_encode(
|
||||
&Handler_Transmit_Buffer[pdu_len], apdu_size, data);
|
||||
pdu_len += len;
|
||||
bytes_sent = datalink_send_pdu(
|
||||
dest, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
|
||||
if (bytes_sent <= 0) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(
|
||||
stderr, "Failed to Send WriteGroup-Request (%s)!\n",
|
||||
strerror(errno));
|
||||
#endif
|
||||
}
|
||||
|
||||
return bytes_sent;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Header file for a basic BACnet WriteGroup-Reqeust service send
|
||||
* @author Steve Karg
|
||||
* @date October 2024
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_SERVICE_SEND_WRITE_GROUP_H
|
||||
#define BACNET_SERVICE_SEND_WRITE_GROUP_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/write_group.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int Send_Write_Group(BACNET_ADDRESS *dest, const BACNET_WRITE_GROUP_DATA *data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -52,6 +52,7 @@
|
||||
#include "bacnet/basic/service/h_whois.h"
|
||||
#include "bacnet/basic/service/h_wp.h"
|
||||
#include "bacnet/basic/service/h_wpm.h"
|
||||
#include "bacnet/basic/service/h_write_group.h"
|
||||
|
||||
/* application layer service send helpers */
|
||||
#include "bacnet/basic/service/s_abort.h"
|
||||
@@ -82,6 +83,7 @@
|
||||
#include "bacnet/basic/service/s_whois.h"
|
||||
#include "bacnet/basic/service/s_wp.h"
|
||||
#include "bacnet/basic/service/s_wpm.h"
|
||||
#include "bacnet/basic/service/s_write_group.h"
|
||||
|
||||
/** @defgroup MISCHNDLR Miscellaneous Service Handlers
|
||||
* Various utilities and functions to support the service handlers
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief BACnet single precision REAL encode and decode functions
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2012
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_CHANNEL_VALUE_H
|
||||
#define BACNET_CHANNEL_VALUE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/lighting.h"
|
||||
#include "bacnet/lighting.h"
|
||||
|
||||
/* BACNET_CHANNEL_VALUE decodes WriteProperty service requests
|
||||
Choose the datatypes that your application supports */
|
||||
#if !( \
|
||||
defined(CHANNEL_NUMERIC) || defined(CHANNEL_NULL) || \
|
||||
defined(CHANNEL_BOOLEAN) || defined(CHANNEL_UNSIGNED) || \
|
||||
defined(CHANNEL_SIGNED) || defined(CHANNEL_REAL) || \
|
||||
defined(CHANNEL_DOUBLE) || defined(CHANNEL_OCTET_STRING) || \
|
||||
defined(CHANNEL_CHARACTER_STRING) || defined(CHANNEL_BIT_STRING) || \
|
||||
defined(CHANNEL_ENUMERATED) || defined(CHANNEL_DATE) || \
|
||||
defined(CHANNEL_TIME) || defined(CHANNEL_OBJECT_ID) || \
|
||||
defined(CHANNEL_LIGHTING_COMMAND) || defined(CHANNEL_XY_COLOR) || \
|
||||
defined(CHANNEL_COLOR_COMMAND))
|
||||
#define CHANNEL_NUMERIC
|
||||
#endif
|
||||
|
||||
#if defined(CHANNEL_NUMERIC)
|
||||
#define CHANNEL_NULL
|
||||
#define CHANNEL_BOOLEAN
|
||||
#define CHANNEL_UNSIGNED
|
||||
#define CHANNEL_SIGNED
|
||||
#define CHANNEL_REAL
|
||||
#define CHANNEL_DOUBLE
|
||||
#define CHANNEL_ENUMERATED
|
||||
#define CHANNEL_LIGHTING_COMMAND
|
||||
#define CHANNEL_COLOR_COMMAND
|
||||
#define CHANNEL_XY_COLOR
|
||||
#endif
|
||||
|
||||
typedef struct BACnet_Channel_Value_t {
|
||||
uint8_t tag;
|
||||
union {
|
||||
/* NULL - not needed as it is encoded in the tag alone */
|
||||
#if defined(CHANNEL_BOOLEAN)
|
||||
bool Boolean;
|
||||
#endif
|
||||
#if defined(CHANNEL_UNSIGNED)
|
||||
BACNET_UNSIGNED_INTEGER Unsigned_Int;
|
||||
#endif
|
||||
#if defined(CHANNEL_SIGNED)
|
||||
int32_t Signed_Int;
|
||||
#endif
|
||||
#if defined(CHANNEL_REAL)
|
||||
float Real;
|
||||
#endif
|
||||
#if defined(CHANNEL_DOUBLE)
|
||||
double Double;
|
||||
#endif
|
||||
#if defined(CHANNEL_OCTET_STRING)
|
||||
BACNET_OCTET_STRING Octet_String;
|
||||
#endif
|
||||
#if defined(CHANNEL_CHARACTER_STRING)
|
||||
BACNET_CHARACTER_STRING Character_String;
|
||||
#endif
|
||||
#if defined(CHANNEL_BIT_STRING)
|
||||
BACNET_BIT_STRING Bit_String;
|
||||
#endif
|
||||
#if defined(CHANNEL_ENUMERATED)
|
||||
uint32_t Enumerated;
|
||||
#endif
|
||||
#if defined(CHANNEL_DATE)
|
||||
BACNET_DATE Date;
|
||||
#endif
|
||||
#if defined(CHANNEL_TIME)
|
||||
BACNET_TIME Time;
|
||||
#endif
|
||||
#if defined(CHANNEL_OBJECT_ID)
|
||||
BACNET_OBJECT_ID Object_Id;
|
||||
#endif
|
||||
#if defined(CHANNEL_LIGHTING_COMMAND)
|
||||
BACNET_LIGHTING_COMMAND Lighting_Command;
|
||||
#endif
|
||||
#if defined(CHANNEL_COLOR_COMMAND)
|
||||
BACNET_COLOR_COMMAND Color_Command;
|
||||
#endif
|
||||
#if defined(CHANNEL_XY_COLOR)
|
||||
BACNET_XY_COLOR XY_Color;
|
||||
#endif
|
||||
} type;
|
||||
/* simple linked list if needed */
|
||||
struct BACnet_Channel_Value_t *next;
|
||||
} BACNET_CHANNEL_VALUE;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int bacnet_channel_value_type_encode(
|
||||
uint8_t *apdu, const BACNET_CHANNEL_VALUE *value);
|
||||
BACNET_STACK_EXPORT
|
||||
int bacnet_channel_value_type_decode(
|
||||
const uint8_t *apdu,
|
||||
size_t apdu_size,
|
||||
uint8_t tag_data_type,
|
||||
uint32_t len_value_type,
|
||||
BACNET_CHANNEL_VALUE *value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int bacnet_channel_value_decode(
|
||||
const uint8_t *apdu, size_t apdu_len, BACNET_CHANNEL_VALUE *value);
|
||||
BACNET_STACK_EXPORT
|
||||
int bacnet_channel_value_encode(
|
||||
uint8_t *apdu, size_t apdu_size, const BACNET_CHANNEL_VALUE *value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bacnet_channel_value_from_ascii(
|
||||
BACNET_CHANNEL_VALUE *value, const char *argv);
|
||||
BACNET_STACK_EXPORT
|
||||
bool bacnet_channel_value_copy(
|
||||
BACNET_CHANNEL_VALUE *dest, const BACNET_CHANNEL_VALUE *src);
|
||||
BACNET_STACK_EXPORT
|
||||
bool bacnet_channel_value_same(
|
||||
const BACNET_CHANNEL_VALUE *value1, const BACNET_CHANNEL_VALUE *value2);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bacnet_channel_value_link_array(BACNET_CHANNEL_VALUE *array, size_t size);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int bacnet_channel_value_coerce_data_encode(
|
||||
uint8_t *apdu,
|
||||
size_t apdu_size,
|
||||
const BACNET_CHANNEL_VALUE *value,
|
||||
BACNET_APPLICATION_TAG tag);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
+4
-1
@@ -204,6 +204,7 @@
|
||||
defined(BACAPP_SCALE) || \
|
||||
defined(BACAPP_SHED_LEVEL) || \
|
||||
defined(BACAPP_ACCESS_RULE) || \
|
||||
defined(BACAPP_CHANNEL_VALUE) || \
|
||||
defined(BACAPP_TYPES_EXTRA))
|
||||
#define BACAPP_ALL
|
||||
#endif
|
||||
@@ -251,6 +252,7 @@
|
||||
#define BACAPP_SCALE
|
||||
#define BACAPP_SHED_LEVEL
|
||||
#define BACAPP_ACCESS_RULE
|
||||
#define BACAPP_CHANNEL_VALUE
|
||||
#endif
|
||||
|
||||
/* clang-format off */
|
||||
@@ -273,7 +275,8 @@
|
||||
defined(BACAPP_ACTION_COMMAND) || \
|
||||
defined(BACAPP_SCALE) || \
|
||||
defined(BACAPP_SHED_LEVEL) || \
|
||||
defined(BACAPP_ACCESS_RULE)
|
||||
defined(BACAPP_ACCESS_RULE) || \
|
||||
defined(BACAPP_CHANNEL_VALUE)
|
||||
#define BACAPP_COMPLEX_TYPES
|
||||
#endif
|
||||
/* clang-format on */
|
||||
|
||||
@@ -0,0 +1,700 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief WriteGroup service encode and decode
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date August 2023
|
||||
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/write_group.h"
|
||||
|
||||
/**
|
||||
* @brief Encode the WriteGroup service request
|
||||
*
|
||||
* WriteGroup-Request ::= SEQUENCE {
|
||||
* group-number [0] Unsigned32,
|
||||
* write-priority [1] Unsigned (1..16),
|
||||
* change-list [2] SEQUENCE OF BACnetGroupChannelValue,
|
||||
* inhibit-delay [3] BOOLEAN 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 bacnet_write_group_encode(
|
||||
uint8_t *apdu, const BACNET_WRITE_GROUP_DATA *data)
|
||||
{
|
||||
int len = 0; /* length of each encoding */
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
BACNET_UNSIGNED_INTEGER unsigned_value;
|
||||
|
||||
if (!data) {
|
||||
return 0;
|
||||
}
|
||||
/* group-number [0] Unsigned32 */
|
||||
len = encode_context_unsigned(apdu, 0, data->group_number);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
/* write-priority [1] Unsigned (1..16) */
|
||||
unsigned_value = data->write_priority;
|
||||
len = encode_context_unsigned(apdu, 1, unsigned_value);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
/* change-list [2] SEQUENCE OF BACnetGroupChannelValue */
|
||||
len = encode_opening_tag(apdu, 2);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
/* SEQUENCE OF BACnetGroupChannelValue */
|
||||
len = bacnet_group_channel_value_encode(apdu, &data->change_list);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
len = encode_closing_tag(apdu, 2);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
/* inhibit-delay [3] BOOLEAN OPTIONAL */
|
||||
if (data->inhibit_delay == WRITE_GROUP_INHIBIT_DELAY_TRUE) {
|
||||
len = encode_context_boolean(apdu, 3, true);
|
||||
apdu_len += len;
|
||||
} else if (data->inhibit_delay == WRITE_GROUP_INHIBIT_DELAY_FALSE) {
|
||||
len = encode_context_boolean(apdu, 3, false);
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Encode the WriteGroup service request
|
||||
* @param apdu Pointer to the buffer for encoding into
|
||||
* @param apdu_size number of bytes available in the buffer
|
||||
* @param data Pointer to the service data used for encoding values
|
||||
* @return number of bytes encoded, or zero if unable to encode or too large
|
||||
*/
|
||||
size_t bacnet_write_group_service_request_encode(
|
||||
uint8_t *apdu, size_t apdu_size, const BACNET_WRITE_GROUP_DATA *data)
|
||||
{
|
||||
size_t apdu_len = 0; /* total length of the apdu, return value */
|
||||
|
||||
apdu_len = bacnet_write_group_encode(NULL, data);
|
||||
if (apdu_len > apdu_size) {
|
||||
apdu_len = 0;
|
||||
} else {
|
||||
apdu_len = bacnet_write_group_encode(apdu, data);
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
static int write_group_service_group_number_decode(
|
||||
const uint8_t *apdu, size_t apdu_size, BACNET_WRITE_GROUP_DATA *data)
|
||||
{
|
||||
int len = 0;
|
||||
BACNET_UNSIGNED_INTEGER unsigned_value;
|
||||
|
||||
/* group-number [0] Unsigned32 */
|
||||
len = bacnet_unsigned_context_decode(apdu, apdu_size, 0, &unsigned_value);
|
||||
if (len > 0) {
|
||||
/* This parameter is an unsigned integer in the
|
||||
range 1 – 4294967295 that represents the control
|
||||
group to be affected by this request.
|
||||
Control group zero shall never be used
|
||||
and shall be reserved. WriteGroup service
|
||||
requests containing a zero value for
|
||||
'Group Number' shall be ignored.*/
|
||||
if ((unsigned_value > 4294967295) || (unsigned_value < 1)) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
if (data) {
|
||||
data->group_number = (uint32_t)unsigned_value;
|
||||
}
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int write_group_service_write_priority_decode(
|
||||
const uint8_t *apdu, size_t apdu_size, BACNET_WRITE_GROUP_DATA *data)
|
||||
{
|
||||
int len = 0;
|
||||
BACNET_UNSIGNED_INTEGER unsigned_value;
|
||||
|
||||
/* write-priority [1] Unsigned (1..16) */
|
||||
len = bacnet_unsigned_context_decode(apdu, apdu_size, 1, &unsigned_value);
|
||||
if (len > 0) {
|
||||
/* This parameter is an unsigned integer in the range 1..16
|
||||
that represents the priority for writing that shall apply
|
||||
to any channel value changes that result in writes to properties
|
||||
of BACnet objects. */
|
||||
if ((unsigned_value > BACNET_MAX_PRIORITY) ||
|
||||
(unsigned_value < BACNET_MIN_PRIORITY)) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
if (data) {
|
||||
data->write_priority = (uint8_t)unsigned_value;
|
||||
}
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int write_group_service_inhibit_delay_decode(
|
||||
const uint8_t *apdu, size_t apdu_size, BACNET_WRITE_GROUP_DATA *data)
|
||||
{
|
||||
int len = 0;
|
||||
bool boolean_value = false;
|
||||
|
||||
/* inhibit-delay [3] BOOLEAN OPTIONAL */
|
||||
len = bacnet_boolean_context_decode(apdu, apdu_size, 3, &boolean_value);
|
||||
if (len > 0) {
|
||||
if (data) {
|
||||
if (boolean_value) {
|
||||
data->inhibit_delay = WRITE_GROUP_INHIBIT_DELAY_TRUE;
|
||||
} else {
|
||||
data->inhibit_delay = WRITE_GROUP_INHIBIT_DELAY_FALSE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int write_group_service_change_list_decode(
|
||||
const uint8_t *apdu,
|
||||
size_t apdu_size,
|
||||
BACNET_WRITE_GROUP_DATA *data,
|
||||
BACnet_Write_Group_Callback callback)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
BACNET_GROUP_CHANNEL_VALUE change_value = { 0 };
|
||||
uint32_t change_list_index = 0;
|
||||
bool closed = false;
|
||||
|
||||
/* change-list [2] SEQUENCE OF BACnetGroupChannelValue */
|
||||
if (bacnet_is_opening_tag_number(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 2, &len)) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
while (apdu_len < apdu_size) {
|
||||
if (bacnet_is_closing_tag_number(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 2, &len)) {
|
||||
/* end of change-list [2] SEQUENCE OF BACnetGroupChannelValue */
|
||||
apdu_len += len;
|
||||
closed = true;
|
||||
break;
|
||||
}
|
||||
len = bacnet_group_channel_value_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, &change_value);
|
||||
if (len <= 0) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
apdu_len += len;
|
||||
if (callback) {
|
||||
callback(data, change_list_index, &change_value);
|
||||
}
|
||||
change_list_index++;
|
||||
}
|
||||
if (!closed) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief generic callback for WriteGroup-Request iterator
|
||||
* @param data [in] The contents of the WriteGroup-Request message
|
||||
* @param change_list_index [in] The index of the current value in the change
|
||||
* list
|
||||
* @param change_list [in] The current value in the change list
|
||||
*/
|
||||
void bacnet_write_group_service_change_list_value_set(
|
||||
BACNET_WRITE_GROUP_DATA *data,
|
||||
uint32_t change_list_index,
|
||||
BACNET_GROUP_CHANNEL_VALUE *change_list)
|
||||
{
|
||||
BACNET_GROUP_CHANNEL_VALUE *value;
|
||||
|
||||
value = bacnet_write_group_change_list_element(data, change_list_index);
|
||||
if (value) {
|
||||
(void)bacnet_group_channel_value_copy(value, change_list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decode the WriteGroup service request
|
||||
*
|
||||
* WriteGroup-Request ::= SEQUENCE {
|
||||
* group-number [0] Unsigned32,
|
||||
* write-priority [1] Unsigned (1..16),
|
||||
* change-list [2] SEQUENCE OF BACnetGroupChannelValue,
|
||||
* inhibit-delay [3] BOOLEAN OPTIONAL
|
||||
* }
|
||||
*
|
||||
* @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_ERROR on error.
|
||||
*/
|
||||
int bacnet_write_group_service_request_decode(
|
||||
const uint8_t *apdu, size_t apdu_size, BACNET_WRITE_GROUP_DATA *data)
|
||||
{
|
||||
return bacnet_write_group_service_request_decode_iterate(
|
||||
apdu, apdu_size, data,
|
||||
bacnet_write_group_service_change_list_value_set);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decode the WriteGroup-Request and call the WriteGroup handler
|
||||
* function to process each change-list element of the request
|
||||
*
|
||||
* WriteGroup-Request ::= SEQUENCE {
|
||||
* group-number [0] Unsigned32,
|
||||
* write-priority [1] Unsigned (1..16),
|
||||
* change-list [2] SEQUENCE OF BACnetGroupChannelValue ::= SEQUENCE {
|
||||
* channel [0] Unsigned16,
|
||||
* overriding-priority [1] Unsigned (1..16) OPTIONAL,
|
||||
* value BACnetChannelValue
|
||||
* }
|
||||
* inhibit-delay [3] BOOLEAN OPTIONAL
|
||||
* }
|
||||
*
|
||||
* @param apdu [in] Buffer of bytes received.
|
||||
* @param apdu_size [in] Count of valid bytes in the buffer.
|
||||
* @param callback [in] The function to call for each change-list element
|
||||
* @return Bytes decoded or BACNET_STATUS_ERROR on error.
|
||||
*/
|
||||
int bacnet_write_group_service_request_decode_iterate(
|
||||
const uint8_t *apdu,
|
||||
size_t apdu_size,
|
||||
BACNET_WRITE_GROUP_DATA *data,
|
||||
BACnet_Write_Group_Callback callback)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
const uint8_t *change_list_apdu;
|
||||
size_t change_list_apdu_size;
|
||||
|
||||
/* group-number [0] Unsigned32 */
|
||||
len = write_group_service_group_number_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, data);
|
||||
if (len <= 0) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
apdu_len += len;
|
||||
/* write-priority [1] Unsigned (1..16) */
|
||||
len = write_group_service_write_priority_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, data);
|
||||
if (len <= 0) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
apdu_len += len;
|
||||
/* change-list [2] SEQUENCE OF BACnetGroupChannelValue */
|
||||
change_list_apdu = &apdu[apdu_len];
|
||||
change_list_apdu_size = apdu_size - apdu_len;
|
||||
len = write_group_service_change_list_decode(
|
||||
change_list_apdu, change_list_apdu_size, data, NULL);
|
||||
if (len <= 0) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
apdu_len += len;
|
||||
if (apdu_len < apdu_size) {
|
||||
/* inhibit-delay [3] BOOLEAN OPTIONAL */
|
||||
len = write_group_service_inhibit_delay_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, data);
|
||||
if (len <= 0) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
apdu_len += len;
|
||||
} else {
|
||||
if (data) {
|
||||
data->inhibit_delay = WRITE_GROUP_INHIBIT_DELAY_NONE;
|
||||
}
|
||||
}
|
||||
len = write_group_service_change_list_decode(
|
||||
change_list_apdu, change_list_apdu_size, data, callback);
|
||||
if (len <= 0) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy WriteGroup data to another WriteGroup data
|
||||
* @param dest Pointer to the destination data
|
||||
* @param src Pointer to the source data
|
||||
* @return true if the values are copied
|
||||
*/
|
||||
bool bacnet_write_group_copy(
|
||||
BACNET_WRITE_GROUP_DATA *dest, const BACNET_WRITE_GROUP_DATA *src)
|
||||
{
|
||||
const BACNET_GROUP_CHANNEL_VALUE *value_src;
|
||||
BACNET_GROUP_CHANNEL_VALUE *value_dest;
|
||||
|
||||
if (!dest || !src) {
|
||||
return false;
|
||||
}
|
||||
dest->group_number = src->group_number;
|
||||
dest->write_priority = src->write_priority;
|
||||
dest->inhibit_delay = src->inhibit_delay;
|
||||
value_src = &src->change_list;
|
||||
value_dest = &dest->change_list;
|
||||
while (value_src && value_dest) {
|
||||
bacnet_group_channel_value_copy(value_dest, value_src);
|
||||
value_src = value_src->next;
|
||||
value_dest = value_dest->next;
|
||||
}
|
||||
if (value_src || value_dest) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare two WriteGroup service requests
|
||||
* @param data1 Pointer to the first data to compare
|
||||
* @param data2 Pointer to the second data to compare
|
||||
* @return true if the values are the same, else false
|
||||
*/
|
||||
bool bacnet_write_group_same(
|
||||
const BACNET_WRITE_GROUP_DATA *data1, const BACNET_WRITE_GROUP_DATA *data2)
|
||||
{
|
||||
if (!data1 || !data2) {
|
||||
return false;
|
||||
}
|
||||
if (data1->group_number != data2->group_number) {
|
||||
return false;
|
||||
}
|
||||
if (data1->write_priority != data2->write_priority) {
|
||||
return false;
|
||||
}
|
||||
if (data1->inhibit_delay != data2->inhibit_delay) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return bacnet_group_change_list_same(
|
||||
&data1->change_list, &data2->change_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare two BACnetGroupChannelValue value lists
|
||||
*/
|
||||
bool bacnet_group_change_list_same(
|
||||
const BACNET_GROUP_CHANNEL_VALUE *head1,
|
||||
const BACNET_GROUP_CHANNEL_VALUE *head2)
|
||||
{
|
||||
const BACNET_GROUP_CHANNEL_VALUE *data1;
|
||||
const BACNET_GROUP_CHANNEL_VALUE *data2;
|
||||
|
||||
data1 = head1;
|
||||
data2 = head2;
|
||||
while (data1 && data2) {
|
||||
if (!bacnet_group_channel_value_same(data1, data2)) {
|
||||
return false;
|
||||
}
|
||||
data1 = data1->next;
|
||||
data2 = data2->next;
|
||||
}
|
||||
if (data1 || data2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare two BACnetGroupChannelValue values
|
||||
* @param value1 Pointer to the first value to compare
|
||||
* @param value2 Pointer to the second value to compare
|
||||
* @return true if the values are the same, else false
|
||||
*/
|
||||
bool bacnet_group_channel_value_same(
|
||||
const BACNET_GROUP_CHANNEL_VALUE *value1,
|
||||
const BACNET_GROUP_CHANNEL_VALUE *value2)
|
||||
{
|
||||
if (!value1 || !value2) {
|
||||
return false;
|
||||
}
|
||||
if (value1->channel != value2->channel) {
|
||||
return false;
|
||||
}
|
||||
if (value1->overriding_priority != value2->overriding_priority) {
|
||||
return false;
|
||||
}
|
||||
if (!bacnet_channel_value_same(&value1->value, &value2->value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Encode a list of BACnetGroupChannelValue values
|
||||
*
|
||||
* BACnetGroupChannelValue ::= SEQUENCE {
|
||||
* channel [0] Unsigned16,
|
||||
* overriding-priority [1] Unsigned (1..16) OPTIONAL,
|
||||
* value BACnetChannelValue
|
||||
* }
|
||||
*
|
||||
* @param apdu Pointer to the buffer for encoded values
|
||||
* @param head Pointer to the first value in the list
|
||||
* @return Bytes encoded or zero on error.
|
||||
*/
|
||||
int bacnet_group_channel_value_encode(
|
||||
uint8_t *apdu, const BACNET_GROUP_CHANNEL_VALUE *head)
|
||||
{
|
||||
const BACNET_GROUP_CHANNEL_VALUE *value;
|
||||
int len = 0; /* length of each encoding */
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
|
||||
value = head;
|
||||
while (value) {
|
||||
/* channel [0] Unsigned16 */
|
||||
len = encode_context_unsigned(apdu, 0, value->channel);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
/* overriding-priority [1] Unsigned (1..16) OPTIONAL */
|
||||
if ((value->overriding_priority >= BACNET_MIN_PRIORITY) &&
|
||||
(value->overriding_priority <= BACNET_MAX_PRIORITY)) {
|
||||
len = encode_context_unsigned(apdu, 1, value->overriding_priority);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
}
|
||||
/* value BACnetChannelValue */
|
||||
len = bacnet_channel_value_type_encode(apdu, &value->value);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
/* is there another one to encode? */
|
||||
value = value->next;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decode a list of BACnetGroupChannelValue values
|
||||
*
|
||||
* BACnetGroupChannelValue ::= SEQUENCE {
|
||||
* channel [0] Unsigned16,
|
||||
* overriding-priority [1] Unsigned (1..16) OPTIONAL,
|
||||
* value BACnetChannelValue
|
||||
* }
|
||||
*
|
||||
* @param apdu Pointer to the buffer for encoded values
|
||||
* @param apdu_size Count of valid bytes in the buffer
|
||||
* @param value Pointer to the first value in the list
|
||||
* @return Bytes decoded or BACNET_STATUS_ERROR on error.
|
||||
*/
|
||||
int bacnet_group_channel_value_decode(
|
||||
const uint8_t *apdu, size_t apdu_size, BACNET_GROUP_CHANNEL_VALUE *value)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
BACNET_UNSIGNED_INTEGER unsigned_value;
|
||||
BACNET_CHANNEL_VALUE channel_value;
|
||||
|
||||
if (!apdu) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* channel [0] Unsigned16 */
|
||||
len = bacnet_unsigned_context_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 0, &unsigned_value);
|
||||
if (len > 0) {
|
||||
if (unsigned_value > UINT16_MAX) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
if (value) {
|
||||
value->channel = (uint16_t)unsigned_value;
|
||||
}
|
||||
apdu_len += len;
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* overriding-priority [1] Unsigned (1..16) OPTIONAL */
|
||||
len = bacnet_unsigned_context_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 1, &unsigned_value);
|
||||
if (len > 0) {
|
||||
if ((unsigned_value >= BACNET_MIN_PRIORITY) &&
|
||||
(unsigned_value <= BACNET_MAX_PRIORITY)) {
|
||||
if (value) {
|
||||
value->overriding_priority = (uint8_t)unsigned_value;
|
||||
}
|
||||
apdu_len += len;
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
} else {
|
||||
if (value) {
|
||||
value->overriding_priority = BACNET_NO_PRIORITY;
|
||||
}
|
||||
}
|
||||
/* value BACnetChannelValue */
|
||||
len = bacnet_channel_value_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, &channel_value);
|
||||
if (len > 0) {
|
||||
if (value) {
|
||||
memcpy(&value->value, &channel_value, sizeof(BACNET_CHANNEL_VALUE));
|
||||
}
|
||||
apdu_len += len;
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy BACnetGroupChannelValue data to another BACnetGroupChannelValue
|
||||
* data
|
||||
* @param dest Pointer to the destination data
|
||||
* @param src Pointer to the source data
|
||||
* @return true if values are able to be copied
|
||||
*/
|
||||
bool bacnet_group_channel_value_copy(
|
||||
BACNET_GROUP_CHANNEL_VALUE *dest, const BACNET_GROUP_CHANNEL_VALUE *src)
|
||||
{
|
||||
if (!dest || !src) {
|
||||
return false;
|
||||
}
|
||||
dest->channel = src->channel;
|
||||
dest->overriding_priority = src->overriding_priority;
|
||||
|
||||
return bacnet_channel_value_copy(&dest->value, &src->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Count the number of BACnetGroupChannelValue elements in change-list
|
||||
* @param data pointer to the WriteGroup data
|
||||
* @return number of elements in the list
|
||||
*/
|
||||
unsigned bacnet_write_group_change_list_count(BACNET_WRITE_GROUP_DATA *data)
|
||||
{
|
||||
BACNET_GROUP_CHANNEL_VALUE *value;
|
||||
unsigned count = 0;
|
||||
|
||||
if (!data) {
|
||||
return 0;
|
||||
}
|
||||
value = &data->change_list;
|
||||
while (value) {
|
||||
count++;
|
||||
value = value->next;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Append a BACnetGroupChannelValue element to change-list
|
||||
* @param data pointer to the WriteGroup data
|
||||
* @param element pointer to an element to add to the list
|
||||
* @param size number of elements in the array
|
||||
* @return true if the element is added to the list
|
||||
*/
|
||||
bool bacnet_write_group_change_list_append(
|
||||
BACNET_WRITE_GROUP_DATA *data, BACNET_GROUP_CHANNEL_VALUE *element)
|
||||
{
|
||||
BACNET_GROUP_CHANNEL_VALUE *value;
|
||||
|
||||
if (!data || !element) {
|
||||
return false;
|
||||
}
|
||||
value = &data->change_list;
|
||||
while (value) {
|
||||
if (!value->next) {
|
||||
value->next = element;
|
||||
return true;
|
||||
}
|
||||
value = value->next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add an array of BACnetGroupChannelValue to linked list
|
||||
* @param array pointer to element zero of the array
|
||||
* @param size number of elements in the array
|
||||
* @return true if the array is added to the linked list
|
||||
*/
|
||||
bool bacnet_write_group_change_list_array_link(
|
||||
BACNET_WRITE_GROUP_DATA *data,
|
||||
BACNET_GROUP_CHANNEL_VALUE *array,
|
||||
size_t size)
|
||||
{
|
||||
size_t i = 0;
|
||||
BACNET_GROUP_CHANNEL_VALUE *value;
|
||||
|
||||
if (!data || !array || (size == 0)) {
|
||||
return false;
|
||||
}
|
||||
value = &data->change_list;
|
||||
for (i = 0; i < size; i++) {
|
||||
value->next = &array[i];
|
||||
value = value->next;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get an array element of BACnetGroupChannelValue from WriteGroup data
|
||||
* @param data pointer to the WriteGroup data
|
||||
* @param index element number to retrieve 0..N
|
||||
*/
|
||||
BACNET_GROUP_CHANNEL_VALUE *bacnet_write_group_change_list_element(
|
||||
BACNET_WRITE_GROUP_DATA *data, unsigned index)
|
||||
{
|
||||
BACNET_GROUP_CHANNEL_VALUE *value;
|
||||
unsigned i = 0;
|
||||
|
||||
if (!data) {
|
||||
return NULL;
|
||||
}
|
||||
value = &data->change_list;
|
||||
while (i < index) {
|
||||
if (value) {
|
||||
value = value->next;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief API for BACnet WriteGroup service encoder and decoder
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date 2024
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_WRITE_GROUP_H
|
||||
#define BACNET_WRITE_GROUP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
/* BACnet Stack defines - first */
|
||||
#include "bacnet/bacdef.h"
|
||||
/* BACnet Stack API */
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/channel_value.h"
|
||||
|
||||
/**
|
||||
* BACnetGroupChannelValue ::= SEQUENCE {
|
||||
* channel [0] Unsigned16,
|
||||
* overriding-priority [1] Unsigned (1..16) OPTIONAL,
|
||||
* value BACnetChannelValue
|
||||
* }
|
||||
*/
|
||||
struct BACnet_Group_Channel_Value;
|
||||
typedef struct BACnet_Group_Channel_Value {
|
||||
uint16_t channel;
|
||||
uint8_t overriding_priority;
|
||||
BACNET_CHANNEL_VALUE value;
|
||||
struct BACnet_Group_Channel_Value *next;
|
||||
} BACNET_GROUP_CHANNEL_VALUE;
|
||||
|
||||
typedef enum {
|
||||
WRITE_GROUP_INHIBIT_DELAY_NONE = 0,
|
||||
WRITE_GROUP_INHIBIT_DELAY_TRUE = 1,
|
||||
WRITE_GROUP_INHIBIT_DELAY_FALSE = 2
|
||||
} WRITE_GROUP_INHIBIT_DELAY;
|
||||
|
||||
/**
|
||||
* WriteGroup-Request ::= SEQUENCE {
|
||||
* group-number [0] Unsigned32,
|
||||
* write-priority [1] Unsigned (1..16),
|
||||
* change-list [2] SEQUENCE OF BACnetGroupChannelValue,
|
||||
* inhibit-delay [3] BOOLEAN OPTIONAL
|
||||
* }
|
||||
*/
|
||||
struct BACnet_Write_Group_Data;
|
||||
typedef struct BACnet_Write_Group_Data {
|
||||
uint32_t group_number;
|
||||
uint8_t write_priority;
|
||||
/* simple linked list of values */
|
||||
BACNET_GROUP_CHANNEL_VALUE change_list;
|
||||
WRITE_GROUP_INHIBIT_DELAY inhibit_delay;
|
||||
struct BACnet_Write_Group_Data *next;
|
||||
} BACNET_WRITE_GROUP_DATA;
|
||||
|
||||
/**
|
||||
* @brief generic callback for WriteGroup-Request iterator
|
||||
* @param data [in] The contents of the WriteGroup-Request message
|
||||
* @param change_list_index [in] The index of the current value in the change
|
||||
* list
|
||||
* @param change_list [in] The current value in the change list
|
||||
*/
|
||||
typedef void (*BACnet_Write_Group_Callback)(
|
||||
BACNET_WRITE_GROUP_DATA *data,
|
||||
uint32_t change_list_index,
|
||||
BACNET_GROUP_CHANNEL_VALUE *change_list);
|
||||
struct BACnet_Write_Group_Notification;
|
||||
typedef struct BACnet_Write_Group_Notification {
|
||||
struct BACnet_Write_Group_Notification *next;
|
||||
BACnet_Write_Group_Callback callback;
|
||||
} BACNET_WRITE_GROUP_NOTIFICATION;
|
||||
|
||||
/**
|
||||
* @brief Process a WriteGroup-Request message, one value at a time
|
||||
* @param device_id [in] The device ID of the source of the message
|
||||
* @param data [in] The contents of the WriteGroup-Request message
|
||||
*/
|
||||
typedef void (*write_group_request_process)(
|
||||
uint32_t device_id, BACNET_WRITE_GROUP_DATA *data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int bacnet_write_group_encode(
|
||||
uint8_t *apdu, const BACNET_WRITE_GROUP_DATA *data);
|
||||
BACNET_STACK_EXPORT
|
||||
bool bacnet_write_group_same(
|
||||
const BACNET_WRITE_GROUP_DATA *data1, const BACNET_WRITE_GROUP_DATA *data2);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
size_t bacnet_write_group_service_request_encode(
|
||||
uint8_t *apdu, size_t apdu_size, const BACNET_WRITE_GROUP_DATA *data);
|
||||
BACNET_STACK_EXPORT
|
||||
int bacnet_write_group_service_request_decode(
|
||||
const uint8_t *apdu, size_t apdu_size, BACNET_WRITE_GROUP_DATA *data);
|
||||
BACNET_STACK_EXPORT
|
||||
int bacnet_write_group_service_request_decode_iterate(
|
||||
const uint8_t *apdu,
|
||||
size_t apdu_size,
|
||||
BACNET_WRITE_GROUP_DATA *data,
|
||||
BACnet_Write_Group_Callback callback);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bacnet_write_group_copy(
|
||||
BACNET_WRITE_GROUP_DATA *dest, const BACNET_WRITE_GROUP_DATA *src);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bacnet_group_change_list_same(
|
||||
const BACNET_GROUP_CHANNEL_VALUE *head1,
|
||||
const BACNET_GROUP_CHANNEL_VALUE *head2);
|
||||
BACNET_STACK_EXPORT
|
||||
unsigned bacnet_write_group_change_list_count(BACNET_WRITE_GROUP_DATA *data);
|
||||
BACNET_STACK_EXPORT
|
||||
bool bacnet_write_group_change_list_append(
|
||||
BACNET_WRITE_GROUP_DATA *data, BACNET_GROUP_CHANNEL_VALUE *element);
|
||||
BACNET_STACK_EXPORT
|
||||
bool bacnet_write_group_change_list_array_link(
|
||||
BACNET_WRITE_GROUP_DATA *data,
|
||||
BACNET_GROUP_CHANNEL_VALUE *array,
|
||||
size_t size);
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_GROUP_CHANNEL_VALUE *bacnet_write_group_change_list_element(
|
||||
BACNET_WRITE_GROUP_DATA *data, unsigned index);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bacnet_write_group_channel_value_process(
|
||||
uint8_t *apdu,
|
||||
size_t apdu_len,
|
||||
BACNET_WRITE_GROUP_DATA *data,
|
||||
write_group_request_process callback);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int bacnet_group_channel_value_encode(
|
||||
uint8_t *apdu, const BACNET_GROUP_CHANNEL_VALUE *value);
|
||||
BACNET_STACK_EXPORT
|
||||
int bacnet_group_channel_value_decode(
|
||||
const uint8_t *apdu, size_t apdu_size, BACNET_GROUP_CHANNEL_VALUE *value);
|
||||
BACNET_STACK_EXPORT
|
||||
bool bacnet_group_channel_value_same(
|
||||
const BACNET_GROUP_CHANNEL_VALUE *value1,
|
||||
const BACNET_GROUP_CHANNEL_VALUE *value2);
|
||||
BACNET_STACK_EXPORT
|
||||
bool bacnet_group_channel_value_copy(
|
||||
BACNET_GROUP_CHANNEL_VALUE *dest, const BACNET_GROUP_CHANNEL_VALUE *src);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
+3
-1
@@ -85,6 +85,7 @@ list(APPEND testdirs
|
||||
bacnet/bacreal
|
||||
bacnet/bacstr
|
||||
bacnet/bactimevalue
|
||||
bacnet/channel_value
|
||||
bacnet/cov
|
||||
bacnet/create_object
|
||||
bacnet/datetime
|
||||
@@ -111,11 +112,12 @@ list(APPEND testdirs
|
||||
bacnet/specialevent
|
||||
bacnet/timestamp
|
||||
bacnet/timesync
|
||||
bacnet/weeklyschedule
|
||||
bacnet/whohas
|
||||
bacnet/whois
|
||||
bacnet/wp
|
||||
bacnet/wpm
|
||||
bacnet/weeklyschedule
|
||||
bacnet/write_group
|
||||
)
|
||||
|
||||
# bacnet/basic/*
|
||||
|
||||
@@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -58,6 +58,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -57,6 +57,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -57,6 +57,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -58,6 +58,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -57,6 +57,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${TST_DIR}/bacnet/basic/object/test/property_test.c
|
||||
|
||||
@@ -58,6 +58,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||
${SRC_DIR}/bacnet/basic/sys/debug.c
|
||||
|
||||
@@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||
${SRC_DIR}/bacnet/basic/sys/keylist.c
|
||||
|
||||
@@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||
${SRC_DIR}/bacnet/basic/sys/debug.c
|
||||
|
||||
@@ -65,6 +65,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${TST_DIR}/bacnet/basic/object/test/apdu_mock.c
|
||||
|
||||
@@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||
${SRC_DIR}/bacnet/basic/sys/debug.c
|
||||
|
||||
@@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${TST_DIR}/bacnet/basic/object/test/property_test.c
|
||||
|
||||
@@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${TST_DIR}/bacnet/basic/object/test/device_mock.c
|
||||
|
||||
@@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||
${SRC_DIR}/bacnet/basic/sys/debug.c
|
||||
|
||||
@@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||
${SRC_DIR}/bacnet/basic/sys/debug.c
|
||||
|
||||
@@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/weeklyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
${SRC_DIR}/bacnet/bactimevalue.c
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
# Test and test library files
|
||||
|
||||
@@ -27,6 +27,7 @@ add_compile_definitions(
|
||||
|
||||
include_directories(
|
||||
${SRC_DIR}
|
||||
${TST_DIR}/bacnet/basic/object/test
|
||||
${TST_DIR}/ztest/include
|
||||
)
|
||||
|
||||
@@ -48,19 +49,24 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/bactimevalue.c
|
||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||
${SRC_DIR}/bacnet/basic/sys/debug.c
|
||||
${SRC_DIR}/bacnet/basic/sys/keylist.c
|
||||
${SRC_DIR}/bacnet/datetime.c
|
||||
${SRC_DIR}/bacnet/indtext.c
|
||||
${SRC_DIR}/bacnet/hostnport.c
|
||||
${SRC_DIR}/bacnet/lighting.c
|
||||
${SRC_DIR}/bacnet/property.c
|
||||
${SRC_DIR}/bacnet/proplist.c
|
||||
${SRC_DIR}/bacnet/timestamp.c
|
||||
${SRC_DIR}/bacnet/wp.c
|
||||
${SRC_DIR}/bacnet/weeklyschedule.c
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${TST_DIR}/bacnet/basic/object/test/property_test.c
|
||||
${TST_DIR}/bacnet/basic/object/test/device_mock.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
${ZTST_DIR}/ztest.c
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <zephyr/ztest.h>
|
||||
#include <bacnet/basic/object/channel.h>
|
||||
#include <bacnet/bactext.h>
|
||||
#include <property_test.h>
|
||||
|
||||
/**
|
||||
* @addtogroup bacnet_tests
|
||||
@@ -19,122 +20,91 @@
|
||||
/**
|
||||
* @brief Test
|
||||
*/
|
||||
static void test_Channel_ReadProperty(void)
|
||||
static void test_Channel_Property_Read_Write(void)
|
||||
{
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0;
|
||||
int test_len = 0;
|
||||
BACNET_READ_PROPERTY_DATA rpdata = { 0 };
|
||||
BACNET_APPLICATION_DATA_VALUE value = { 0 };
|
||||
BACNET_WRITE_PROPERTY_DATA wpdata = { 0 };
|
||||
const uint32_t instance = 123;
|
||||
const int *pRequired = NULL;
|
||||
const int *pOptional = NULL;
|
||||
const int *pProprietary = NULL;
|
||||
unsigned count = 0;
|
||||
bool status = false;
|
||||
unsigned index;
|
||||
unsigned index = 0;
|
||||
const char *sample_name = "Channel:0";
|
||||
const char *test_name = NULL;
|
||||
char *sample_name = "sample";
|
||||
uint32_t test_instance = 0;
|
||||
bool status = false;
|
||||
const int skip_fail_property_list[] = { -1 };
|
||||
BACNET_CHANNEL_VALUE channel_value = { 0 };
|
||||
BACNET_WRITE_PROPERTY_DATA wp_data = { 0 };
|
||||
BACNET_APPLICATION_DATA_VALUE value = { 0 };
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE member = { 0 };
|
||||
|
||||
Channel_Init();
|
||||
Channel_Create(instance);
|
||||
status = Channel_Valid_Instance(instance);
|
||||
zassert_true(status, NULL);
|
||||
status = Channel_Valid_Instance(instance - 1);
|
||||
zassert_false(status, NULL);
|
||||
index = Channel_Instance_To_Index(instance);
|
||||
zassert_equal(index, 0, NULL);
|
||||
|
||||
test_instance = Channel_Index_To_Instance(index);
|
||||
zassert_equal(instance, test_instance, NULL);
|
||||
count = Channel_Count();
|
||||
zassert_true(count > 0, NULL);
|
||||
rpdata.application_data = &apdu[0];
|
||||
rpdata.application_data_len = sizeof(apdu);
|
||||
rpdata.object_type = OBJECT_CHANNEL;
|
||||
rpdata.object_instance = Channel_Index_To_Instance(0);
|
||||
status = Channel_Valid_Instance(rpdata.object_instance);
|
||||
/* configure the instance property values and test API for lists */
|
||||
member.deviceIdentifier.type = OBJECT_DEVICE;
|
||||
member.deviceIdentifier.instance = 0;
|
||||
member.objectIdentifier.type = OBJECT_ANALOG_OUTPUT;
|
||||
member.objectIdentifier.instance = 1;
|
||||
member.propertyIdentifier = PROP_PRESENT_VALUE;
|
||||
member.arrayIndex = BACNET_ARRAY_ALL;
|
||||
index = Channel_Reference_List_Member_Element_Add(instance, &member);
|
||||
zassert_not_equal(index, 0, NULL);
|
||||
status =
|
||||
Channel_Reference_List_Member_Element_Set(instance, index, &member);
|
||||
zassert_true(status, NULL);
|
||||
Channel_Property_Lists(&pRequired, &pOptional, &pProprietary);
|
||||
while ((*pRequired) != -1) {
|
||||
rpdata.object_property = *pRequired;
|
||||
rpdata.array_index = BACNET_ARRAY_ALL;
|
||||
len = Channel_Read_Property(&rpdata);
|
||||
zassert_not_equal(
|
||||
len, BACNET_STATUS_ERROR,
|
||||
"property '%s': failed to ReadProperty!\n",
|
||||
bactext_property_name(rpdata.object_property));
|
||||
if (len > 0) {
|
||||
test_len = bacapp_decode_application_data(
|
||||
rpdata.application_data, (uint8_t)rpdata.application_data_len,
|
||||
&value);
|
||||
if ((rpdata.object_property == PROP_PRIORITY_ARRAY) ||
|
||||
(rpdata.object_property == PROP_CONTROL_GROUPS) ||
|
||||
(rpdata.object_property ==
|
||||
PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES)) {
|
||||
/* FIXME: known fail to decode */
|
||||
len = test_len;
|
||||
}
|
||||
zassert_equal(
|
||||
len, test_len, "property '%s': failed to decode!\n",
|
||||
bactext_property_name(rpdata.object_property));
|
||||
/* check WriteProperty properties */
|
||||
wpdata.object_type = rpdata.object_type;
|
||||
wpdata.object_instance = rpdata.object_instance;
|
||||
wpdata.object_property = rpdata.object_property;
|
||||
wpdata.array_index = rpdata.array_index;
|
||||
memcpy(&wpdata.application_data, rpdata.application_data, MAX_APDU);
|
||||
wpdata.application_data_len = len;
|
||||
wpdata.error_code = ERROR_CODE_SUCCESS;
|
||||
status = Channel_Write_Property(&wpdata);
|
||||
if (!status) {
|
||||
/* verify WriteProperty property is known */
|
||||
zassert_not_equal(
|
||||
wpdata.error_code, ERROR_CODE_UNKNOWN_PROPERTY,
|
||||
"property '%s': WriteProperty Unknown!\n",
|
||||
bactext_property_name(rpdata.object_property));
|
||||
}
|
||||
}
|
||||
pRequired++;
|
||||
}
|
||||
while ((*pOptional) != -1) {
|
||||
rpdata.object_property = *pOptional;
|
||||
rpdata.array_index = BACNET_ARRAY_ALL;
|
||||
len = Channel_Read_Property(&rpdata);
|
||||
zassert_not_equal(
|
||||
len, BACNET_STATUS_ERROR,
|
||||
"property '%s': failed to ReadProperty!\n",
|
||||
bactext_property_name(rpdata.object_property));
|
||||
if (len > 0) {
|
||||
test_len = bacapp_decode_application_data(
|
||||
rpdata.application_data, (uint8_t)rpdata.application_data_len,
|
||||
&value);
|
||||
zassert_equal(
|
||||
len, test_len, "property '%s': failed to decode!\n",
|
||||
bactext_property_name(rpdata.object_property));
|
||||
/* check WriteProperty properties */
|
||||
wpdata.object_type = rpdata.object_type;
|
||||
wpdata.object_instance = rpdata.object_instance;
|
||||
wpdata.object_property = rpdata.object_property;
|
||||
wpdata.array_index = rpdata.array_index;
|
||||
memcpy(&wpdata.application_data, rpdata.application_data, MAX_APDU);
|
||||
wpdata.application_data_len = len;
|
||||
wpdata.error_code = ERROR_CODE_SUCCESS;
|
||||
status = Channel_Write_Property(&wpdata);
|
||||
if (!status) {
|
||||
/* verify WriteProperty property is known */
|
||||
zassert_not_equal(
|
||||
wpdata.error_code, ERROR_CODE_UNKNOWN_PROPERTY,
|
||||
"property '%s': WriteProperty Unknown!\n",
|
||||
bactext_property_name(rpdata.object_property));
|
||||
}
|
||||
}
|
||||
pOptional++;
|
||||
}
|
||||
/* check for unsupported property - use ALL */
|
||||
rpdata.object_property = PROP_ALL;
|
||||
len = Channel_Read_Property(&rpdata);
|
||||
zassert_equal(len, BACNET_STATUS_ERROR, NULL);
|
||||
wpdata.object_property = PROP_ALL;
|
||||
status = Channel_Write_Property(&wpdata);
|
||||
zassert_false(status, NULL);
|
||||
status = Channel_Control_Groups_Element_Set(instance, 1, 1);
|
||||
zassert_true(status, NULL);
|
||||
member.deviceIdentifier.type = OBJECT_DEVICE;
|
||||
member.deviceIdentifier.instance = 0;
|
||||
member.objectIdentifier.type = OBJECT_BINARY_OUTPUT;
|
||||
member.objectIdentifier.instance = 1;
|
||||
member.propertyIdentifier = PROP_PRESENT_VALUE;
|
||||
member.arrayIndex = BACNET_ARRAY_ALL;
|
||||
index = Channel_Reference_List_Member_Element_Add(instance, &member);
|
||||
zassert_not_equal(index, 0, NULL);
|
||||
member.deviceIdentifier.type = OBJECT_DEVICE;
|
||||
member.deviceIdentifier.instance = 0;
|
||||
member.objectIdentifier.type = OBJECT_MULTI_STATE_OUTPUT;
|
||||
member.objectIdentifier.instance = 1;
|
||||
member.propertyIdentifier = PROP_PRESENT_VALUE;
|
||||
member.arrayIndex = BACNET_ARRAY_ALL;
|
||||
index = Channel_Reference_List_Member_Element_Add(instance, &member);
|
||||
zassert_not_equal(index, 0, NULL);
|
||||
member.deviceIdentifier.type = OBJECT_DEVICE;
|
||||
member.deviceIdentifier.instance = 0;
|
||||
member.objectIdentifier.type = OBJECT_LIGHTING_OUTPUT;
|
||||
member.objectIdentifier.instance = 1;
|
||||
member.propertyIdentifier = PROP_PRESENT_VALUE;
|
||||
member.arrayIndex = BACNET_ARRAY_ALL;
|
||||
index = Channel_Reference_List_Member_Element_Add(instance, &member);
|
||||
zassert_not_equal(index, 0, NULL);
|
||||
member.deviceIdentifier.type = OBJECT_DEVICE;
|
||||
member.deviceIdentifier.instance = 0;
|
||||
member.objectIdentifier.type = OBJECT_COLOR;
|
||||
member.objectIdentifier.instance = 1;
|
||||
member.propertyIdentifier = PROP_PRESENT_VALUE;
|
||||
member.arrayIndex = BACNET_ARRAY_ALL;
|
||||
index = Channel_Reference_List_Member_Element_Add(instance, &member);
|
||||
zassert_not_equal(index, 0, NULL);
|
||||
member.deviceIdentifier.type = OBJECT_DEVICE;
|
||||
member.deviceIdentifier.instance = 0;
|
||||
member.objectIdentifier.type = OBJECT_COLOR_TEMPERATURE;
|
||||
member.objectIdentifier.instance = 1;
|
||||
member.propertyIdentifier = PROP_PRESENT_VALUE;
|
||||
member.arrayIndex = BACNET_ARRAY_ALL;
|
||||
index = Channel_Reference_List_Member_Element_Add(instance, &member);
|
||||
zassert_not_equal(index, 0, NULL);
|
||||
/* perform a general test for RP/WP */
|
||||
bacnet_object_properties_read_write_test(
|
||||
OBJECT_CHANNEL, instance, Channel_Property_Lists, Channel_Read_Property,
|
||||
Channel_Write_Property, skip_fail_property_list);
|
||||
/* test the ASCII name get/set */
|
||||
status = Channel_Name_Set(instance, sample_name);
|
||||
zassert_true(status, NULL);
|
||||
@@ -144,9 +114,131 @@ static void test_Channel_ReadProperty(void)
|
||||
zassert_true(status, NULL);
|
||||
test_name = Channel_Name_ASCII(instance);
|
||||
zassert_equal(test_name, NULL, NULL);
|
||||
|
||||
/* test specific WriteProperty values - common configuration */
|
||||
wp_data.object_type = OBJECT_CHANNEL;
|
||||
wp_data.object_instance = instance;
|
||||
wp_data.array_index = BACNET_ARRAY_ALL;
|
||||
wp_data.priority = BACNET_MAX_PRIORITY;
|
||||
/* specific WriteProperty value */
|
||||
wp_data.object_property = PROP_PRESENT_VALUE;
|
||||
value.tag = BACNET_APPLICATION_TAG_CHANNEL_VALUE;
|
||||
value.type.Channel_Value.tag = BACNET_APPLICATION_TAG_REAL;
|
||||
value.type.Channel_Value.type.Real = 3.14159;
|
||||
wp_data.application_data_len =
|
||||
bacapp_encode_application_data(wp_data.application_data, &value);
|
||||
status = Channel_Write_Property(&wp_data);
|
||||
zassert_true(status, NULL);
|
||||
/* specific WriteProperty value */
|
||||
wp_data.object_property = PROP_OUT_OF_SERVICE;
|
||||
value.tag = BACNET_APPLICATION_TAG_BOOLEAN;
|
||||
value.type.Boolean = true;
|
||||
wp_data.application_data_len =
|
||||
bacapp_encode_application_data(wp_data.application_data, &value);
|
||||
status = Channel_Write_Property(&wp_data);
|
||||
zassert_true(status, NULL);
|
||||
/* specific WriteProperty value */
|
||||
wp_data.object_property = PROP_CHANNEL_NUMBER;
|
||||
value.tag = BACNET_APPLICATION_TAG_UNSIGNED_INT;
|
||||
value.type.Unsigned_Int = 123;
|
||||
wp_data.application_data_len =
|
||||
bacapp_encode_application_data(wp_data.application_data, &value);
|
||||
status = Channel_Write_Property(&wp_data);
|
||||
zassert_true(status, NULL);
|
||||
value.type.Unsigned_Int = UINT16_MAX + 1;
|
||||
wp_data.application_data_len =
|
||||
bacapp_encode_application_data(wp_data.application_data, &value);
|
||||
status = Channel_Write_Property(&wp_data);
|
||||
zassert_false(status, NULL);
|
||||
/* specific WriteProperty value */
|
||||
wp_data.object_property = PROP_CONTROL_GROUPS;
|
||||
wp_data.array_index = 1;
|
||||
value.tag = BACNET_APPLICATION_TAG_UNSIGNED_INT;
|
||||
/* min valid value */
|
||||
value.type.Unsigned_Int = 0;
|
||||
wp_data.application_data_len =
|
||||
bacapp_encode_application_data(wp_data.application_data, &value);
|
||||
status = Channel_Write_Property(&wp_data);
|
||||
zassert_true(status, NULL);
|
||||
/* max valid value */
|
||||
value.type.Unsigned_Int = UINT16_MAX;
|
||||
wp_data.application_data_len =
|
||||
bacapp_encode_application_data(wp_data.application_data, &value);
|
||||
status = Channel_Write_Property(&wp_data);
|
||||
zassert_true(status, NULL);
|
||||
/* array size - read-only */
|
||||
wp_data.array_index = 0;
|
||||
status = Channel_Write_Property(&wp_data);
|
||||
zassert_false(status, NULL);
|
||||
/* out-of-range value */
|
||||
wp_data.array_index = 1;
|
||||
value.type.Unsigned_Int = UINT16_MAX + 1;
|
||||
wp_data.application_data_len =
|
||||
bacapp_encode_application_data(wp_data.application_data, &value);
|
||||
status = Channel_Write_Property(&wp_data);
|
||||
zassert_false(status, NULL);
|
||||
/* invalid data type for Array element */
|
||||
wp_data.array_index = 1;
|
||||
value.type.Real = 3.14159f;
|
||||
value.tag = BACNET_APPLICATION_TAG_REAL;
|
||||
wp_data.application_data_len =
|
||||
bacapp_encode_application_data(wp_data.application_data, &value);
|
||||
status = Channel_Write_Property(&wp_data);
|
||||
zassert_false(status, NULL);
|
||||
/* invalid data type for Array size */
|
||||
wp_data.array_index = 0;
|
||||
status = Channel_Write_Property(&wp_data);
|
||||
zassert_false(status, NULL);
|
||||
/* invalid array-index - probably */
|
||||
wp_data.array_index = BACNET_ARRAY_ALL - 1;
|
||||
value.tag = BACNET_APPLICATION_TAG_UNSIGNED_INT;
|
||||
value.type.Unsigned_Int = 0;
|
||||
wp_data.application_data_len =
|
||||
bacapp_encode_application_data(wp_data.application_data, &value);
|
||||
status = Channel_Write_Property(&wp_data);
|
||||
zassert_false(status, NULL);
|
||||
/* specific WriteProperty value */
|
||||
wp_data.array_index = 1;
|
||||
wp_data.priority = BACNET_MAX_PRIORITY;
|
||||
wp_data.object_property = PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES;
|
||||
value.tag = BACNET_APPLICATION_TAG_DEVICE_OBJECT_PROPERTY_REFERENCE;
|
||||
value.type.Device_Object_Property_Reference.objectIdentifier.type =
|
||||
OBJECT_ANALOG_OUTPUT;
|
||||
value.type.Device_Object_Property_Reference.objectIdentifier.instance = 1;
|
||||
value.type.Device_Object_Property_Reference.propertyIdentifier =
|
||||
PROP_PRESENT_VALUE;
|
||||
value.type.Device_Object_Property_Reference.arrayIndex = BACNET_ARRAY_ALL;
|
||||
wp_data.application_data_len =
|
||||
bacapp_encode_application_data(wp_data.application_data, &value);
|
||||
status = Channel_Write_Property(&wp_data);
|
||||
zassert_true(status, NULL);
|
||||
wp_data.array_index = 0;
|
||||
status = Channel_Write_Property(&wp_data);
|
||||
zassert_false(status, NULL);
|
||||
wp_data.array_index = BACNET_ARRAY_ALL - 1;
|
||||
status = Channel_Write_Property(&wp_data);
|
||||
zassert_false(status, NULL);
|
||||
/* read-only property */
|
||||
wp_data.array_index = BACNET_ARRAY_ALL;
|
||||
wp_data.priority = BACNET_MAX_PRIORITY;
|
||||
wp_data.object_property = PROP_OBJECT_TYPE;
|
||||
value.tag = BACNET_APPLICATION_TAG_ENUMERATED;
|
||||
value.type.Enumerated = OBJECT_ANALOG_INPUT;
|
||||
wp_data.application_data_len =
|
||||
bacapp_encode_application_data(wp_data.application_data, &value);
|
||||
status = Channel_Write_Property(&wp_data);
|
||||
zassert_equal(wp_data.error_class, ERROR_CLASS_PROPERTY, NULL);
|
||||
zassert_equal(wp_data.error_code, ERROR_CODE_WRITE_ACCESS_DENIED, NULL);
|
||||
zassert_false(status, NULL);
|
||||
/* present-value API */
|
||||
channel_value.tag = BACNET_APPLICATION_TAG_REAL;
|
||||
channel_value.type.Real = 3.14159f;
|
||||
status = Channel_Present_Value_Set(instance, 1, &channel_value);
|
||||
zassert_true(status, NULL);
|
||||
/* cleanup */
|
||||
status = Channel_Delete(instance);
|
||||
zassert_true(status, NULL);
|
||||
Channel_Cleanup();
|
||||
}
|
||||
/**
|
||||
* @}
|
||||
@@ -154,7 +246,8 @@ static void test_Channel_ReadProperty(void)
|
||||
|
||||
void test_main(void)
|
||||
{
|
||||
ztest_test_suite(channel_tests, ztest_unit_test(test_Channel_ReadProperty));
|
||||
ztest_test_suite(
|
||||
channel_tests, ztest_unit_test(test_Channel_Property_Read_Write));
|
||||
|
||||
ztest_run_test_suite(channel_tests);
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${TST_DIR}/bacnet/basic/object/test/device_mock.c
|
||||
|
||||
@@ -62,6 +62,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${TST_DIR}/bacnet/basic/object/test/device_mock.c
|
||||
|
||||
@@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${TST_DIR}/bacnet/basic/object/test/property_test.c
|
||||
|
||||
@@ -63,6 +63,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${TST_DIR}/bacnet/basic/object/test/property_test.c
|
||||
|
||||
@@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
${SRC_DIR}/bacnet/memcopy.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
|
||||
@@ -106,6 +106,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
./stubs.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
|
||||
@@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||
${SRC_DIR}/bacnet/basic/sys/debug.c
|
||||
|
||||
@@ -69,6 +69,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${TST_DIR}/bacnet/basic/object/test/datetime_local.c
|
||||
|
||||
@@ -63,6 +63,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${TST_DIR}/bacnet/basic/object/test/device_mock.c
|
||||
|
||||
@@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${TST_DIR}/bacnet/basic/object/test/property_test.c
|
||||
|
||||
@@ -63,6 +63,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||
${SRC_DIR}/bacnet/basic/sys/debug.c
|
||||
${SRC_DIR}/bacnet/basic/sys/keylist.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${TST_DIR}/bacnet/basic/object/test/property_test.c
|
||||
|
||||
@@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||
${SRC_DIR}/bacnet/basic/sys/debug.c
|
||||
|
||||
@@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||
${SRC_DIR}/bacnet/basic/sys/debug.c
|
||||
|
||||
@@ -63,6 +63,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./stubs.c
|
||||
./src/main.c
|
||||
|
||||
@@ -55,6 +55,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/hostnport.c
|
||||
${SRC_DIR}/bacnet/lighting.c
|
||||
${SRC_DIR}/bacnet/proplist.c
|
||||
${SRC_DIR}/bacnet/property.c
|
||||
${SRC_DIR}/bacnet/timestamp.c
|
||||
${SRC_DIR}/bacnet/wp.c
|
||||
${SRC_DIR}/bacnet/weeklyschedule.c
|
||||
@@ -62,6 +63,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${TST_DIR}/bacnet/basic/object/test/property_test.c
|
||||
|
||||
@@ -58,6 +58,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -58,6 +58,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${TST_DIR}/bacnet/basic/object/test/property_test.c
|
||||
|
||||
@@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||
${SRC_DIR}/bacnet/basic/sys/debug.c
|
||||
${SRC_DIR}/bacnet/basic/sys/keylist.c
|
||||
|
||||
@@ -63,6 +63,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${TST_DIR}/bacnet/basic/object/test/datetime_local.c
|
||||
|
||||
@@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${TST_DIR}/bacnet/basic/object/test/device_mock.c
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
|
||||
|
||||
get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME)
|
||||
project(test_${basename}
|
||||
VERSION 1.0.0
|
||||
LANGUAGES C)
|
||||
|
||||
|
||||
string(REGEX REPLACE
|
||||
"/test/bacnet/[a-zA-Z_/-]*$"
|
||||
"/src"
|
||||
SRC_DIR
|
||||
${CMAKE_CURRENT_SOURCE_DIR})
|
||||
string(REGEX REPLACE
|
||||
"/test/bacnet/[a-zA-Z_/-]*$"
|
||||
"/test"
|
||||
TST_DIR
|
||||
${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(ZTST_DIR "${TST_DIR}/ztest/src")
|
||||
|
||||
add_compile_definitions(
|
||||
BIG_ENDIAN=0
|
||||
CONFIG_ZTEST=1
|
||||
BACAPP_ALL
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${SRC_DIR}
|
||||
${TST_DIR}/ztest/include
|
||||
)
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
# File(s) under test
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Support files and stubs (pathname alphabetical)
|
||||
${SRC_DIR}/bacnet/bacdcode.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/indtext.c
|
||||
${SRC_DIR}/bacnet/lighting.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
${ZTST_DIR}/ztest.c
|
||||
)
|
||||
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Unit test for BACnetChannelValue.
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date October 2024
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <zephyr/ztest.h>
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/channel_value.h"
|
||||
|
||||
/**
|
||||
* @addtogroup bacnet_tests
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @brief try decoding a real sample from Siemens, captured with wireshark */
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(BACnetChannelValue_Tests, test_BACnetChannelValue)
|
||||
#else
|
||||
static void test_BACnetChannelValue(void)
|
||||
#endif
|
||||
{
|
||||
uint8_t apdu[MAX_APDU];
|
||||
int apdu_len, test_len, null_len;
|
||||
bool status;
|
||||
BACNET_CHANNEL_VALUE *value;
|
||||
BACNET_CHANNEL_VALUE case_value[] = {
|
||||
{ .tag = BACNET_APPLICATION_TAG_NULL, .type.Real = 0.0f, .next = NULL },
|
||||
{ .tag = BACNET_APPLICATION_TAG_BOOLEAN,
|
||||
.type.Boolean = false,
|
||||
.next = NULL },
|
||||
{ .tag = BACNET_APPLICATION_TAG_UNSIGNED_INT,
|
||||
.type.Unsigned_Int = 0xDEADBEEF,
|
||||
.next = NULL },
|
||||
{ .tag = BACNET_APPLICATION_TAG_SIGNED_INT,
|
||||
.type.Signed_Int = 0x00C0FFEE,
|
||||
.next = NULL },
|
||||
{ .tag = BACNET_APPLICATION_TAG_REAL,
|
||||
.type.Real = 3.141592654f,
|
||||
.next = NULL },
|
||||
{ .tag = BACNET_APPLICATION_TAG_DOUBLE,
|
||||
.type.Double = 2.32323232323,
|
||||
.next = NULL },
|
||||
{ .tag = BACNET_APPLICATION_TAG_ENUMERATED,
|
||||
.type.Enumerated = 0x0BADF00D,
|
||||
.next = NULL },
|
||||
{ .tag = BACNET_APPLICATION_TAG_LIGHTING_COMMAND,
|
||||
.type.Lighting_Command.operation = BACNET_LIGHTS_NONE,
|
||||
.next = NULL },
|
||||
{ .tag = BACNET_APPLICATION_TAG_COLOR_COMMAND,
|
||||
.type.Color_Command.operation = BACNET_COLOR_OPERATION_NONE,
|
||||
.next = NULL },
|
||||
{ .tag = BACNET_APPLICATION_TAG_XY_COLOR,
|
||||
.type.XY_Color.x_coordinate = 0.0f,
|
||||
.type.XY_Color.y_coordinate = 0.0f,
|
||||
.next = NULL },
|
||||
};
|
||||
struct ascii_channel_value {
|
||||
const char *string;
|
||||
BACNET_APPLICATION_TAG tag;
|
||||
} ascii_values[] = {
|
||||
{ "NULL", BACNET_APPLICATION_TAG_NULL },
|
||||
{ "FALSE", BACNET_APPLICATION_TAG_BOOLEAN },
|
||||
{ "1234567890", BACNET_APPLICATION_TAG_UNSIGNED_INT },
|
||||
{ "-1234567890", BACNET_APPLICATION_TAG_SIGNED_INT },
|
||||
{ "3.141592654", BACNET_APPLICATION_TAG_REAL },
|
||||
{ "-3.141592654", BACNET_APPLICATION_TAG_REAL },
|
||||
{ "F1.21", BACNET_APPLICATION_TAG_REAL },
|
||||
{ "f1.21", BACNET_APPLICATION_TAG_REAL },
|
||||
{ "D1.21", BACNET_APPLICATION_TAG_DOUBLE },
|
||||
{ "d1.21", BACNET_APPLICATION_TAG_DOUBLE },
|
||||
{ "L0", BACNET_APPLICATION_TAG_LIGHTING_COMMAND },
|
||||
{ "l0", BACNET_APPLICATION_TAG_LIGHTING_COMMAND },
|
||||
{ "C0", BACNET_APPLICATION_TAG_COLOR_COMMAND },
|
||||
{ "c0", BACNET_APPLICATION_TAG_COLOR_COMMAND },
|
||||
{ "X0.0,0.0", BACNET_APPLICATION_TAG_XY_COLOR },
|
||||
{ "x0.0,0.0", BACNET_APPLICATION_TAG_XY_COLOR },
|
||||
};
|
||||
size_t i;
|
||||
BACNET_CHANNEL_VALUE test_value = { 0 };
|
||||
|
||||
bacnet_channel_value_link_array(case_value, ARRAY_SIZE(case_value));
|
||||
value = &case_value[0];
|
||||
while (value) {
|
||||
null_len = bacnet_channel_value_encode(NULL, sizeof(apdu), value);
|
||||
if (value->tag != BACNET_APPLICATION_TAG_NULL) {
|
||||
zassert_not_equal(null_len, 0, NULL);
|
||||
}
|
||||
apdu_len = bacnet_channel_value_encode(apdu, sizeof(apdu), value);
|
||||
zassert_equal(
|
||||
apdu_len, null_len, "value->tag: %s len=%d null_len=%d",
|
||||
bactext_application_tag_name(value->tag), apdu_len, null_len);
|
||||
test_len = bacnet_channel_value_decode(apdu, apdu_len, &test_value);
|
||||
zassert_not_equal(
|
||||
test_len, BACNET_STATUS_ERROR, "value->tag: %s test_len=%d",
|
||||
bactext_application_tag_name(value->tag), test_len);
|
||||
zassert_equal(test_len, apdu_len, NULL);
|
||||
zassert_equal(
|
||||
value->tag, test_value.tag, "value->tag: %s test_tag=%s",
|
||||
bactext_application_tag_name(value->tag),
|
||||
bactext_application_tag_name(test_value.tag));
|
||||
status = bacnet_channel_value_same(value, &test_value);
|
||||
zassert_true(
|
||||
status, "decode: different: %s",
|
||||
bactext_application_tag_name(value->tag));
|
||||
status = bacnet_channel_value_copy(&test_value, value);
|
||||
zassert_true(
|
||||
status, "copy: failed: %s",
|
||||
bactext_application_tag_name(value->tag));
|
||||
status = bacnet_channel_value_same(value, &test_value);
|
||||
zassert_true(
|
||||
status, "copy: different: %s",
|
||||
bactext_application_tag_name(value->tag));
|
||||
value = value->next;
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(ascii_values); i++) {
|
||||
status = bacnet_channel_value_from_ascii(
|
||||
&test_value, ascii_values[i].string);
|
||||
zassert_true(status, "from_ascii: failed: %s", ascii_values[i].string);
|
||||
zassert_equal(
|
||||
test_value.tag, ascii_values[i].tag, "from_ascii: %s",
|
||||
ascii_values[i].string);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST_SUITE(BACnetChannelValue_Tests, NULL, NULL, NULL, NULL, NULL);
|
||||
#else
|
||||
void test_main(void)
|
||||
{
|
||||
ztest_test_suite(
|
||||
BACnetChannelValue_Tests, ztest_unit_test(test_BACnetChannelValue));
|
||||
|
||||
ztest_run_test_suite(BACnetChannelValue_Tests);
|
||||
}
|
||||
#endif
|
||||
@@ -58,6 +58,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -61,6 +61,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/hostnport.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -58,6 +58,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -58,6 +58,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -58,6 +58,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -60,6 +60,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -57,7 +57,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/weeklyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -57,6 +57,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/timestamp.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -58,6 +58,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -59,6 +59,7 @@ add_executable(${PROJECT_NAME}
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# 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/write_group.c
|
||||
# Support files and stubs (pathname alphabetical)
|
||||
${SRC_DIR}/bacnet/access_rule.c
|
||||
${SRC_DIR}/bacnet/bacaction.c
|
||||
${SRC_DIR}/bacnet/bacaddr.c
|
||||
${SRC_DIR}/bacnet/bacapp.c
|
||||
${SRC_DIR}/bacnet/bacdcode.c
|
||||
${SRC_DIR}/bacnet/bacdest.c
|
||||
${SRC_DIR}/bacnet/bacdevobjpropref.c
|
||||
${SRC_DIR}/bacnet/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/hostnport.c
|
||||
${SRC_DIR}/bacnet/lighting.c
|
||||
${SRC_DIR}/bacnet/timestamp.c
|
||||
${SRC_DIR}/bacnet/indtext.c
|
||||
${SRC_DIR}/bacnet/weeklyschedule.c
|
||||
${SRC_DIR}/bacnet/bactimevalue.c
|
||||
${SRC_DIR}/bacnet/dailyschedule.c
|
||||
${SRC_DIR}/bacnet/calendar_entry.c
|
||||
${SRC_DIR}/bacnet/special_event.c
|
||||
${SRC_DIR}/bacnet/channel_value.c
|
||||
# Test and test library files
|
||||
./src/main.c
|
||||
${ZTST_DIR}/ztest_mock.c
|
||||
${ZTST_DIR}/ztest.c
|
||||
)
|
||||
@@ -0,0 +1,292 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Unit test for WriteGroup service
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date October 2024
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <zephyr/ztest.h>
|
||||
#include <bacnet/write_group.h>
|
||||
|
||||
#define WRITE_GROUP_CHANNEL_LIST_MAX 8
|
||||
|
||||
/**
|
||||
* @addtogroup bacnet_tests
|
||||
* @{
|
||||
*/
|
||||
|
||||
static void test_WriteGroup_Postive(BACNET_WRITE_GROUP_DATA *data)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
bool status;
|
||||
uint8_t apdu[480] = { 0 };
|
||||
BACNET_WRITE_GROUP_DATA test_data = { 0 };
|
||||
|
||||
/* positive test */
|
||||
len =
|
||||
bacnet_write_group_service_request_encode(&apdu[0], sizeof(apdu), data);
|
||||
zassert_true(len > 0, NULL);
|
||||
apdu_len = len;
|
||||
len = bacnet_write_group_service_request_decode(
|
||||
&apdu[0], apdu_len, &test_data);
|
||||
zassert_not_equal(len, BACNET_STATUS_ERROR, NULL);
|
||||
zassert_equal(len, apdu_len, NULL);
|
||||
status = bacnet_write_group_same(data, &test_data);
|
||||
zassert_true(status, NULL);
|
||||
/* negative decoding test */
|
||||
while (--apdu_len) {
|
||||
len = bacnet_write_group_service_request_decode(
|
||||
apdu, apdu_len, &test_data);
|
||||
/* inhibit delay is an optional element at the end of the
|
||||
construction and the APDU is valid without it */
|
||||
if (data->inhibit_delay == WRITE_GROUP_INHIBIT_DELAY_NONE) {
|
||||
zassert_true(len <= 0, NULL);
|
||||
}
|
||||
}
|
||||
/* negative encoding test */
|
||||
len =
|
||||
bacnet_write_group_service_request_encode(&apdu[0], sizeof(apdu), data);
|
||||
zassert_true(len > 0, NULL);
|
||||
apdu_len = len;
|
||||
while (--apdu_len) {
|
||||
len =
|
||||
bacnet_write_group_service_request_encode(&apdu[0], apdu_len, data);
|
||||
zassert_equal(len, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @addtogroup bacnet_tests
|
||||
* @{
|
||||
*/
|
||||
|
||||
static void test_WriteGroup_Negative(BACNET_WRITE_GROUP_DATA *data)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
uint8_t apdu[480] = { 0 };
|
||||
BACNET_WRITE_GROUP_DATA test_data = { 0 };
|
||||
|
||||
len =
|
||||
bacnet_write_group_service_request_encode(&apdu[0], sizeof(apdu), data);
|
||||
zassert_true(len > 0, NULL);
|
||||
apdu_len = len;
|
||||
len = bacnet_write_group_service_request_decode(
|
||||
&apdu[0], apdu_len, &test_data);
|
||||
zassert_true(len < 0, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief callback for WriteGroup-Request iterator
|
||||
* @param data [in] The contents of the WriteGroup-Request message
|
||||
* @param change_list_index [in] The index of the current value in the change
|
||||
* list
|
||||
* @param change_list [in] The current value in the change list
|
||||
*/
|
||||
static void test_WriteGroup_Iterate_Value(
|
||||
BACNET_WRITE_GROUP_DATA *data,
|
||||
uint32_t change_list_index,
|
||||
BACNET_GROUP_CHANNEL_VALUE *change_list)
|
||||
{
|
||||
BACNET_GROUP_CHANNEL_VALUE *value = NULL;
|
||||
|
||||
value = bacnet_write_group_change_list_element(data, change_list_index);
|
||||
zassert_not_null(value, NULL);
|
||||
zassert_true(bacnet_group_channel_value_same(value, change_list), NULL);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(write_group_tests, test_WriteGroup_Iterate)
|
||||
#else
|
||||
static void test_WriteGroup_Iterate(void)
|
||||
#endif
|
||||
{
|
||||
int len, test_len;
|
||||
uint8_t apdu[480] = { 0 };
|
||||
BACNET_WRITE_GROUP_DATA data = {
|
||||
/* initial test values for sunny day use-case */
|
||||
.group_number = 1,
|
||||
.write_priority = BACNET_MIN_PRIORITY,
|
||||
.change_list = { 0 },
|
||||
.inhibit_delay = WRITE_GROUP_INHIBIT_DELAY_NONE,
|
||||
.next = NULL
|
||||
};
|
||||
BACNET_GROUP_CHANNEL_VALUE value[WRITE_GROUP_CHANNEL_LIST_MAX] = { 0 };
|
||||
BACNET_GROUP_CHANNEL_VALUE value_append = { 0 };
|
||||
BACNET_GROUP_CHANNEL_VALUE *value_element = NULL;
|
||||
unsigned count = 0;
|
||||
unsigned index = 0;
|
||||
bool status;
|
||||
|
||||
/* link the array of change-list to our data */
|
||||
status = bacnet_write_group_change_list_array_link(
|
||||
&data, value, ARRAY_SIZE(value));
|
||||
zassert_true(status, NULL);
|
||||
/* note: array + head */
|
||||
for (index = 0; index < ARRAY_SIZE(value) + 1; index++) {
|
||||
value_element = bacnet_write_group_change_list_element(&data, index);
|
||||
zassert_not_null(value_element, NULL);
|
||||
/* set some predictable values to be tested */
|
||||
value_element->channel = index;
|
||||
value_element->overriding_priority = index;
|
||||
value_element->value.tag = BACNET_APPLICATION_TAG_UNSIGNED_INT;
|
||||
value_element->value.type.Unsigned_Int = index;
|
||||
}
|
||||
len = bacnet_write_group_service_request_encode(
|
||||
&apdu[0], sizeof(apdu), &data);
|
||||
zassert_true(len > 0, NULL);
|
||||
test_len = bacnet_write_group_service_request_decode_iterate(
|
||||
&apdu[0], len, &data, test_WriteGroup_Iterate_Value);
|
||||
count = bacnet_write_group_change_list_count(&data);
|
||||
zassert_equal(count, WRITE_GROUP_CHANNEL_LIST_MAX + 1, NULL);
|
||||
/* validate append API */
|
||||
value_append.channel = count;
|
||||
value_append.overriding_priority = count;
|
||||
value_append.value.tag = BACNET_APPLICATION_TAG_UNSIGNED_INT;
|
||||
value_append.value.type.Unsigned_Int = count;
|
||||
status = bacnet_write_group_change_list_append(&data, &value_append);
|
||||
zassert_true(status, NULL);
|
||||
count = bacnet_write_group_change_list_count(&data);
|
||||
zassert_equal(count, WRITE_GROUP_CHANNEL_LIST_MAX + 2, NULL);
|
||||
len = bacnet_write_group_service_request_encode(
|
||||
&apdu[0], sizeof(apdu), &data);
|
||||
zassert_true(len > 0, NULL);
|
||||
test_len = bacnet_write_group_service_request_decode_iterate(
|
||||
&apdu[0], len, &data, test_WriteGroup_Iterate_Value);
|
||||
/* some negative testing for coverage */
|
||||
count = bacnet_write_group_change_list_count(NULL);
|
||||
zassert_equal(count, 0, NULL);
|
||||
value_element = bacnet_write_group_change_list_element(NULL, 0);
|
||||
zassert_true(value_element == NULL, NULL);
|
||||
status = bacnet_write_group_change_list_array_link(NULL, NULL, 0);
|
||||
zassert_false(status, NULL);
|
||||
status = bacnet_write_group_change_list_append(NULL, NULL);
|
||||
zassert_false(status, NULL);
|
||||
status = bacnet_write_group_change_list_append(&data, NULL);
|
||||
zassert_false(status, NULL);
|
||||
status = bacnet_write_group_change_list_append(NULL, &value_append);
|
||||
zassert_false(status, NULL);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(write_group_tests, test_WriteGroup)
|
||||
#else
|
||||
static void test_WriteGroup(void)
|
||||
#endif
|
||||
{
|
||||
int len = 0;
|
||||
uint8_t apdu[480] = { 0 };
|
||||
BACNET_WRITE_GROUP_DATA data = {
|
||||
/* initial test values for sunny day use-case */
|
||||
.group_number = 1,
|
||||
.write_priority = BACNET_MIN_PRIORITY,
|
||||
.change_list = { 0 },
|
||||
.inhibit_delay = WRITE_GROUP_INHIBIT_DELAY_NONE,
|
||||
.next = NULL
|
||||
};
|
||||
|
||||
/* negative test */
|
||||
len =
|
||||
bacnet_write_group_service_request_encode(&apdu[0], sizeof(apdu), NULL);
|
||||
zassert_false(len > 0, NULL);
|
||||
/* positive tests */
|
||||
data.inhibit_delay = WRITE_GROUP_INHIBIT_DELAY_TRUE;
|
||||
test_WriteGroup_Postive(&data);
|
||||
data.inhibit_delay = WRITE_GROUP_INHIBIT_DELAY_FALSE;
|
||||
test_WriteGroup_Postive(&data);
|
||||
data.inhibit_delay = WRITE_GROUP_INHIBIT_DELAY_NONE;
|
||||
test_WriteGroup_Postive(&data);
|
||||
data.write_priority = 0;
|
||||
test_WriteGroup_Negative(&data);
|
||||
data.write_priority = BACNET_MAX_PRIORITY + 1;
|
||||
test_WriteGroup_Negative(&data);
|
||||
data.write_priority = BACNET_MAX_PRIORITY;
|
||||
test_WriteGroup_Postive(&data);
|
||||
data.group_number = 0;
|
||||
test_WriteGroup_Negative(&data);
|
||||
data.group_number = 1;
|
||||
}
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(write_group_tests, test_WriteGroup_Same)
|
||||
#else
|
||||
static void test_WriteGroup_Same(void)
|
||||
#endif
|
||||
{
|
||||
bool status;
|
||||
BACNET_WRITE_GROUP_DATA data = {
|
||||
/* initial test values for sunny day use-case */
|
||||
.group_number = 1,
|
||||
.write_priority = BACNET_MIN_PRIORITY,
|
||||
.change_list = { 0 },
|
||||
.inhibit_delay = WRITE_GROUP_INHIBIT_DELAY_NONE,
|
||||
.next = NULL
|
||||
};
|
||||
BACNET_WRITE_GROUP_DATA test_data = { 0 };
|
||||
|
||||
status = bacnet_write_group_same(&data, &test_data);
|
||||
zassert_false(status, NULL);
|
||||
status = bacnet_write_group_copy(NULL, &data);
|
||||
zassert_false(status, NULL);
|
||||
status = bacnet_write_group_copy(&test_data, NULL);
|
||||
zassert_false(status, NULL);
|
||||
|
||||
status = bacnet_write_group_copy(&test_data, &data);
|
||||
zassert_true(status, NULL);
|
||||
status = bacnet_write_group_same(&data, &test_data);
|
||||
zassert_true(status, NULL);
|
||||
status = bacnet_write_group_same(NULL, &test_data);
|
||||
zassert_false(status, NULL);
|
||||
status = bacnet_write_group_same(&data, NULL);
|
||||
zassert_false(status, NULL);
|
||||
|
||||
status = bacnet_write_group_copy(&test_data, &data);
|
||||
test_data.group_number = 0;
|
||||
status = bacnet_write_group_same(&data, &test_data);
|
||||
zassert_false(status, NULL);
|
||||
|
||||
status = bacnet_write_group_copy(&test_data, &data);
|
||||
test_data.write_priority = BACNET_MAX_PRIORITY;
|
||||
status = bacnet_write_group_same(&data, &test_data);
|
||||
zassert_false(status, NULL);
|
||||
|
||||
status = bacnet_write_group_copy(&test_data, &data);
|
||||
test_data.inhibit_delay = WRITE_GROUP_INHIBIT_DELAY_TRUE;
|
||||
status = bacnet_write_group_same(&data, &test_data);
|
||||
zassert_false(status, NULL);
|
||||
|
||||
status = bacnet_write_group_copy(&test_data, &data);
|
||||
test_data.change_list.channel = 1;
|
||||
status = bacnet_write_group_same(&data, &test_data);
|
||||
zassert_false(status, NULL);
|
||||
|
||||
status = bacnet_write_group_copy(&test_data, &data);
|
||||
test_data.change_list.overriding_priority = 1;
|
||||
status = bacnet_write_group_same(&data, &test_data);
|
||||
zassert_false(status, NULL);
|
||||
|
||||
status = bacnet_write_group_copy(&test_data, &data);
|
||||
test_data.change_list.value.tag = BACNET_APPLICATION_TAG_BOOLEAN;
|
||||
test_data.change_list.value.type.Boolean = true;
|
||||
status = bacnet_write_group_same(&data, &test_data);
|
||||
zassert_false(status, NULL);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST_SUITE(write_group_tests, NULL, NULL, NULL, NULL, NULL);
|
||||
#else
|
||||
void test_main(void)
|
||||
{
|
||||
ztest_test_suite(
|
||||
write_group_tests, ztest_unit_test(test_WriteGroup),
|
||||
ztest_unit_test(test_WriteGroup_Same),
|
||||
ztest_unit_test(test_WriteGroup_Iterate));
|
||||
|
||||
ztest_run_test_suite(write_group_tests);
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user