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
+417 -11
View File
@@ -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;
+8
View File
@@ -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,
+3 -1
View File
@@ -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, */
File diff suppressed because it is too large Load Diff
+12 -98
View File
@@ -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);
+111
View File
@@ -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");
}
}
+36
View File
@@ -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
+62
View File
@@ -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;
}
+30
View File
@@ -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
+2
View File
@@ -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
+146
View File
@@ -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
View File
@@ -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 */
+700
View File
@@ -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;
}
+154
View File
@@ -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