Added WriteGroup service and Channel object interfaces (#829)
This commit is contained in:
+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
|
||||
Reference in New Issue
Block a user