Skip to content

Commit

Permalink
Added WriteGroup service and Channel object interfaces (bacnet-stack#829
Browse files Browse the repository at this point in the history
)
  • Loading branch information
skarg authored Oct 25, 2024
1 parent 9397cfa commit 3329dff
Show file tree
Hide file tree
Showing 89 changed files with 4,328 additions and 1,016 deletions.
11 changes: 11 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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})

Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ whois:
writepropm:
$(MAKE) -s -C apps $@

.PHONY: writegroup
writegroup:
$(MAKE) -s -C apps $@

.PHONY: router
router:
$(MAKE) -s -C apps $@
Expand Down
6 changes: 5 additions & 1 deletion apps/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 $@
22 changes: 7 additions & 15 deletions apps/server/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 [email protected]
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);
Expand Down Expand Up @@ -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);
Expand Down
39 changes: 39 additions & 0 deletions apps/writegroup/Makefile
Original file line number Diff line number Diff line change
@@ -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
207 changes: 207 additions & 0 deletions apps/writegroup/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
/**
* @file
* @brief command line tool that sends a BACnet WriteGroup-Request message
* to the network
* @author Steve Karg <[email protected]>
* @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;
}
Loading

0 comments on commit 3329dff

Please sign in to comment.