Refactored BACnetShedLevel encoding, decoding, and printing into separate file. Added unit testing. (#1187)

* Refactored BACnetShedLevel encoding, decoding, and printing into separate file.  Added unit testing.

* Added API to load control object for shed-level, start-time, duty-window, full-duty-baseline, and enable.

* Fixed bacapp sprintf for shed level and host-n-port found in scan build.
This commit is contained in:
Steve Karg
2025-12-12 08:32:42 -06:00
committed by GitHub
parent 7a319c2901
commit 380540635c
101 changed files with 974 additions and 246 deletions
+10 -233
View File
@@ -151,149 +151,6 @@ bacnet_scale_same(const BACNET_SCALE *value1, const BACNET_SCALE *value2)
}
#endif
#if defined(BACAPP_SHED_LEVEL)
/**
* @brief Encode a BACnetScale value.
*
* BACnetScale ::= CHOICE {
* float-scale [0] REAL,
* integer-scale [1] INTEGER
* }
*
* @param apdu - buffer to encode to
* @param value - value to encode
* @return number of bytes encoded
*/
static int
bacnet_shed_level_encode(uint8_t *apdu, const BACNET_SHED_LEVEL *value)
{
int apdu_len = 0;
if (!value) {
return 0;
}
switch (value->type) {
case BACNET_SHED_TYPE_PERCENT:
apdu_len = encode_context_unsigned(apdu, 0, value->value.percent);
break;
case BACNET_SHED_TYPE_AMOUNT:
apdu_len = encode_context_real(apdu, 2, value->value.amount);
break;
case BACNET_SHED_TYPE_LEVEL:
apdu_len = encode_context_unsigned(apdu, 1, value->value.level);
break;
default:
break;
}
return apdu_len;
}
#endif
#if defined(BACAPP_SHED_LEVEL)
/**
* @brief Decode a BACnetShedLevel value.
*
* BACnetShedLevel ::= CHOICE {
* percent [0] Unsigned,
* level [1] Unsigned,
* amount [2] REAL
* }
*
* @param apdu - buffer to decode to
* @param apdu_size - size of the buffer
* @param value - value to encode
* @return number of bytes decoded, or BACNET_STATUS_ERROR on error
*/
static int bacnet_shed_level_decode(
const uint8_t *apdu, size_t apdu_size, BACNET_SHED_LEVEL *value)
{
int apdu_len = 0;
BACNET_TAG tag = { 0 };
BACNET_UNSIGNED_INTEGER unsigned_value = 0;
float real_value = 0.0f;
if (!apdu) {
return BACNET_STATUS_ERROR;
}
apdu_len = bacnet_tag_decode(apdu, apdu_size, &tag);
if (apdu_len <= 0) {
return BACNET_STATUS_ERROR;
}
switch (tag.number) {
case 0:
/* percent - Unsigned */
apdu_len = bacnet_unsigned_context_decode(
apdu, apdu_size, tag.number, &unsigned_value);
if (apdu_len > 0) {
value->value.percent = unsigned_value;
value->type = BACNET_SHED_TYPE_PERCENT;
}
break;
case 1:
/* level - Unsigned */
apdu_len = bacnet_unsigned_context_decode(
apdu, apdu_size, tag.number, &unsigned_value);
if (apdu_len > 0) {
value->value.level = unsigned_value;
value->type = BACNET_SHED_TYPE_LEVEL;
}
break;
case 2:
apdu_len = bacnet_real_context_decode(
apdu, apdu_size, tag.number, &real_value);
if (apdu_len > 0) {
value->type = BACNET_SHED_TYPE_AMOUNT;
value->value.amount = real_value;
}
break;
default:
return BACNET_STATUS_ERROR;
}
return apdu_len;
}
#endif
#if defined(BACAPP_SHED_LEVEL)
static bool bacnet_shed_level_same(
const BACNET_SHED_LEVEL *value1, const BACNET_SHED_LEVEL *value2)
{
bool status = false;
if (value1 && value2) {
status = true;
if (value1->type != value2->type) {
status = false;
} else {
switch (value1->type) {
case BACNET_SHED_TYPE_PERCENT:
if (value1->value.percent != value2->value.percent) {
status = false;
}
break;
case BACNET_SHED_TYPE_AMOUNT:
if (islessgreater(
value1->value.amount, value2->value.amount)) {
status = false;
}
break;
case BACNET_SHED_TYPE_LEVEL:
if (value1->value.level != value2->value.level) {
status = false;
}
break;
default:
break;
}
}
}
return status;
}
#endif
/**
* @brief Encode application data given by a pointer into the APDU.
* @param apdu - Pointer to the buffer to encode to, or NULL for length
@@ -2062,40 +1919,6 @@ int bacapp_snprintf_shift(int len, char **buf, size_t *buf_size)
return len;
}
#if defined(BACAPP_SHED_LEVEL)
/**
* @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_shed_level(
char *str, size_t str_len, const BACNET_SHED_LEVEL *value)
{
int length = 0;
switch (value->type) {
case BACNET_SHED_TYPE_PERCENT:
length = bacapp_snprintf(
str, str_len, "%u%%", (unsigned)value->value.percent);
break;
case BACNET_SHED_TYPE_LEVEL:
length = bacapp_snprintf(
str, str_len, "%u", (unsigned)value->value.level);
break;
case BACNET_SHED_TYPE_AMOUNT:
length = bacapp_snprintf(
str, str_len, "%f", (double)value->value.amount);
break;
default:
break;
}
return length;
}
#endif
/**
* @brief Print a value to a string for EPICS
* @param str - destination string, or NULL for length only
@@ -3440,7 +3263,7 @@ static int bacapp_snprintf_host_n_port(
str, str_len, "%u.%u.%u.%u:%u", (unsigned)octet_str[0],
(unsigned)octet_str[1], (unsigned)octet_str[2],
(unsigned)octet_str[3], (unsigned)value->port);
ret_val += slen;
ret_val += bacapp_snprintf_shift(slen, &str, &str_len);
} else if (value->host_name) {
const BACNET_CHARACTER_STRING *name;
name = &value->host.name;
@@ -3458,7 +3281,7 @@ static int bacapp_snprintf_host_n_port(
char_str++;
}
slen = bacapp_snprintf(str, str_len, "\"");
ret_val += slen;
ret_val += bacapp_snprintf_shift(slen, &str, &str_len);
}
slen = bacapp_snprintf(str, str_len, "}");
ret_val += bacapp_snprintf_shift(slen, &str, &str_len);
@@ -3801,7 +3624,8 @@ int bacapp_snprintf_value(
BACNET_PROPERTY_ID property = PROP_ALL;
BACNET_OBJECT_TYPE object_type = MAX_BACNET_OBJECT_TYPE;
int ret_val = 0;
#if defined(BACAPP_BDT_ENTRY) || defined(BACAPP_FDT_ENTRY)
#if defined(BACAPP_BDT_ENTRY) || defined(BACAPP_FDT_ENTRY) || \
defined(BACAPP_SHED_LEVEL)
int slen = 0;
#endif
@@ -4039,8 +3863,13 @@ int bacapp_snprintf_value(
#endif
#if defined(BACAPP_SHED_LEVEL)
case BACNET_APPLICATION_TAG_SHED_LEVEL:
ret_val = bacapp_snprintf_shed_level(
slen = bacapp_snprintf(str, str_len, "{");
ret_val += bacapp_snprintf_shift(slen, &str, &str_len);
slen = bacapp_snprintf_shed_level(
str, str_len, &value->type.Shed_Level);
ret_val += bacapp_snprintf_shift(slen, &str, &str_len);
slen = bacapp_snprintf(str, str_len, "}");
ret_val += bacapp_snprintf_shift(slen, &str, &str_len);
break;
#endif
#if defined(BACAPP_ACCESS_RULE)
@@ -4335,58 +4164,6 @@ static bool bacnet_scale_from_ascii(BACNET_SCALE *value, const char *argv)
}
#endif
#if defined(BACAPP_SHED_LEVEL)
/**
* @brief Parse a string into a BACnet Shed Level value
* @param value [out] The BACnet Shed Level value
* @param argv [in] The string to parse
* @return True on success, else False
*/
static bool
bacnet_shed_level_from_ascii(BACNET_SHED_LEVEL *value, const char *argv)
{
bool status = false;
int count;
unsigned percent, level;
float amount;
const char *percentage;
const char *decimal_point;
if (!status) {
percentage = strchr(argv, '%');
if (percentage) {
count = sscanf(argv, "%u", &percent);
if (count == 1) {
value->type = BACNET_SHED_TYPE_PERCENT;
value->value.percent = percent;
status = true;
}
}
}
if (!status) {
decimal_point = strchr(argv, '.');
if (decimal_point) {
count = sscanf(argv, "%f", &amount);
if (count == 1) {
value->type = BACNET_SHED_TYPE_AMOUNT;
value->value.amount = amount;
status = true;
}
}
}
if (!status) {
count = sscanf(argv, "%u", &level);
if (count == 1) {
value->type = BACNET_SHED_TYPE_LEVEL;
value->value.level = level;
status = true;
}
}
return status;
}
#endif
#if defined(BACAPP_DEVICE_OBJECT_PROPERTY_REFERENCE)
/**
* @brief Parse a string into a BACnetDeviceObjectPropertyReference value
+1 -10
View File
@@ -31,6 +31,7 @@
#include "bacnet/calendar_entry.h"
#include "bacnet/special_event.h"
#include "bacnet/channel_value.h"
#include "bacnet/shed_level.h"
#include "bacnet/timer_value.h"
#include "bacnet/secure_connect.h"
@@ -53,16 +54,6 @@ typedef struct BACnetScale {
} type;
} BACNET_SCALE;
/* The shed levels for the LEVEL choice of BACnetShedLevel. */
typedef struct {
BACNET_SHED_LEVEL_TYPE type;
union {
BACNET_UNSIGNED_INTEGER level;
BACNET_UNSIGNED_INTEGER percent;
float amount;
} value;
} BACNET_SHED_LEVEL;
struct BACnet_Application_Data_Value;
typedef struct BACnet_Application_Data_Value {
bool context_specific; /* true if context specific data */
+327 -3
View File
@@ -16,6 +16,7 @@
#include "bacnet/bacdcode.h"
#include "bacnet/bactext.h"
#include "bacnet/datetime.h"
#include "bacnet/shed_level.h"
#include "bacnet/basic/object/lc.h"
#include "bacnet/basic/object/ao.h"
#include "bacnet/wp.h"
@@ -33,9 +34,6 @@
#define LOAD_CONTROL_TASK_INTERVAL_MS 1000UL
struct object_data {
void *Context;
const char *Object_Name;
const char *Description;
/* indicates the current load shedding state of the object */
BACNET_SHED_STATE Present_Value;
/* tracking for the Load Control finite state machine */
@@ -91,6 +89,9 @@ struct object_data {
load_control_manipulated_object_read_callback Manipulated_Object_Read;
/* state machine task time tracking per object */
uint32_t Task_Milliseconds;
void *Context;
const char *Object_Name;
const char *Description;
};
/* Key List for storing the object data sorted by instance number */
static OS_Keylist Object_List;
@@ -1667,6 +1668,329 @@ bool Load_Control_Shed_Level_Array(
return true;
}
/**
* @brief For a given object instance-number, gets the requested shed level
* property
* @param object_instance - object-instance number of the object
* @param value - holds the value to be retrieved
* @return the requested shed level of this object instance.
*/
bool Load_Control_Requested_Shed_Level(
uint32_t object_instance, BACNET_SHED_LEVEL *value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
status = bacnet_shed_level_copy(value, &pObject->Requested_Shed_Level);
}
return status;
}
/**
* @brief For a given object instance-number, sets the requested shed level
* property
* @param object_instance - object-instance number of the object
* @param value - holds the value to be set
* @return true if requested shed level was set
*/
bool Load_Control_Requested_Shed_Level_Set(
uint32_t object_instance, BACNET_SHED_LEVEL *value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
status = bacnet_shed_level_copy(&pObject->Requested_Shed_Level, value);
}
return status;
}
/**
* @brief For a given object instance-number, gets the expected shed level
* property
* @param object_instance - object-instance number of the object
* @param value - holds the value to be retrieved
* @return the expected shed level of this object instance.
*/
bool Load_Control_Expected_Shed_Level(
uint32_t object_instance, BACNET_SHED_LEVEL *value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
status = bacnet_shed_level_copy(value, &pObject->Expected_Shed_Level);
}
return status;
}
/**
* @brief For a given object instance-number, sets the expected shed level
* property
* @param object_instance - object-instance number of the object
* @param value - holds the value to be set
* @return true if expected shed level was set
*/
bool Load_Control_Expected_Shed_Level_Set(
uint32_t object_instance, BACNET_SHED_LEVEL *value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
status = bacnet_shed_level_copy(&pObject->Expected_Shed_Level, value);
}
return status;
}
/**
* @brief For a given object instance-number, gets the expected shed level
* property
* @param object_instance - object-instance number of the object
* @param value - holds the value to be retrieved
* @return the expected shed level of this object instance.
*/
bool Load_Control_Actual_Shed_Level(
uint32_t object_instance, BACNET_SHED_LEVEL *value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
status = bacnet_shed_level_copy(value, &pObject->Expected_Shed_Level);
}
return status;
}
/**
* @brief For a given object instance-number, sets the expected shed level
* property
* @param object_instance - object-instance number of the object
* @param value - holds the value to be set
* @return true if expected shed level was set
*/
bool Load_Control_Actual_Shed_Level_Set(
uint32_t object_instance, BACNET_SHED_LEVEL *value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
status = bacnet_shed_level_copy(&pObject->Expected_Shed_Level, value);
}
return status;
}
/**
* @brief For a given object instance-number, gets the start-time property
* @param object_instance - object-instance number of the object
* @param value - holds the value to be retrieved
* @return the start-time property value of this object instance.
*/
bool Load_Control_Start_Time(uint32_t object_instance, BACNET_DATE_TIME *value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
datetime_copy_date(&value->date, &pObject->Start_Time.date);
datetime_copy_time(&value->time, &pObject->Start_Time.time);
status = true;
}
return status;
}
/**
* @brief For a given object instance-number, sets the start-time property
* @param object_instance - object-instance number of the object
* @param value - holds the value to be set
* @return true if property value was set
*/
bool Load_Control_Start_Time_Set(
uint32_t object_instance, BACNET_DATE_TIME *value)
{
bool status = false;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
datetime_copy_date(&pObject->Start_Time.date, &value->date);
datetime_copy_time(&pObject->Start_Time.time, &value->time);
status = true;
}
return status;
}
/**
* @brief For a given object instance-number, gets the shed-duration property
* @param object_instance - object-instance number of the object
* @return the shed-duration property value of this object instance.
*/
uint32_t Load_Control_Shed_Duration(uint32_t object_instance)
{
struct object_data *pObject;
uint32_t value = 0;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
value = pObject->Shed_Duration;
}
return value;
}
/**
* @brief For a given object instance-number, sets the shed-duration property
* @param object_instance - object-instance number of the object
* @param value - holds the value to be set
* @return true if property value was set
*/
bool Load_Control_Shed_Duration_Set(uint32_t object_instance, uint32_t value)
{
struct object_data *pObject;
bool status = false;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
pObject->Shed_Duration = value;
status = true;
}
return status;
}
/**
* @brief For a given object instance-number, gets the duty-window property
* @param object_instance - object-instance number of the object
* @return the duty-window property value of this object instance.
*/
uint32_t Load_Control_Duty_Window(uint32_t object_instance)
{
struct object_data *pObject;
uint32_t value = 0;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
value = pObject->Duty_Window;
}
return value;
}
/**
* @brief For a given object instance-number, sets the duty-window property
* @param object_instance - object-instance number of the object
* @param value - holds the value to be set
* @return true if property value was set
*/
bool Load_Control_Duty_Window_Set(uint32_t object_instance, uint32_t value)
{
struct object_data *pObject;
bool status = false;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
pObject->Duty_Window = value;
status = true;
}
return status;
}
/**
* @brief For a given object instance-number, gets the full-duty-baseline
* property value
* @param object_instance - object-instance number of the object
* @return the full-duty-baseline property value of this object instance.
*/
float Load_Control_Full_Duty_Baseline(uint32_t object_instance)
{
struct object_data *pObject;
float value = 0.0f;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
value = pObject->Full_Duty_Baseline;
}
return value;
}
/**
* @brief For a given object instance-number, sets the full-duty-baseline
* property value
* @param object_instance - object-instance number of the object
* @param value - holds the value to be set
* @return true if property value was set
*/
bool Load_Control_Full_Duty_Baseline_Set(uint32_t object_instance, float value)
{
struct object_data *pObject;
bool status = false;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
pObject->Full_Duty_Baseline = value;
status = true;
}
return status;
}
/**
* @brief For a given object instance-number, gets the enable property value
* @param object_instance - object-instance number of the object
* @return the enable property value of this object instance.
*/
bool Load_Control_Enable(uint32_t object_instance)
{
struct object_data *pObject;
bool value = false;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
value = pObject->Load_Control_Enable;
}
return value;
}
/**
* @brief For a given object instance-number, sets the enable property
* @param object_instance - object-instance number of the object
* @param value - holds the value to be set
* @return true if property value was set
*/
bool Load_Control_Enable_Set(uint32_t object_instance, bool value)
{
struct object_data *pObject;
bool status = false;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
pObject->Load_Control_Enable = value;
status = true;
}
return status;
}
/**
* @brief Set the context used with a specific object instance
* @param object_instance [in] BACnet object instance number
+48
View File
@@ -13,6 +13,7 @@
#include "bacnet/bacdef.h"
/* BACnet Stack API */
#include "bacnet/bacerror.h"
#include "bacnet/shed_level.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
@@ -112,6 +113,53 @@ bool Load_Control_Shed_Level_Array(
uint32_t array_entry,
struct shed_level_data *value);
BACNET_STACK_EXPORT
bool Load_Control_Requested_Shed_Level(
uint32_t object_instance, BACNET_SHED_LEVEL *value);
BACNET_STACK_EXPORT
bool Load_Control_Requested_Shed_Level_Set(
uint32_t object_instance, BACNET_SHED_LEVEL *value);
BACNET_STACK_EXPORT
bool Load_Control_Expected_Shed_Level(
uint32_t object_instance, BACNET_SHED_LEVEL *value);
BACNET_STACK_EXPORT
bool Load_Control_Expected_Shed_Level_Set(
uint32_t object_instance, BACNET_SHED_LEVEL *value);
BACNET_STACK_EXPORT
bool Load_Control_Actual_Shed_Level(
uint32_t object_instance, BACNET_SHED_LEVEL *value);
BACNET_STACK_EXPORT
bool Load_Control_Actual_Shed_Level_Set(
uint32_t object_instance, BACNET_SHED_LEVEL *value);
BACNET_STACK_EXPORT
bool Load_Control_Start_Time(uint32_t object_instance, BACNET_DATE_TIME *value);
BACNET_STACK_EXPORT
bool Load_Control_Start_Time_Set(
uint32_t object_instance, BACNET_DATE_TIME *value);
BACNET_STACK_EXPORT
uint32_t Load_Control_Shed_Duration(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Load_Control_Shed_Duration_Set(uint32_t object_instance, uint32_t value);
BACNET_STACK_EXPORT
uint32_t Load_Control_Duty_Window(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Load_Control_Duty_Window_Set(uint32_t object_instance, uint32_t value);
BACNET_STACK_EXPORT
float Load_Control_Full_Duty_Baseline(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Load_Control_Full_Duty_Baseline_Set(uint32_t object_instance, float value);
BACNET_STACK_EXPORT
bool Load_Control_Enable(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Load_Control_Enable_Set(uint32_t object_instance, bool value);
BACNET_STACK_EXPORT
void *Load_Control_Context_Get(uint32_t object_instance);
BACNET_STACK_EXPORT
+259
View File
@@ -0,0 +1,259 @@
/**
* @file
* @brief BACnetShedLevel complex data type encode and decode
* @author Steve Karg <skarg@users.sourceforge.net>
* @date December 2025
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
*/
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdcode.h"
#include "bacnet/shed_level.h"
/**
* @brief Encode a BACnetShedLevel value.
*
* BACnetShedLevel ::= CHOICE {
* percent[0] Unsigned,
* level[1] Unsigned,
* amount[2] Real
* }
*
* @param apdu - buffer to encode to
* @param value - value to encode
* @return number of bytes encoded
*/
int bacnet_shed_level_encode(uint8_t *apdu, const BACNET_SHED_LEVEL *value)
{
int apdu_len = 0;
if (!value) {
return 0;
}
switch (value->type) {
case BACNET_SHED_TYPE_PERCENT:
apdu_len = encode_context_unsigned(apdu, 0, value->value.percent);
break;
case BACNET_SHED_TYPE_LEVEL:
apdu_len = encode_context_unsigned(apdu, 1, value->value.level);
break;
case BACNET_SHED_TYPE_AMOUNT:
apdu_len = encode_context_real(apdu, 2, value->value.amount);
break;
default:
break;
}
return apdu_len;
}
/**
* @brief Decode a BACnetShedLevel value.
*
* BACnetShedLevel ::= CHOICE {
* percent[0] Unsigned,
* level[1] Unsigned,
* amount[2] Real
* }
*
* @param apdu - buffer to decode to
* @param apdu_size - size of the buffer
* @param value - value to encode, or NULL for size only
* @return number of bytes decoded, or BACNET_STATUS_ERROR on error
*/
int bacnet_shed_level_decode(
const uint8_t *apdu, size_t apdu_size, BACNET_SHED_LEVEL *value)
{
int apdu_len = 0;
BACNET_TAG tag = { 0 };
BACNET_UNSIGNED_INTEGER unsigned_value = 0;
float real_value = 0.0f;
if (!apdu) {
return BACNET_STATUS_ERROR;
}
apdu_len = bacnet_tag_decode(apdu, apdu_size, &tag);
if (apdu_len <= 0) {
return BACNET_STATUS_ERROR;
}
switch (tag.number) {
case 0:
/* percent - Unsigned */
apdu_len = bacnet_unsigned_context_decode(
apdu, apdu_size, tag.number, &unsigned_value);
if (apdu_len > 0) {
if (value) {
value->value.percent = unsigned_value;
value->type = BACNET_SHED_TYPE_PERCENT;
}
}
break;
case 1:
/* level - Unsigned */
apdu_len = bacnet_unsigned_context_decode(
apdu, apdu_size, tag.number, &unsigned_value);
if (apdu_len > 0) {
if (value) {
value->value.level = unsigned_value;
value->type = BACNET_SHED_TYPE_LEVEL;
}
}
break;
case 2:
apdu_len = bacnet_real_context_decode(
apdu, apdu_size, tag.number, &real_value);
if (apdu_len > 0) {
if (value) {
value->type = BACNET_SHED_TYPE_AMOUNT;
value->value.amount = real_value;
}
}
break;
default:
return BACNET_STATUS_ERROR;
}
return apdu_len;
}
/**
* @brief Compare the BACnetShedLevel complex data
* @param value1 - BACNET_SHED_LEVEL structure
* @param value2 - BACNET_SHED_LEVEL structure
* @return true if the same
*/
bool bacnet_shed_level_same(
const BACNET_SHED_LEVEL *value1, const BACNET_SHED_LEVEL *value2)
{
bool status = false;
if (value1 && value2) {
status = true;
if (value1->type != value2->type) {
status = false;
} else {
switch (value1->type) {
case BACNET_SHED_TYPE_PERCENT:
if (value1->value.percent != value2->value.percent) {
status = false;
}
break;
case BACNET_SHED_TYPE_LEVEL:
if (value1->value.level != value2->value.level) {
status = false;
}
break;
case BACNET_SHED_TYPE_AMOUNT:
if (islessgreater(
value1->value.amount, value2->value.amount)) {
status = false;
}
break;
default:
break;
}
}
}
return status;
}
/**
* @brief Compare the BACnetShedLevel complex data
* @param value1 - BACNET_SHED_LEVEL structure
* @param value2 - BACNET_SHED_LEVEL structure
* @return true if the same
*/
bool bacnet_shed_level_copy(
BACNET_SHED_LEVEL *dest, const BACNET_SHED_LEVEL *src)
{
if (!dest || !src) {
return false;
}
memcpy(dest, src, sizeof(BACNET_SHED_LEVEL));
return true;
}
/**
* @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
*/
int bacapp_snprintf_shed_level(
char *str, size_t str_len, const BACNET_SHED_LEVEL *value)
{
int length = 0;
switch (value->type) {
case BACNET_SHED_TYPE_PERCENT:
length = bacnet_snprintf(
str, str_len, length, "%u%%", (unsigned)value->value.percent);
break;
case BACNET_SHED_TYPE_LEVEL:
length = bacnet_snprintf(
str, str_len, length, "%u", (unsigned)value->value.level);
break;
case BACNET_SHED_TYPE_AMOUNT:
length = bacnet_snprintf(
str, str_len, length, "%f", (double)value->value.amount);
break;
default:
break;
}
return length;
}
/**
* @brief Parse a string into a BACnet Shed Level value
* @param value [out] The BACnet Shed Level value
* @param argv [in] The string to parse
* @return True on success, else False
*/
bool bacnet_shed_level_from_ascii(BACNET_SHED_LEVEL *value, const char *argv)
{
bool status = false;
int count;
unsigned percent, level;
float amount;
const char *percentage;
const char *decimal_point;
if (!status) {
percentage = strchr(argv, '%');
if (percentage) {
count = sscanf(argv, "%u", &percent);
if (count == 1) {
value->type = BACNET_SHED_TYPE_PERCENT;
value->value.percent = percent;
status = true;
}
}
}
if (!status) {
decimal_point = strchr(argv, '.');
if (decimal_point) {
count = sscanf(argv, "%f", &amount);
if (count == 1) {
value->type = BACNET_SHED_TYPE_AMOUNT;
value->value.amount = amount;
status = true;
}
}
}
if (!status) {
count = sscanf(argv, "%u", &level);
if (count == 1) {
value->type = BACNET_SHED_TYPE_LEVEL;
value->value.level = level;
status = true;
}
}
return status;
}
+58
View File
@@ -0,0 +1,58 @@
/**
* @file
* @brief API for BACnetShedLevel complex data type encode and decode
* @author Steve Karg <skarg@users.sourceforge.net>
* @date December 2025
* @copyright SPDX-License-Identifier: MIT
*/
#ifndef BACNET_SHED_LEVEL_H
#define BACNET_SHED_LEVEL_H
#include <stdint.h>
#include <stdbool.h>
/* BACnet Stack defines - first */
#include "bacnet/bacdef.h"
#include "bacnet/bacint.h"
/* The shed levels for the LEVEL choice of BACnetShedLevel. */
typedef struct BACnetShedLevel {
BACNET_SHED_LEVEL_TYPE type;
union {
BACNET_UNSIGNED_INTEGER level;
BACNET_UNSIGNED_INTEGER percent;
float amount;
} value;
} BACNET_SHED_LEVEL;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/** Decode Special Event */
BACNET_STACK_EXPORT
int bacnet_shed_level_decode(
const uint8_t *apdu, size_t apdu_size, BACNET_SHED_LEVEL *value);
/** Encode Special Event */
BACNET_STACK_EXPORT
int bacnet_shed_level_encode(uint8_t *apdu, const BACNET_SHED_LEVEL *value);
BACNET_STACK_EXPORT
bool bacnet_shed_level_same(
const BACNET_SHED_LEVEL *value1, const BACNET_SHED_LEVEL *value2);
BACNET_STACK_EXPORT
bool bacnet_shed_level_copy(
BACNET_SHED_LEVEL *dest, const BACNET_SHED_LEVEL *src);
BACNET_STACK_EXPORT
int bacapp_snprintf_shed_level(
char *str, size_t str_len, const BACNET_SHED_LEVEL *value);
BACNET_STACK_EXPORT
bool bacnet_shed_level_from_ascii(BACNET_SHED_LEVEL *value, const char *argv);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif