Added WriteGroup service and Channel object interfaces (#829)

This commit is contained in:
Steve Karg
2024-10-25 10:43:29 -05:00
committed by GitHub
parent 9397cfaafb
commit 3329dff337
89 changed files with 4334 additions and 1022 deletions
+5 -1
View File
@@ -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
View File
@@ -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);
+39
View File
@@ -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
View File
@@ -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;
}