Refactor/cov subscriptions encoding decoding test (#1296)

* Add BACnetRecipientProcess type encoding and decoding functions with associated tests

* Refactor COV subscription encoding and decoding functions to reduce code size and reuse existing unit tested functions.

* Refactor COV subscription handling to simplify error checking
This commit is contained in:
Steve Karg
2026-04-20 10:53:55 -05:00
committed by GitHub
parent 52561f2336
commit b9149dd639
15 changed files with 1151 additions and 171 deletions
+169
View File
@@ -733,6 +733,175 @@ int bacnet_recipient_context_decode(
return apdu_len;
}
/**
* @brief Encode the BACnetRecipientProcess complex data
*
* BACnetRecipientProcess ::= SEQUENCE {
* recipient[0] BACnetRecipient,
* process-identifier[1] Unsigned32
* }
*
* @param apdu Pointer to the buffer for encoding.
* @param recipient_process Pointer to the property data to be encoded.
*
* @return bytes encoded or #BACNET_STATUS_REJECT on error.
*/
int bacnet_recipient_process_encode(
uint8_t *apdu, const BACNET_RECIPIENT_PROCESS *recipient_process)
{
int apdu_len = 0, len = 0;
if (recipient_process) {
len = bacnet_recipient_context_encode(
apdu, 0, &recipient_process->recipient);
apdu_len += len;
if (apdu) {
apdu += len;
}
/* Process Identifier - Unsigned32 */
len = encode_context_unsigned(
apdu, 1, recipient_process->process_identifier);
apdu_len += len;
}
return apdu_len;
}
/**
* @brief Encode a BACnetRecipientProcess complex data type
* @param apdu - the APDU buffer
* @param tag_number - context tag number
* @param recipient_process Pointer to the property data to be encoded.
* @return length of the APDU buffer, or 0 if not able to encode
*/
int bacnet_recipient_process_context_encode(
uint8_t *apdu,
uint8_t tag_number,
const BACNET_RECIPIENT_PROCESS *recipient_process)
{
int len = 0;
int apdu_len = 0;
if (recipient_process) {
len = encode_opening_tag(apdu, tag_number);
apdu_len += len;
if (apdu) {
apdu += len;
}
len = bacnet_recipient_process_encode(apdu, recipient_process);
apdu_len += len;
if (apdu) {
apdu += len;
}
len = encode_closing_tag(apdu, tag_number);
apdu_len += len;
}
return apdu_len;
}
/**
* @brief Decode the BACnetRecipientProcess complex data
*
* BACnetRecipientProcess ::= SEQUENCE {
* recipient[0] BACnetRecipient,
* process-identifier[1] Unsigned32
* }
*
* @param apdu Pointer to the buffer for decoding.
* @param apdu_size Size of the APDU buffer.
* @param recipient_process Pointer to the property data to be decoded.
*
* @return bytes decoded or #BACNET_STATUS_ERROR on error.
*/
int bacnet_recipient_process_decode(
const uint8_t *apdu,
int apdu_size,
BACNET_RECIPIENT_PROCESS *recipient_process)
{
int len = 0, apdu_len = 0;
BACNET_RECIPIENT recipient = { 0 };
BACNET_UNSIGNED_INTEGER unsigned_value = 0;
if (!apdu) {
return BACNET_STATUS_ERROR;
}
/* Recipient */
len = bacnet_recipient_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 0, &recipient);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
if (recipient_process) {
bacnet_recipient_copy(&recipient_process->recipient, &recipient);
}
apdu_len += len;
/* Process Identifier */
len = bacnet_unsigned_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 1, &unsigned_value);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
if (recipient_process) {
recipient_process->process_identifier = unsigned_value;
}
apdu_len += len;
return apdu_len;
}
/**
* @brief Decode a BACnetRecipientProcess complex data type
* @param apdu Pointer to the APDU buffer.
* @param apdu_size - the APDU buffer length
* @param tag_number The tag number that shall hold the time stamp.
* @param value Pointer to the variable that shall take the time stamp values.
* @return number of bytes decoded, zero on opening tag mismatch,
* or BACNET_STATUS_ERROR if an error occurs
*/
int bacnet_recipient_process_context_decode(
const uint8_t *apdu,
uint32_t apdu_size,
uint8_t tag_number,
BACNET_RECIPIENT_PROCESS *value)
{
int len = 0;
int apdu_len = 0;
if (!bacnet_is_opening_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, tag_number, &len)) {
return 0;
}
apdu_len += len;
len = bacnet_recipient_process_decode(
&apdu[apdu_len], apdu_size - apdu_len, value);
if (len < 0) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
if (!bacnet_is_closing_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, tag_number, &len)) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
return apdu_len;
}
/**
* @brief Copy the BACnetRecipientProcess complex data from src to dest
* @param dest - BACnetRecipientProcess 1 structure
* @param src - BACnetRecipientProcess 2 structure
*/
void bacnet_recipient_process_copy(
BACNET_RECIPIENT_PROCESS *dest, const BACNET_RECIPIENT_PROCESS *src)
{
if (dest && src) {
dest->process_identifier = src->process_identifier;
bacnet_recipient_copy(&dest->recipient, &src->recipient);
}
}
/**
* Convert BACnet_Destination to ASCII for printing
*
+34
View File
@@ -36,6 +36,17 @@ typedef struct BACnet_Recipient {
} type;
} BACNET_RECIPIENT;
/**
* BACnetRecipientProcess ::= SEQUENCE {
* recipient[0] BACnetRecipient,
* process-identifier[1] Unsigned32
* }
*/
typedef struct BACnetRecipientProcess {
uint32_t process_identifier;
BACNET_RECIPIENT recipient;
} BACNET_RECIPIENT_PROCESS;
struct BACnet_Recipient_List;
typedef struct BACnet_Recipient_List {
BACNET_RECIPIENT recipient;
@@ -136,6 +147,29 @@ int bacnet_recipient_context_decode(
uint8_t tag_number,
BACNET_RECIPIENT *value);
BACNET_STACK_EXPORT
int bacnet_recipient_process_encode(
uint8_t *apdu, const BACNET_RECIPIENT_PROCESS *recipient_process);
BACNET_STACK_EXPORT
int bacnet_recipient_process_context_encode(
uint8_t *apdu,
uint8_t tag_number,
const BACNET_RECIPIENT_PROCESS *recipient_process);
BACNET_STACK_EXPORT
int bacnet_recipient_process_decode(
const uint8_t *apdu,
int apdu_size,
BACNET_RECIPIENT_PROCESS *recipient_process);
BACNET_STACK_EXPORT
int bacnet_recipient_process_context_decode(
const uint8_t *apdu,
uint32_t apdu_size,
uint8_t tag_number,
BACNET_RECIPIENT_PROCESS *value);
BACNET_STACK_EXPORT
void bacnet_recipient_process_copy(
BACNET_RECIPIENT_PROCESS *dest, const BACNET_RECIPIENT_PROCESS *src);
BACNET_STACK_EXPORT
bool bacnet_recipient_address_from_ascii(BACNET_ADDRESS *src, const char *arg);
BACNET_STACK_EXPORT
+2 -2
View File
@@ -2963,8 +2963,8 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
break;
#endif
case PROP_ACTIVE_COV_SUBSCRIPTIONS:
if ((apdu_len = handler_cov_encode_subscriptions(
&apdu[0], apdu_max)) < 0) {
apdu_len = handler_cov_encode_subscriptions(&apdu[0], apdu_max);
if (apdu_len < 0) {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
+10
View File
@@ -29,6 +29,12 @@
#include "bacnet/basic/server/bacnet_basic.h"
#include "bacnet/basic/server/bacnet_port.h"
#ifdef CONFIG_BACNET_BASIC_COV_SUBSCRIPTIONS_SIZE
#define BACNET_COV_SUBSCRIPTIONS_SIZE CONFIG_BACNET_BASIC_COV_SUBSCRIPTIONS_SIZE
#else
#define BACNET_COV_SUBSCRIPTIONS_SIZE 0
#endif
/* 1s timer for basic non-critical timed tasks */
static struct mstimer BACnet_Task_Timer;
/* task timer for object functionality */
@@ -277,11 +283,15 @@ void bacnet_basic_task(void)
BACnet_Uptime_Seconds += elapsed_seconds;
dcc_timer_seconds(elapsed_seconds);
datalink_maintenance_timer(elapsed_seconds);
#if (BACNET_COV_SUBSCRIPTIONS_SIZE > 0)
handler_cov_timer_seconds(elapsed_seconds);
#endif
}
#if (BACNET_COV_SUBSCRIPTIONS_SIZE > 0)
while (!handler_cov_fsm()) {
/* waiting for COV processing to be IDLE */
}
#endif
/* object specific cyclic tasks */
if (mstimer_expired(&BACnet_Object_Timer)) {
elapsed_milliseconds = mstimer_elapsed(&BACnet_Object_Timer);
+2 -2
View File
@@ -2826,8 +2826,8 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
#endif
#if (BACNET_COV_SUBSCRIPTIONS_SIZE > 0)
case PROP_ACTIVE_COV_SUBSCRIPTIONS:
if ((apdu_len = handler_cov_encode_subscriptions(
&apdu[0], apdu_max)) < 0) {
apdu_len = handler_cov_encode_subscriptions(&apdu[0], apdu_max);
if (apdu_len < 0) {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
+67 -164
View File
@@ -8,7 +8,6 @@
#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
/* BACnet Stack defines - first */
#include "bacnet/bacdef.h"
@@ -37,26 +36,26 @@
#define MAX_COV_PROPERTIES 2
#endif
typedef struct BACnet_COV_Address {
typedef struct BACnet_COV_Handler_Address {
bool valid : 1;
BACNET_ADDRESS dest;
} BACNET_COV_ADDRESS;
} BACNET_COV_HANDLER_ADDRESS;
/* note: This COV service only monitors the properties
of an object that have been specified in the standard. */
typedef struct BACnet_COV_Subscription_Flags {
typedef struct BACnet_COV_Handler_Subscription_Flags {
bool issueConfirmedNotifications : 1; /* optional */
bool send_requested : 1;
} BACNET_COV_SUBSCRIPTION_FLAGS;
} BACNET_COV_HANDLER_SUBSCRIPTION_FLAGS;
typedef struct BACnet_COV_Subscription {
BACNET_COV_SUBSCRIPTION_FLAGS flag;
typedef struct BACnet_COV_Handler_Subscription {
BACNET_COV_HANDLER_SUBSCRIPTION_FLAGS flag;
unsigned dest_index;
uint8_t invokeID; /* for confirmed COV */
uint32_t subscriberProcessIdentifier;
uint32_t lifetime; /* optional */
BACNET_OBJECT_ID monitoredObjectIdentifier;
} BACNET_COV_SUBSCRIPTION;
} BACNET_COV_HANDLER_SUBSCRIPTION;
#ifndef MAX_COV_SUBSCRIPTIONS
#define MAX_COV_SUBSCRIPTIONS 128
@@ -71,7 +70,7 @@ static OS_Keylist COV_Subscriptions_List[MAX_NUM_DEVICES];
#ifndef MAX_COV_ADDRESSES
#define MAX_COV_ADDRESSES 16
#endif
static BACNET_COV_ADDRESS COV_Addresses[MAX_COV_ADDRESSES];
static BACNET_COV_HANDLER_ADDRESS COV_Addresses[MAX_COV_ADDRESSES];
/**
* @brief Deletes a subscription
@@ -81,7 +80,7 @@ static BACNET_COV_ADDRESS COV_Addresses[MAX_COV_ADDRESSES];
static bool cov_subscription_delete(uint32_t list_idx)
{
bool status = false;
BACNET_COV_SUBSCRIPTION *subscription = NULL;
BACNET_COV_HANDLER_SUBSCRIPTION *subscription = NULL;
subscription = Keylist_Data_Delete_By_Index(COV_Subscriptions, list_idx);
if (subscription) {
@@ -97,16 +96,17 @@ static bool cov_subscription_delete(uint32_t list_idx)
* @param list_key - keylist key
* @return the subscription that was created, or NULL
*/
static BACNET_COV_SUBSCRIPTION *cov_subscription_create(uint32_t list_key)
static BACNET_COV_HANDLER_SUBSCRIPTION *
cov_subscription_create(uint32_t list_key)
{
BACNET_COV_SUBSCRIPTION *subscription = NULL;
BACNET_COV_HANDLER_SUBSCRIPTION *subscription = NULL;
int index = 0;
if (list_key >= MAX_COV_SUBSCRIPTIONS) {
return NULL;
}
subscription = Keylist_Data(COV_Subscriptions, list_key);
if (!subscription) {
subscription = calloc(1, sizeof(BACNET_COV_SUBSCRIPTION));
subscription = calloc(1, sizeof(BACNET_COV_HANDLER_SUBSCRIPTION));
if (subscription) {
index = Keylist_Data_Add(COV_Subscriptions, list_key, subscription);
if (index < 0) {
@@ -150,7 +150,7 @@ static void cov_address_remove_unused(void)
unsigned index = 0;
unsigned cov_index = 0;
bool found = false;
BACNET_COV_SUBSCRIPTION *subscription = NULL;
BACNET_COV_HANDLER_SUBSCRIPTION *subscription = NULL;
#ifdef BAC_ROUTING
uint16_t current_dev_id = Routed_Device_Object_Index();
@@ -243,160 +243,62 @@ static int cov_address_add(const BACNET_ADDRESS *dest)
return index;
}
/*
BACnetCOVSubscription ::= SEQUENCE {
Recipient [0] BACnetRecipientProcess,
BACnetRecipient ::= CHOICE {
device [0] BACnetObjectIdentifier,
address [1] BACnetAddress
BACnetAddress ::= SEQUENCE {
network-number Unsigned16, -- A value of 0 indicates the local network
mac-address OCTET STRING -- A string of length 0 indicates a broadcast
}
}
BACnetRecipientProcess ::= SEQUENCE {
recipient [0] BACnetRecipient,
processIdentifier [1] Unsigned32
}
MonitoredPropertyReference [1] BACnetObjectPropertyReference,
BACnetObjectPropertyReference ::= SEQUENCE {
objectIdentifier [0] BACnetObjectIdentifier,
propertyIdentifier [1] BACnetPropertyIdentifier,
propertyArrayIndex [2] Unsigned OPTIONAL -- used only with array datatype
-- if omitted with an array the entire array is referenced
}
IssueConfirmedNotifications [2] BOOLEAN,
TimeRemaining [3] Unsigned,
COVIncrement [4] REAL OPTIONAL
*/
static int cov_encode_subscription(
uint8_t *apdu,
int max_apdu,
const BACNET_COV_SUBSCRIPTION *cov_subscription)
{
int len = 0;
int apdu_len = 0;
BACNET_OCTET_STRING octet_string = { 0 };
BACNET_ADDRESS *dest = NULL;
(void)max_apdu;
if (!cov_subscription) {
return 0;
}
dest = cov_address_get(cov_subscription->dest_index);
if (!dest) {
return 0;
}
/* Recipient [0] BACnetRecipientProcess - opening */
len = encode_opening_tag(&apdu[apdu_len], 0);
apdu_len += len;
/* recipient [0] BACnetRecipient - opening */
len = encode_opening_tag(&apdu[apdu_len], 0);
apdu_len += len;
/* CHOICE - address [1] BACnetAddress - opening */
len = encode_opening_tag(&apdu[apdu_len], 1);
apdu_len += len;
/* network-number Unsigned16, */
/* -- A value of 0 indicates the local network */
len = encode_application_unsigned(&apdu[apdu_len], dest->net);
apdu_len += len;
/* mac-address OCTET STRING */
/* -- A string of length 0 indicates a broadcast */
if (dest->net) {
octetstring_init(&octet_string, &dest->adr[0], dest->len);
} else {
octetstring_init(&octet_string, &dest->mac[0], dest->mac_len);
}
len = encode_application_octet_string(&apdu[apdu_len], &octet_string);
apdu_len += len;
/* CHOICE - address [1] BACnetAddress - closing */
len = encode_closing_tag(&apdu[apdu_len], 1);
apdu_len += len;
/* recipient [0] BACnetRecipient - closing */
len = encode_closing_tag(&apdu[apdu_len], 0);
apdu_len += len;
/* processIdentifier [1] Unsigned32 */
len = encode_context_unsigned(
&apdu[apdu_len], 1, cov_subscription->subscriberProcessIdentifier);
apdu_len += len;
/* Recipient [0] BACnetRecipientProcess - closing */
len = encode_closing_tag(&apdu[apdu_len], 0);
apdu_len += len;
/* MonitoredPropertyReference [1] BACnetObjectPropertyReference, */
len = encode_opening_tag(&apdu[apdu_len], 1);
apdu_len += len;
/* objectIdentifier [0] */
len = encode_context_object_id(
&apdu[apdu_len], 0, cov_subscription->monitoredObjectIdentifier.type,
cov_subscription->monitoredObjectIdentifier.instance);
apdu_len += len;
/* propertyIdentifier [1] */
/* FIXME: we are monitoring 2 properties! How to encode? */
len = encode_context_enumerated(&apdu[apdu_len], 1, PROP_PRESENT_VALUE);
apdu_len += len;
/* MonitoredPropertyReference [1] - closing */
len = encode_closing_tag(&apdu[apdu_len], 1);
apdu_len += len;
/* IssueConfirmedNotifications [2] BOOLEAN, */
len = encode_context_boolean(
&apdu[apdu_len], 2, cov_subscription->flag.issueConfirmedNotifications);
apdu_len += len;
/* TimeRemaining [3] Unsigned, */
len =
encode_context_unsigned(&apdu[apdu_len], 3, cov_subscription->lifetime);
apdu_len += len;
return apdu_len;
}
/** Handle a request to list all the COV subscriptions.
* @ingroup DSCOV
* Invoked by a request to read the Device object's
* PROP_ACTIVE_COV_SUBSCRIPTIONS. Loops through the list of COV Subscriptions,
* and, for each valid one, adds its description to the APDU.
* @param apdu [out] Buffer in which the APDU contents are built.
* @param max_apdu [in] Max length of the APDU buffer.
* @return How many bytes were encoded in the buffer, or -2 if the response
* would not fit within the buffer.
* @param apdu_size [in] Max length of the APDU buffer.
* @return Number of bytes encoded in the buffer, or
* #BACNET_STATUS_ABORT if the response would not fit within the buffer.
*/
/* Maximume length for an encoded COV subscription - 31 bytes for BACNET IP6
* 35 bytes for IPv4 (longest MAC) with the maximum length
* of PID (5 bytes) and lets round it up to the 64bit machine word
* alignment */
#define MAX_COV_SUB_SIZE (40)
int handler_cov_encode_subscriptions(uint8_t *apdu, int max_apdu)
int handler_cov_encode_subscriptions(uint8_t *apdu, int apdu_size)
{
if (apdu) {
uint8_t cov_sub[MAX_COV_SUB_SIZE] = {
0,
};
unsigned index = 0;
int apdu_len = 0;
BACNET_COV_SUBSCRIPTION *subscription = NULL;
BACNET_COV_SUBSCRIPTION data = { 0 };
BACNET_ADDRESS *dest = NULL;
int len = 0;
int apdu_len = 0;
unsigned index = 0;
BACNET_COV_HANDLER_SUBSCRIPTION *subscription = NULL;
for (index = 0; index < Keylist_Count(COV_Subscriptions); index++) {
subscription = Keylist_Data_Index(COV_Subscriptions, index);
if (subscription) {
/* Lets encode a COV subscription into an intermediate buffer
* that can hold it */
int len = cov_encode_subscription(
&cov_sub[0], max_apdu - apdu_len, subscription);
if ((apdu_len + len) > max_apdu) {
return -2;
}
/* Lets copy if and only if it fits in the buffer */
memcpy(&apdu[apdu_len], cov_sub, len);
apdu_len += len;
}
for (index = 0; index < Keylist_Count(COV_Subscriptions); index++) {
subscription = Keylist_Data_Index(COV_Subscriptions, index);
if (!subscription) {
continue;
}
dest = cov_address_get(subscription->dest_index);
if (!dest) {
continue;
}
data.time_remaining = subscription->lifetime;
data.cov_increment_present = false;
data.cov_increment = 1.0f; /* dummy value */
data.issue_confirmed_notifications =
subscription->flag.issueConfirmedNotifications;
data.monitored_property_reference.object_identifier.type =
subscription->monitoredObjectIdentifier.type;
data.monitored_property_reference.object_identifier.instance =
subscription->monitoredObjectIdentifier.instance;
data.monitored_property_reference.property_identifier =
PROP_PRESENT_VALUE;
data.monitored_property_reference.property_array_index =
BACNET_ARRAY_ALL;
data.recipient.process_identifier =
subscription->subscriberProcessIdentifier;
data.recipient.recipient.tag = BACNET_RECIPIENT_TAG_ADDRESS;
bacnet_address_copy(&data.recipient.recipient.type.address, dest);
len = bacnet_cov_subscription_encode(apdu, apdu_size - apdu_len, &data);
if (len <= 0) {
return BACNET_STATUS_ABORT;
}
apdu_len += len;
if (apdu) {
apdu += len;
}
return apdu_len;
}
return 0;
return apdu_len;
}
/** Handler to initialize the COV list, clearing and disabling each entry.
@@ -440,7 +342,7 @@ static bool cov_list_subscribe(
bool found = true;
bool address_match = false;
const BACNET_ADDRESS *dest = NULL;
BACNET_COV_SUBSCRIPTION *subscription = NULL;
BACNET_COV_HANDLER_SUBSCRIPTION *subscription = NULL;
/* unable to subscribe - resources? */
/* unable to cancel subscription - other? */
@@ -535,7 +437,7 @@ static bool cov_list_subscribe(
}
static bool cov_send_request(
BACNET_COV_SUBSCRIPTION *cov_subscription,
BACNET_COV_HANDLER_SUBSCRIPTION *cov_subscription,
BACNET_PROPERTY_VALUE *value_list)
{
int len = 0;
@@ -620,7 +522,7 @@ static void cov_lifetime_expiration_handler(
unsigned index, uint32_t elapsed_seconds, uint32_t lifetime_seconds)
{
if (index < MAX_COV_SUBSCRIPTIONS) {
BACNET_COV_SUBSCRIPTION *subscription =
BACNET_COV_HANDLER_SUBSCRIPTION *subscription =
Keylist_Data_Index(COV_Subscriptions, index);
/* handle lifetime expiration */
if (lifetime_seconds >= elapsed_seconds) {
@@ -655,8 +557,8 @@ static void cov_lifetime_expiration_handler(
}
}
/** Handler to check the list of subscribed objects for any that have changed
* and so need to have notifications sent.
/** Handler to check the list of subscribed objects for any that have
* changed and so need to have notifications sent.
* @ingroup DSCOV
* This handler will be invoked by the main program every second or so.
* This example only handles Binary Inputs, but can be easily extended to
@@ -674,13 +576,14 @@ static void cov_lifetime_expiration_handler(
* @note worst case tasking: MS/TP with the ability to send only
* one notification per task cycle.
*
* @param elapsed_seconds [in] How many seconds have elapsed since last called.
* @param elapsed_seconds [in] How many seconds have elapsed since last
* called.
*/
void handler_cov_timer_seconds(uint32_t elapsed_seconds)
{
int index = 0;
uint32_t lifetime_seconds = 0;
BACNET_COV_SUBSCRIPTION *subscription = NULL;
BACNET_COV_HANDLER_SUBSCRIPTION *subscription = NULL;
int list_cnt = 0;
#ifdef BAC_ROUTING
uint16_t current_dev_id = Routed_Device_Object_Index();
@@ -755,7 +658,7 @@ bool handler_cov_fsm(void)
int index = indices[dev_id];
cov_fsm_state_t cov_task_state = cov_task_states[dev_id];
BACNET_COV_SUBSCRIPTION *subscription =
BACNET_COV_HANDLER_SUBSCRIPTION *subscription =
Keylist_Data_Index(COV_Subscriptions, index);
int list_cnt = Keylist_Count(COV_Subscriptions);
+169
View File
@@ -19,8 +19,177 @@ COV Subscribe
COV Subscribe Property
COV Notification
Unconfirmed COV Notification
COV Subscription
*/
/**
* @brief Encode APDU for COV Subscription.
*
* BACnetCOVSubscription ::= SEQUENCE {
* recipient[0] BACnetRecipientProcess,
* monitored-property-reference[1] BACnetObjectPropertyReference,
* issue-confirmed-notifications[2] Boolean,
* time-remaining[3] Unsigned,
* cov-increment[4] Real OPTIONAL-- used only with monitored
* -- properties with a numeric datatype
* }
*
* @param apdu Pointer to the buffer, or NULL for length
* @param data Pointer to the data to encode.
* @return number of bytes encoded, or zero on error.
*/
size_t
cov_subscription_encode(uint8_t *apdu, const BACNET_COV_SUBSCRIPTION *data)
{
int len;
size_t apdu_len = 0; /* total length of the apdu, return value */
if (!data) {
return 0;
}
/* Recipient [0] BACnetRecipientProcess */
len = bacnet_recipient_process_context_encode(apdu, 0, &data->recipient);
apdu_len += len;
if (apdu) {
apdu += len;
}
/* MonitoredPropertyReference [1] BACnetObjectPropertyReference, */
len = bacapp_encode_context_obj_property_ref(
apdu, 1, &data->monitored_property_reference);
apdu_len += len;
if (apdu) {
apdu += len;
}
/* IssueConfirmedNotifications [2] BOOLEAN, */
len = encode_context_boolean(apdu, 2, data->issue_confirmed_notifications);
apdu_len += len;
if (apdu) {
apdu += len;
}
/* TimeRemaining [3] Unsigned, */
len = encode_context_unsigned(apdu, 3, data->time_remaining);
apdu_len += len;
if (apdu) {
apdu += len;
}
/* cov-increment [4] Real OPTIONAL */
if (data->cov_increment_present) {
len = encode_context_real(apdu, 4, data->cov_increment);
apdu_len += len;
}
return apdu_len;
}
/**
* @brief Encode APDU for COV Subscription.
* @param apdu Pointer to the buffer, or NULL for length
* @param apdu_size number of bytes available in the buffer
* @param data Pointer to the data to encode.
* @return number of bytes encoded, or zero on error.
*/
size_t bacnet_cov_subscription_encode(
uint8_t *apdu, size_t apdu_size, const BACNET_COV_SUBSCRIPTION *data)
{
size_t apdu_len = 0; /* total length of the apdu, return value */
apdu_len = cov_subscription_encode(NULL, data);
if (apdu_len > apdu_size) {
apdu_len = 0;
} else {
apdu_len = cov_subscription_encode(apdu, data);
}
return apdu_len;
}
/**
* @brief Decode APDU for COV Subscription.
*
* BACnetCOVSubscription ::= SEQUENCE {
* recipient[0] BACnetRecipientProcess,
* monitored-property-reference[1] BACnetObjectPropertyReference,
* issue-confirmed-notifications[2] Boolean,
* time-remaining[3] Unsigned,
* cov-increment[4] Real OPTIONAL-- used only with monitored
* -- properties with a numeric datatype
* }
*
* @param apdu Pointer to the buffer for decoding.
* @param apdu_size number of bytes available in the buffer
* @param data Pointer to the data to decode into, or NULL for length only.
* @return Bytes decoded or BACNET_STATUS_ERROR on error.
*/
int bacnet_cov_subscription_decode(
const uint8_t *apdu, size_t apdu_size, BACNET_COV_SUBSCRIPTION *data)
{
int len = 0, apdu_len = 0;
BACNET_UNSIGNED_INTEGER unsigned_value = 0;
BACNET_RECIPIENT_PROCESS recipient = { 0 };
BACNET_OBJECT_PROPERTY_REFERENCE monitored_property_reference = { 0 };
bool issue_confirmed_notifications = false;
float float_value = 1.0f;
bool cov_increment_present = false;
if (!apdu || (apdu_size == 0)) {
return 0;
}
/* Recipient [0] BACnetRecipientProcess */
len = bacnet_recipient_process_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 0, &recipient);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
/* MonitoredPropertyReference [1] BACnetObjectPropertyReference */
len = bacapp_decode_context_obj_property_ref(
&apdu[apdu_len], apdu_size - apdu_len, 1,
&monitored_property_reference);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
/* IssueConfirmedNotifications [2] BOOLEAN */
len = bacnet_boolean_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 2,
&issue_confirmed_notifications);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
/* TimeRemaining [3] Unsigned */
len = bacnet_unsigned_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 3, &unsigned_value);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
/* CovIncrement [4] Real OPTIONAL */
len = bacnet_real_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 4, &float_value);
if (len < 0) {
return BACNET_STATUS_ERROR;
} else if (len == 0) {
/* not present - skip */
cov_increment_present = false;
} else {
cov_increment_present = true;
apdu_len += len;
}
/* copy into the data structure */
if (data) {
bacnet_recipient_process_copy(&data->recipient, &recipient);
bacnet_object_property_reference_copy(
&data->monitored_property_reference, &monitored_property_reference);
data->issue_confirmed_notifications = issue_confirmed_notifications;
data->time_remaining = unsigned_value;
data->cov_increment = float_value;
data->cov_increment_present = cov_increment_present;
}
return apdu_len;
}
/**
* @brief Encode APDU for COV Notification.
* @param apdu Pointer to the buffer, or NULL for length
+31
View File
@@ -14,6 +14,8 @@
#include "bacnet/bacdef.h"
/* BACnet Stack API */
#include "bacnet/bacapp.h"
#include "bacnet/bacdest.h"
#include "bacnet/bacdevobjpropref.h"
typedef struct BACnet_COV_Data {
uint32_t subscriberProcessIdentifier;
@@ -40,6 +42,25 @@ typedef struct BACnet_Subscribe_COV_Data {
struct BACnet_Subscribe_COV_Data *next;
} BACNET_SUBSCRIBE_COV_DATA;
/**
* BACnetCOVSubscription ::= SEQUENCE {
* recipient[0] BACnetRecipientProcess,
* monitored-property-reference[1] BACnetObjectPropertyReference,
* issue-confirmed-notifications[2] Boolean,
* time-remaining[3] Unsigned,
* cov-increment[4] Real OPTIONAL-- used only with monitored
* -- properties with a numeric datatype
* }
*/
typedef struct BACnetCOVSubscription {
uint32_t time_remaining; /* seconds */
BACNET_RECIPIENT_PROCESS recipient;
BACNET_OBJECT_PROPERTY_REFERENCE monitored_property_reference;
bool issue_confirmed_notifications;
bool cov_increment_present; /* true if present */
float cov_increment; /* optional */
} BACNET_COV_SUBSCRIPTION;
/* generic callback for COV notifications */
typedef void (*BACnet_COV_Notification_Callback)(BACNET_COV_DATA *cov_data);
struct BACnet_COV_Notification;
@@ -52,6 +73,16 @@ typedef struct BACnet_COV_Notification {
extern "C" {
#endif /* __cplusplus */
BACNET_STACK_EXPORT
size_t
cov_subscription_encode(uint8_t *apdu, const BACNET_COV_SUBSCRIPTION *data);
BACNET_STACK_EXPORT
size_t bacnet_cov_subscription_encode(
uint8_t *apdu, size_t apdu_size, const BACNET_COV_SUBSCRIPTION *data);
BACNET_STACK_EXPORT
int bacnet_cov_subscription_decode(
const uint8_t *apdu, size_t apdu_size, BACNET_COV_SUBSCRIPTION *data);
BACNET_STACK_EXPORT
size_t cov_notify_service_request_encode(
uint8_t *apdu, size_t apdu_size, const BACNET_COV_DATA *data);