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;