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:
@@ -733,6 +733,175 @@ int bacnet_recipient_context_decode(
|
|||||||
return apdu_len;
|
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
|
* Convert BACnet_Destination to ASCII for printing
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -36,6 +36,17 @@ typedef struct BACnet_Recipient {
|
|||||||
} type;
|
} type;
|
||||||
} BACNET_RECIPIENT;
|
} 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;
|
struct BACnet_Recipient_List;
|
||||||
typedef struct BACnet_Recipient_List {
|
typedef struct BACnet_Recipient_List {
|
||||||
BACNET_RECIPIENT recipient;
|
BACNET_RECIPIENT recipient;
|
||||||
@@ -136,6 +147,29 @@ int bacnet_recipient_context_decode(
|
|||||||
uint8_t tag_number,
|
uint8_t tag_number,
|
||||||
BACNET_RECIPIENT *value);
|
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
|
BACNET_STACK_EXPORT
|
||||||
bool bacnet_recipient_address_from_ascii(BACNET_ADDRESS *src, const char *arg);
|
bool bacnet_recipient_address_from_ascii(BACNET_ADDRESS *src, const char *arg);
|
||||||
BACNET_STACK_EXPORT
|
BACNET_STACK_EXPORT
|
||||||
|
|||||||
@@ -2963,8 +2963,8 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
|
|||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case PROP_ACTIVE_COV_SUBSCRIPTIONS:
|
case PROP_ACTIVE_COV_SUBSCRIPTIONS:
|
||||||
if ((apdu_len = handler_cov_encode_subscriptions(
|
apdu_len = handler_cov_encode_subscriptions(&apdu[0], apdu_max);
|
||||||
&apdu[0], apdu_max)) < 0) {
|
if (apdu_len < 0) {
|
||||||
rpdata->error_code =
|
rpdata->error_code =
|
||||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||||
apdu_len = BACNET_STATUS_ABORT;
|
apdu_len = BACNET_STATUS_ABORT;
|
||||||
|
|||||||
@@ -29,6 +29,12 @@
|
|||||||
#include "bacnet/basic/server/bacnet_basic.h"
|
#include "bacnet/basic/server/bacnet_basic.h"
|
||||||
#include "bacnet/basic/server/bacnet_port.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 */
|
/* 1s timer for basic non-critical timed tasks */
|
||||||
static struct mstimer BACnet_Task_Timer;
|
static struct mstimer BACnet_Task_Timer;
|
||||||
/* task timer for object functionality */
|
/* task timer for object functionality */
|
||||||
@@ -277,11 +283,15 @@ void bacnet_basic_task(void)
|
|||||||
BACnet_Uptime_Seconds += elapsed_seconds;
|
BACnet_Uptime_Seconds += elapsed_seconds;
|
||||||
dcc_timer_seconds(elapsed_seconds);
|
dcc_timer_seconds(elapsed_seconds);
|
||||||
datalink_maintenance_timer(elapsed_seconds);
|
datalink_maintenance_timer(elapsed_seconds);
|
||||||
|
#if (BACNET_COV_SUBSCRIPTIONS_SIZE > 0)
|
||||||
handler_cov_timer_seconds(elapsed_seconds);
|
handler_cov_timer_seconds(elapsed_seconds);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
#if (BACNET_COV_SUBSCRIPTIONS_SIZE > 0)
|
||||||
while (!handler_cov_fsm()) {
|
while (!handler_cov_fsm()) {
|
||||||
/* waiting for COV processing to be IDLE */
|
/* waiting for COV processing to be IDLE */
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
/* object specific cyclic tasks */
|
/* object specific cyclic tasks */
|
||||||
if (mstimer_expired(&BACnet_Object_Timer)) {
|
if (mstimer_expired(&BACnet_Object_Timer)) {
|
||||||
elapsed_milliseconds = mstimer_elapsed(&BACnet_Object_Timer);
|
elapsed_milliseconds = mstimer_elapsed(&BACnet_Object_Timer);
|
||||||
|
|||||||
@@ -2826,8 +2826,8 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
|
|||||||
#endif
|
#endif
|
||||||
#if (BACNET_COV_SUBSCRIPTIONS_SIZE > 0)
|
#if (BACNET_COV_SUBSCRIPTIONS_SIZE > 0)
|
||||||
case PROP_ACTIVE_COV_SUBSCRIPTIONS:
|
case PROP_ACTIVE_COV_SUBSCRIPTIONS:
|
||||||
if ((apdu_len = handler_cov_encode_subscriptions(
|
apdu_len = handler_cov_encode_subscriptions(&apdu[0], apdu_max);
|
||||||
&apdu[0], apdu_max)) < 0) {
|
if (apdu_len < 0) {
|
||||||
rpdata->error_code =
|
rpdata->error_code =
|
||||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||||
apdu_len = BACNET_STATUS_ABORT;
|
apdu_len = BACNET_STATUS_ABORT;
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
/* BACnet Stack defines - first */
|
/* BACnet Stack defines - first */
|
||||||
#include "bacnet/bacdef.h"
|
#include "bacnet/bacdef.h"
|
||||||
@@ -37,26 +36,26 @@
|
|||||||
#define MAX_COV_PROPERTIES 2
|
#define MAX_COV_PROPERTIES 2
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct BACnet_COV_Address {
|
typedef struct BACnet_COV_Handler_Address {
|
||||||
bool valid : 1;
|
bool valid : 1;
|
||||||
BACNET_ADDRESS dest;
|
BACNET_ADDRESS dest;
|
||||||
} BACNET_COV_ADDRESS;
|
} BACNET_COV_HANDLER_ADDRESS;
|
||||||
|
|
||||||
/* note: This COV service only monitors the properties
|
/* note: This COV service only monitors the properties
|
||||||
of an object that have been specified in the standard. */
|
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 issueConfirmedNotifications : 1; /* optional */
|
||||||
bool send_requested : 1;
|
bool send_requested : 1;
|
||||||
} BACNET_COV_SUBSCRIPTION_FLAGS;
|
} BACNET_COV_HANDLER_SUBSCRIPTION_FLAGS;
|
||||||
|
|
||||||
typedef struct BACnet_COV_Subscription {
|
typedef struct BACnet_COV_Handler_Subscription {
|
||||||
BACNET_COV_SUBSCRIPTION_FLAGS flag;
|
BACNET_COV_HANDLER_SUBSCRIPTION_FLAGS flag;
|
||||||
unsigned dest_index;
|
unsigned dest_index;
|
||||||
uint8_t invokeID; /* for confirmed COV */
|
uint8_t invokeID; /* for confirmed COV */
|
||||||
uint32_t subscriberProcessIdentifier;
|
uint32_t subscriberProcessIdentifier;
|
||||||
uint32_t lifetime; /* optional */
|
uint32_t lifetime; /* optional */
|
||||||
BACNET_OBJECT_ID monitoredObjectIdentifier;
|
BACNET_OBJECT_ID monitoredObjectIdentifier;
|
||||||
} BACNET_COV_SUBSCRIPTION;
|
} BACNET_COV_HANDLER_SUBSCRIPTION;
|
||||||
|
|
||||||
#ifndef MAX_COV_SUBSCRIPTIONS
|
#ifndef MAX_COV_SUBSCRIPTIONS
|
||||||
#define MAX_COV_SUBSCRIPTIONS 128
|
#define MAX_COV_SUBSCRIPTIONS 128
|
||||||
@@ -71,7 +70,7 @@ static OS_Keylist COV_Subscriptions_List[MAX_NUM_DEVICES];
|
|||||||
#ifndef MAX_COV_ADDRESSES
|
#ifndef MAX_COV_ADDRESSES
|
||||||
#define MAX_COV_ADDRESSES 16
|
#define MAX_COV_ADDRESSES 16
|
||||||
#endif
|
#endif
|
||||||
static BACNET_COV_ADDRESS COV_Addresses[MAX_COV_ADDRESSES];
|
static BACNET_COV_HANDLER_ADDRESS COV_Addresses[MAX_COV_ADDRESSES];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Deletes a subscription
|
* @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)
|
static bool cov_subscription_delete(uint32_t list_idx)
|
||||||
{
|
{
|
||||||
bool status = false;
|
bool status = false;
|
||||||
BACNET_COV_SUBSCRIPTION *subscription = NULL;
|
BACNET_COV_HANDLER_SUBSCRIPTION *subscription = NULL;
|
||||||
|
|
||||||
subscription = Keylist_Data_Delete_By_Index(COV_Subscriptions, list_idx);
|
subscription = Keylist_Data_Delete_By_Index(COV_Subscriptions, list_idx);
|
||||||
if (subscription) {
|
if (subscription) {
|
||||||
@@ -97,16 +96,17 @@ static bool cov_subscription_delete(uint32_t list_idx)
|
|||||||
* @param list_key - keylist key
|
* @param list_key - keylist key
|
||||||
* @return the subscription that was created, or NULL
|
* @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;
|
int index = 0;
|
||||||
if (list_key >= MAX_COV_SUBSCRIPTIONS) {
|
if (list_key >= MAX_COV_SUBSCRIPTIONS) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
subscription = Keylist_Data(COV_Subscriptions, list_key);
|
subscription = Keylist_Data(COV_Subscriptions, list_key);
|
||||||
if (!subscription) {
|
if (!subscription) {
|
||||||
subscription = calloc(1, sizeof(BACNET_COV_SUBSCRIPTION));
|
subscription = calloc(1, sizeof(BACNET_COV_HANDLER_SUBSCRIPTION));
|
||||||
if (subscription) {
|
if (subscription) {
|
||||||
index = Keylist_Data_Add(COV_Subscriptions, list_key, subscription);
|
index = Keylist_Data_Add(COV_Subscriptions, list_key, subscription);
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
@@ -150,7 +150,7 @@ static void cov_address_remove_unused(void)
|
|||||||
unsigned index = 0;
|
unsigned index = 0;
|
||||||
unsigned cov_index = 0;
|
unsigned cov_index = 0;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
BACNET_COV_SUBSCRIPTION *subscription = NULL;
|
BACNET_COV_HANDLER_SUBSCRIPTION *subscription = NULL;
|
||||||
|
|
||||||
#ifdef BAC_ROUTING
|
#ifdef BAC_ROUTING
|
||||||
uint16_t current_dev_id = Routed_Device_Object_Index();
|
uint16_t current_dev_id = Routed_Device_Object_Index();
|
||||||
@@ -243,160 +243,62 @@ static int cov_address_add(const BACNET_ADDRESS *dest)
|
|||||||
return index;
|
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.
|
/** Handle a request to list all the COV subscriptions.
|
||||||
* @ingroup DSCOV
|
* @ingroup DSCOV
|
||||||
* Invoked by a request to read the Device object's
|
* Invoked by a request to read the Device object's
|
||||||
* PROP_ACTIVE_COV_SUBSCRIPTIONS. Loops through the list of COV Subscriptions,
|
* PROP_ACTIVE_COV_SUBSCRIPTIONS. Loops through the list of COV Subscriptions,
|
||||||
* and, for each valid one, adds its description to the APDU.
|
* and, for each valid one, adds its description to the APDU.
|
||||||
* @param apdu [out] Buffer in which the APDU contents are built.
|
* @param apdu [out] Buffer in which the APDU contents are built.
|
||||||
* @param max_apdu [in] Max length of the APDU buffer.
|
* @param apdu_size [in] Max length of the APDU buffer.
|
||||||
* @return How many bytes were encoded in the buffer, or -2 if the response
|
* @return Number of bytes encoded in the buffer, or
|
||||||
* would not fit within the buffer.
|
* #BACNET_STATUS_ABORT if the response would not fit within the buffer.
|
||||||
*/
|
*/
|
||||||
/* Maximume length for an encoded COV subscription - 31 bytes for BACNET IP6
|
int handler_cov_encode_subscriptions(uint8_t *apdu, int apdu_size)
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
if (apdu) {
|
BACNET_COV_SUBSCRIPTION data = { 0 };
|
||||||
uint8_t cov_sub[MAX_COV_SUB_SIZE] = {
|
BACNET_ADDRESS *dest = NULL;
|
||||||
0,
|
int len = 0;
|
||||||
};
|
int apdu_len = 0;
|
||||||
unsigned index = 0;
|
unsigned index = 0;
|
||||||
int apdu_len = 0;
|
BACNET_COV_HANDLER_SUBSCRIPTION *subscription = NULL;
|
||||||
BACNET_COV_SUBSCRIPTION *subscription = NULL;
|
|
||||||
|
|
||||||
for (index = 0; index < Keylist_Count(COV_Subscriptions); index++) {
|
for (index = 0; index < Keylist_Count(COV_Subscriptions); index++) {
|
||||||
subscription = Keylist_Data_Index(COV_Subscriptions, index);
|
subscription = Keylist_Data_Index(COV_Subscriptions, index);
|
||||||
if (subscription) {
|
if (!subscription) {
|
||||||
/* Lets encode a COV subscription into an intermediate buffer
|
continue;
|
||||||
* that can hold it */
|
}
|
||||||
int len = cov_encode_subscription(
|
dest = cov_address_get(subscription->dest_index);
|
||||||
&cov_sub[0], max_apdu - apdu_len, subscription);
|
if (!dest) {
|
||||||
|
continue;
|
||||||
if ((apdu_len + len) > max_apdu) {
|
}
|
||||||
return -2;
|
data.time_remaining = subscription->lifetime;
|
||||||
}
|
data.cov_increment_present = false;
|
||||||
|
data.cov_increment = 1.0f; /* dummy value */
|
||||||
/* Lets copy if and only if it fits in the buffer */
|
data.issue_confirmed_notifications =
|
||||||
memcpy(&apdu[apdu_len], cov_sub, len);
|
subscription->flag.issueConfirmedNotifications;
|
||||||
apdu_len += len;
|
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.
|
/** Handler to initialize the COV list, clearing and disabling each entry.
|
||||||
@@ -440,7 +342,7 @@ static bool cov_list_subscribe(
|
|||||||
bool found = true;
|
bool found = true;
|
||||||
bool address_match = false;
|
bool address_match = false;
|
||||||
const BACNET_ADDRESS *dest = NULL;
|
const BACNET_ADDRESS *dest = NULL;
|
||||||
BACNET_COV_SUBSCRIPTION *subscription = NULL;
|
BACNET_COV_HANDLER_SUBSCRIPTION *subscription = NULL;
|
||||||
|
|
||||||
/* unable to subscribe - resources? */
|
/* unable to subscribe - resources? */
|
||||||
/* unable to cancel subscription - other? */
|
/* unable to cancel subscription - other? */
|
||||||
@@ -535,7 +437,7 @@ static bool cov_list_subscribe(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool cov_send_request(
|
static bool cov_send_request(
|
||||||
BACNET_COV_SUBSCRIPTION *cov_subscription,
|
BACNET_COV_HANDLER_SUBSCRIPTION *cov_subscription,
|
||||||
BACNET_PROPERTY_VALUE *value_list)
|
BACNET_PROPERTY_VALUE *value_list)
|
||||||
{
|
{
|
||||||
int len = 0;
|
int len = 0;
|
||||||
@@ -620,7 +522,7 @@ static void cov_lifetime_expiration_handler(
|
|||||||
unsigned index, uint32_t elapsed_seconds, uint32_t lifetime_seconds)
|
unsigned index, uint32_t elapsed_seconds, uint32_t lifetime_seconds)
|
||||||
{
|
{
|
||||||
if (index < MAX_COV_SUBSCRIPTIONS) {
|
if (index < MAX_COV_SUBSCRIPTIONS) {
|
||||||
BACNET_COV_SUBSCRIPTION *subscription =
|
BACNET_COV_HANDLER_SUBSCRIPTION *subscription =
|
||||||
Keylist_Data_Index(COV_Subscriptions, index);
|
Keylist_Data_Index(COV_Subscriptions, index);
|
||||||
/* handle lifetime expiration */
|
/* handle lifetime expiration */
|
||||||
if (lifetime_seconds >= elapsed_seconds) {
|
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
|
/** Handler to check the list of subscribed objects for any that have
|
||||||
* and so need to have notifications sent.
|
* changed and so need to have notifications sent.
|
||||||
* @ingroup DSCOV
|
* @ingroup DSCOV
|
||||||
* This handler will be invoked by the main program every second or so.
|
* 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
|
* 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
|
* @note worst case tasking: MS/TP with the ability to send only
|
||||||
* one notification per task cycle.
|
* 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)
|
void handler_cov_timer_seconds(uint32_t elapsed_seconds)
|
||||||
{
|
{
|
||||||
int index = 0;
|
int index = 0;
|
||||||
uint32_t lifetime_seconds = 0;
|
uint32_t lifetime_seconds = 0;
|
||||||
BACNET_COV_SUBSCRIPTION *subscription = NULL;
|
BACNET_COV_HANDLER_SUBSCRIPTION *subscription = NULL;
|
||||||
int list_cnt = 0;
|
int list_cnt = 0;
|
||||||
#ifdef BAC_ROUTING
|
#ifdef BAC_ROUTING
|
||||||
uint16_t current_dev_id = Routed_Device_Object_Index();
|
uint16_t current_dev_id = Routed_Device_Object_Index();
|
||||||
@@ -755,7 +658,7 @@ bool handler_cov_fsm(void)
|
|||||||
|
|
||||||
int index = indices[dev_id];
|
int index = indices[dev_id];
|
||||||
cov_fsm_state_t cov_task_state = cov_task_states[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);
|
Keylist_Data_Index(COV_Subscriptions, index);
|
||||||
int list_cnt = Keylist_Count(COV_Subscriptions);
|
int list_cnt = Keylist_Count(COV_Subscriptions);
|
||||||
|
|
||||||
|
|||||||
@@ -19,8 +19,177 @@ COV Subscribe
|
|||||||
COV Subscribe Property
|
COV Subscribe Property
|
||||||
COV Notification
|
COV Notification
|
||||||
Unconfirmed 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.
|
* @brief Encode APDU for COV Notification.
|
||||||
* @param apdu Pointer to the buffer, or NULL for length
|
* @param apdu Pointer to the buffer, or NULL for length
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
#include "bacnet/bacdef.h"
|
#include "bacnet/bacdef.h"
|
||||||
/* BACnet Stack API */
|
/* BACnet Stack API */
|
||||||
#include "bacnet/bacapp.h"
|
#include "bacnet/bacapp.h"
|
||||||
|
#include "bacnet/bacdest.h"
|
||||||
|
#include "bacnet/bacdevobjpropref.h"
|
||||||
|
|
||||||
typedef struct BACnet_COV_Data {
|
typedef struct BACnet_COV_Data {
|
||||||
uint32_t subscriberProcessIdentifier;
|
uint32_t subscriberProcessIdentifier;
|
||||||
@@ -40,6 +42,25 @@ typedef struct BACnet_Subscribe_COV_Data {
|
|||||||
struct BACnet_Subscribe_COV_Data *next;
|
struct BACnet_Subscribe_COV_Data *next;
|
||||||
} BACNET_SUBSCRIBE_COV_DATA;
|
} 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 */
|
/* generic callback for COV notifications */
|
||||||
typedef void (*BACnet_COV_Notification_Callback)(BACNET_COV_DATA *cov_data);
|
typedef void (*BACnet_COV_Notification_Callback)(BACNET_COV_DATA *cov_data);
|
||||||
struct BACnet_COV_Notification;
|
struct BACnet_COV_Notification;
|
||||||
@@ -52,6 +73,16 @@ typedef struct BACnet_COV_Notification {
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif /* __cplusplus */
|
#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
|
BACNET_STACK_EXPORT
|
||||||
size_t cov_notify_service_request_encode(
|
size_t cov_notify_service_request_encode(
|
||||||
uint8_t *apdu, size_t apdu_size, const BACNET_COV_DATA *data);
|
uint8_t *apdu, size_t apdu_size, const BACNET_COV_DATA *data);
|
||||||
|
|||||||
@@ -193,6 +193,8 @@ list(APPEND testdirs
|
|||||||
bacnet/basic/object/trendlog
|
bacnet/basic/object/trendlog
|
||||||
# basic/program
|
# basic/program
|
||||||
bacnet/basic/program/ubasic
|
bacnet/basic/program/ubasic
|
||||||
|
# basic/service
|
||||||
|
bacnet/basic/service/h_cov
|
||||||
# basic/server
|
# basic/server
|
||||||
bacnet/basic/server/bacnet_device
|
bacnet/basic/server/bacnet_device
|
||||||
# basic/sys
|
# basic/sys
|
||||||
|
|||||||
@@ -245,6 +245,89 @@ static void test_BACnetRecipient_ASCII(void)
|
|||||||
zassert_true(status, NULL);
|
zassert_true(status, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test BACnetRecipientProcess encode, decode, copy functions
|
||||||
|
*/
|
||||||
|
#if defined(CONFIG_ZTEST_NEW_API)
|
||||||
|
ZTEST(bacnet_destination_tests, testBACnetRecipientProcess)
|
||||||
|
#else
|
||||||
|
static void testBACnetRecipientProcess(void)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
uint8_t apdu[MAX_APDU] = { 0 };
|
||||||
|
BACNET_RECIPIENT_PROCESS value = { 0 }, test_value = { 0 };
|
||||||
|
BACNET_MAC_ADDRESS mac = { .len = 1, .adr[0] = 0x01 };
|
||||||
|
BACNET_MAC_ADDRESS adr = { .len = 1, .adr[0] = 0x02 };
|
||||||
|
uint16_t snet = 4321;
|
||||||
|
BACNET_ADDRESS address = { 0 };
|
||||||
|
int apdu_len = 0, null_len = 0, test_len = 0;
|
||||||
|
uint8_t tag_number = 3;
|
||||||
|
bool status = false;
|
||||||
|
|
||||||
|
/* device recipient */
|
||||||
|
bacnet_recipient_device_set(&value.recipient, OBJECT_DEVICE, 5678);
|
||||||
|
value.process_identifier = 99;
|
||||||
|
null_len = bacnet_recipient_process_encode(NULL, &value);
|
||||||
|
apdu_len = bacnet_recipient_process_encode(apdu, &value);
|
||||||
|
zassert_equal(apdu_len, null_len, NULL);
|
||||||
|
test_len = bacnet_recipient_process_decode(apdu, apdu_len, &test_value);
|
||||||
|
zassert_equal(apdu_len, test_len, NULL);
|
||||||
|
zassert_equal(
|
||||||
|
value.process_identifier, test_value.process_identifier, NULL);
|
||||||
|
status = bacnet_recipient_same(&value.recipient, &test_value.recipient);
|
||||||
|
zassert_true(status, NULL);
|
||||||
|
/* NULL decode target - length-only */
|
||||||
|
test_len = bacnet_recipient_process_decode(apdu, apdu_len, NULL);
|
||||||
|
zassert_equal(apdu_len, test_len, NULL);
|
||||||
|
|
||||||
|
/* copy */
|
||||||
|
memset(&test_value, 0, sizeof(test_value));
|
||||||
|
bacnet_recipient_process_copy(&test_value, &value);
|
||||||
|
zassert_equal(
|
||||||
|
value.process_identifier, test_value.process_identifier, NULL);
|
||||||
|
status = bacnet_recipient_same(&value.recipient, &test_value.recipient);
|
||||||
|
zassert_true(status, NULL);
|
||||||
|
|
||||||
|
/* address recipient */
|
||||||
|
status = bacnet_address_init(&address, &mac, snet, &adr);
|
||||||
|
zassert_true(status, NULL);
|
||||||
|
bacnet_recipient_address_set(&value.recipient, &address);
|
||||||
|
value.process_identifier = 42;
|
||||||
|
null_len = bacnet_recipient_process_encode(NULL, &value);
|
||||||
|
apdu_len = bacnet_recipient_process_encode(apdu, &value);
|
||||||
|
zassert_equal(apdu_len, null_len, NULL);
|
||||||
|
test_len = bacnet_recipient_process_decode(apdu, apdu_len, &test_value);
|
||||||
|
zassert_equal(apdu_len, test_len, NULL);
|
||||||
|
zassert_equal(
|
||||||
|
value.process_identifier, test_value.process_identifier, NULL);
|
||||||
|
status = bacnet_recipient_same(&value.recipient, &test_value.recipient);
|
||||||
|
zassert_true(status, NULL);
|
||||||
|
|
||||||
|
/* context encode/decode */
|
||||||
|
bacnet_recipient_device_set(&value.recipient, OBJECT_DEVICE, 1234);
|
||||||
|
value.process_identifier = 7;
|
||||||
|
null_len =
|
||||||
|
bacnet_recipient_process_context_encode(NULL, tag_number, &value);
|
||||||
|
apdu_len =
|
||||||
|
bacnet_recipient_process_context_encode(apdu, tag_number, &value);
|
||||||
|
zassert_equal(apdu_len, null_len, NULL);
|
||||||
|
test_len = bacnet_recipient_process_context_decode(
|
||||||
|
apdu, apdu_len, tag_number, &test_value);
|
||||||
|
zassert_equal(apdu_len, test_len, NULL);
|
||||||
|
zassert_equal(
|
||||||
|
value.process_identifier, test_value.process_identifier, NULL);
|
||||||
|
status = bacnet_recipient_same(&value.recipient, &test_value.recipient);
|
||||||
|
zassert_true(status, NULL);
|
||||||
|
/* wrong tag - expect zero (tag mismatch) */
|
||||||
|
test_len = bacnet_recipient_process_context_decode(
|
||||||
|
apdu, apdu_len, tag_number + 1, &test_value);
|
||||||
|
zassert_equal(test_len, 0, NULL);
|
||||||
|
|
||||||
|
/* negative: NULL apdu */
|
||||||
|
test_len = bacnet_recipient_process_decode(NULL, apdu_len, &test_value);
|
||||||
|
zassert_equal(test_len, BACNET_STATUS_ERROR, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_ZTEST_NEW_API)
|
#if defined(CONFIG_ZTEST_NEW_API)
|
||||||
ZTEST_SUITE(bacnet_destination_tests, NULL, NULL, NULL, NULL, NULL);
|
ZTEST_SUITE(bacnet_destination_tests, NULL, NULL, NULL, NULL, NULL);
|
||||||
#else
|
#else
|
||||||
@@ -254,7 +337,8 @@ void test_main(void)
|
|||||||
bacnet_destination_tests, ztest_unit_test(testBACnetDestination),
|
bacnet_destination_tests, ztest_unit_test(testBACnetDestination),
|
||||||
ztest_unit_test(test_BACnetDestination_ASCII),
|
ztest_unit_test(test_BACnetDestination_ASCII),
|
||||||
ztest_unit_test(testBACnetRecipient),
|
ztest_unit_test(testBACnetRecipient),
|
||||||
ztest_unit_test(test_BACnetRecipient_ASCII));
|
ztest_unit_test(test_BACnetRecipient_ASCII),
|
||||||
|
ztest_unit_test(testBACnetRecipientProcess));
|
||||||
|
|
||||||
ztest_run_test_suite(bacnet_destination_tests);
|
ztest_run_test_suite(bacnet_destination_tests);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,16 @@
|
|||||||
|
|
||||||
void bip_get_my_address(BACNET_ADDRESS *my_address)
|
void bip_get_my_address(BACNET_ADDRESS *my_address)
|
||||||
{
|
{
|
||||||
(void)my_address;
|
if (my_address) {
|
||||||
|
my_address->mac_len = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int bip_send_pdu(
|
int bip_send_pdu(
|
||||||
BACNET_ADDRESS *dest,
|
BACNET_ADDRESS *dest,
|
||||||
BACNET_NPDU_DATA *npdu_data,
|
BACNET_NPDU_DATA *npdu_data,
|
||||||
uint8_t *pdu,
|
uint8_t *pdu,
|
||||||
unsigned pdu_len)
|
uint16_t pdu_len)
|
||||||
{
|
{
|
||||||
(void)dest;
|
(void)dest;
|
||||||
(void)npdu_data;
|
(void)npdu_data;
|
||||||
|
|||||||
@@ -46,3 +46,57 @@ int Device_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
|||||||
(void)rpdata;
|
(void)rpdata;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Device_Valid_Object_Id(
|
||||||
|
BACNET_OBJECT_TYPE object_type, uint32_t object_instance)
|
||||||
|
{
|
||||||
|
(void)object_type;
|
||||||
|
(void)object_instance;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Device_Value_List_Supported(BACNET_OBJECT_TYPE object_type)
|
||||||
|
{
|
||||||
|
(void)object_type;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Device_COV(BACNET_OBJECT_TYPE object_type, uint32_t object_instance)
|
||||||
|
{
|
||||||
|
(void)object_type;
|
||||||
|
(void)object_instance;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device_COV_Clear(BACNET_OBJECT_TYPE object_type, uint32_t object_instance)
|
||||||
|
{
|
||||||
|
(void)object_type;
|
||||||
|
(void)object_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Device_Encode_Value_List(
|
||||||
|
BACNET_OBJECT_TYPE object_type,
|
||||||
|
uint32_t object_instance,
|
||||||
|
BACNET_PROPERTY_VALUE *value_list)
|
||||||
|
{
|
||||||
|
(void)object_type;
|
||||||
|
(void)object_instance;
|
||||||
|
(void)value_list;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t Routed_Device_Object_Index(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Set_Routed_Device_Object_Index(uint16_t index)
|
||||||
|
{
|
||||||
|
(void)index;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t Get_Num_Managed_Devices(void)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
|
||||||
|
|
||||||
|
get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME)
|
||||||
|
project(test_${basename}
|
||||||
|
VERSION 1.0.0
|
||||||
|
LANGUAGES C)
|
||||||
|
|
||||||
|
string(REGEX REPLACE
|
||||||
|
"/test/bacnet/[a-zA-Z0-9_/-]*$"
|
||||||
|
"/src"
|
||||||
|
SRC_DIR
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
string(REGEX REPLACE
|
||||||
|
"/test/bacnet/[a-zA-Z0-9_/-]*$"
|
||||||
|
"/test"
|
||||||
|
TST_DIR
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
set(ZTST_DIR "${TST_DIR}/ztest/src")
|
||||||
|
|
||||||
|
add_compile_definitions(
|
||||||
|
BACNET_BIG_ENDIAN=0
|
||||||
|
CONFIG_ZTEST=1
|
||||||
|
BACAPP_ALL=1
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${SRC_DIR}
|
||||||
|
${TST_DIR}/ztest/include
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME}
|
||||||
|
# File(s) under test
|
||||||
|
${SRC_DIR}/bacnet/basic/service/h_cov.c
|
||||||
|
# Support files and stubs (pathname alphabetical)
|
||||||
|
${SRC_DIR}/bacnet/access_rule.c
|
||||||
|
${SRC_DIR}/bacnet/bacaction.c
|
||||||
|
${SRC_DIR}/bacnet/bacaddr.c
|
||||||
|
${SRC_DIR}/bacnet/bacapp.c
|
||||||
|
${SRC_DIR}/bacnet/bacdcode.c
|
||||||
|
${SRC_DIR}/bacnet/bacdest.c
|
||||||
|
${SRC_DIR}/bacnet/bacdevobjpropref.c
|
||||||
|
${SRC_DIR}/bacnet/bacint.c
|
||||||
|
${SRC_DIR}/bacnet/baclog.c
|
||||||
|
${SRC_DIR}/bacnet/bacreal.c
|
||||||
|
${SRC_DIR}/bacnet/bacstr.c
|
||||||
|
${SRC_DIR}/bacnet/bactext.c
|
||||||
|
${SRC_DIR}/bacnet/bacerror.c
|
||||||
|
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||||
|
${SRC_DIR}/bacnet/basic/sys/debug.c
|
||||||
|
${SRC_DIR}/bacnet/basic/tsm/tsm.c
|
||||||
|
${SRC_DIR}/bacnet/bactimevalue.c
|
||||||
|
${SRC_DIR}/bacnet/calendar_entry.c
|
||||||
|
${SRC_DIR}/bacnet/channel_value.c
|
||||||
|
${SRC_DIR}/bacnet/cov.c
|
||||||
|
${SRC_DIR}/bacnet/dailyschedule.c
|
||||||
|
${SRC_DIR}/bacnet/datetime.c
|
||||||
|
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||||
|
${SRC_DIR}/bacnet/basic/sys/keylist.c
|
||||||
|
${SRC_DIR}/bacnet/hostnport.c
|
||||||
|
${SRC_DIR}/bacnet/indtext.c
|
||||||
|
${SRC_DIR}/bacnet/lighting.c
|
||||||
|
${SRC_DIR}/bacnet/memcopy.c
|
||||||
|
${SRC_DIR}/bacnet/secure_connect.c
|
||||||
|
${SRC_DIR}/bacnet/shed_level.c
|
||||||
|
${SRC_DIR}/bacnet/special_event.c
|
||||||
|
${SRC_DIR}/bacnet/timer_value.c
|
||||||
|
${SRC_DIR}/bacnet/timestamp.c
|
||||||
|
${SRC_DIR}/bacnet/weeklyschedule.c
|
||||||
|
${SRC_DIR}/bacnet/abort.c
|
||||||
|
${SRC_DIR}/bacnet/dcc.c
|
||||||
|
${SRC_DIR}/bacnet/npdu.c
|
||||||
|
${SRC_DIR}/bacnet/reject.c
|
||||||
|
# Test and test library files
|
||||||
|
./src/main.c
|
||||||
|
${TST_DIR}/bacnet/basic/object/test/apdu_mock.c
|
||||||
|
${TST_DIR}/bacnet/basic/object/test/bip_mock.c
|
||||||
|
${TST_DIR}/bacnet/basic/object/test/device_mock.c
|
||||||
|
${ZTST_DIR}/ztest_mock.c
|
||||||
|
${ZTST_DIR}/ztest.c
|
||||||
|
)
|
||||||
@@ -0,0 +1,348 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief test BACnet COV service handler
|
||||||
|
* @date 2025
|
||||||
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
#include <zephyr/ztest.h>
|
||||||
|
#include <bacnet/bacdef.h>
|
||||||
|
#include <bacnet/cov.h>
|
||||||
|
#include <bacnet/basic/services.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup bacnet_tests
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Mock functions for Device_ */
|
||||||
|
#include <bacnet/basic/object/device.h>
|
||||||
|
|
||||||
|
/* Mocks have been moved to bacnet/basic/object/test/ */
|
||||||
|
/**
|
||||||
|
* @brief Test test_h_cov_init_encode
|
||||||
|
*/
|
||||||
|
#if defined(CONFIG_ZTEST_NEW_API)
|
||||||
|
ZTEST(h_cov_tests, test_h_cov_init_encode)
|
||||||
|
#else
|
||||||
|
static void test_h_cov_init_encode(void)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
uint8_t apdu[480] = { 0 };
|
||||||
|
int apdu_size = sizeof(apdu);
|
||||||
|
int len;
|
||||||
|
uint8_t service_request[128] = { 0 };
|
||||||
|
BACNET_ADDRESS src = { 0 };
|
||||||
|
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
||||||
|
BACNET_SUBSCRIBE_COV_DATA cov_data = { 0 };
|
||||||
|
|
||||||
|
handler_cov_init();
|
||||||
|
|
||||||
|
len = handler_cov_encode_subscriptions(apdu, apdu_size);
|
||||||
|
/* Should be zero because we initialized the handler */
|
||||||
|
zassert_equal(len, 0, NULL);
|
||||||
|
|
||||||
|
/* Create a valid subscription */
|
||||||
|
cov_data.subscriberProcessIdentifier = 1;
|
||||||
|
cov_data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT;
|
||||||
|
cov_data.monitoredObjectIdentifier.instance = 0;
|
||||||
|
cov_data.cancellationRequest = false;
|
||||||
|
cov_data.issueConfirmedNotifications = false;
|
||||||
|
cov_data.lifetime = 0;
|
||||||
|
len = cov_subscribe_service_request_encode(
|
||||||
|
service_request, sizeof(service_request), &cov_data);
|
||||||
|
zassert_not_equal(len, 0, NULL);
|
||||||
|
zassert_not_equal(len, BACNET_STATUS_ABORT, NULL);
|
||||||
|
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||||
|
|
||||||
|
/* Normal encode */
|
||||||
|
len = handler_cov_encode_subscriptions(apdu, apdu_size);
|
||||||
|
zassert_not_equal(len, 0, NULL);
|
||||||
|
zassert_not_equal(len, BACNET_STATUS_ABORT, NULL);
|
||||||
|
|
||||||
|
/* edge case: exact size buffer encode */
|
||||||
|
len = handler_cov_encode_subscriptions(apdu, len);
|
||||||
|
zassert_equal(len, len, NULL);
|
||||||
|
|
||||||
|
/* edge case: buffer too small */
|
||||||
|
len = handler_cov_encode_subscriptions(apdu, len - 1);
|
||||||
|
zassert_equal(len, BACNET_STATUS_ABORT, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test test_h_cov_timer
|
||||||
|
*/
|
||||||
|
#if defined(CONFIG_ZTEST_NEW_API)
|
||||||
|
ZTEST(h_cov_tests, test_h_cov_timer)
|
||||||
|
#else
|
||||||
|
static void test_h_cov_timer(void)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
handler_cov_timer_seconds(1);
|
||||||
|
zassert_true(true, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test test_h_cov_fsm
|
||||||
|
*/
|
||||||
|
#if defined(CONFIG_ZTEST_NEW_API)
|
||||||
|
ZTEST(h_cov_tests, test_h_cov_fsm)
|
||||||
|
#else
|
||||||
|
static void test_h_cov_fsm(void)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
bool idle;
|
||||||
|
uint8_t service_request[128] = { 0 };
|
||||||
|
BACNET_ADDRESS src = { 0 };
|
||||||
|
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
||||||
|
BACNET_SUBSCRIBE_COV_DATA cov_data = { 0 };
|
||||||
|
int len;
|
||||||
|
|
||||||
|
handler_cov_init();
|
||||||
|
|
||||||
|
idle = handler_cov_fsm();
|
||||||
|
zassert_true(idle, NULL); /* No subscriptions, quickly back to idle */
|
||||||
|
|
||||||
|
/* Subscribe Unconfirmed */
|
||||||
|
cov_data.subscriberProcessIdentifier = 1;
|
||||||
|
cov_data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT;
|
||||||
|
cov_data.monitoredObjectIdentifier.instance = 0;
|
||||||
|
cov_data.cancellationRequest = false;
|
||||||
|
cov_data.issueConfirmedNotifications = false;
|
||||||
|
cov_data.lifetime = 0;
|
||||||
|
len = cov_subscribe_service_request_encode(
|
||||||
|
service_request, sizeof(service_request), &cov_data);
|
||||||
|
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||||
|
|
||||||
|
/* IDLE -> MARK */
|
||||||
|
idle = handler_cov_fsm();
|
||||||
|
zassert_false(idle, NULL);
|
||||||
|
/* MARK -> CLEAR */
|
||||||
|
idle = handler_cov_fsm();
|
||||||
|
zassert_false(idle, NULL);
|
||||||
|
/* CLEAR -> FREE */
|
||||||
|
idle = handler_cov_fsm();
|
||||||
|
zassert_false(idle, NULL);
|
||||||
|
/* FREE -> SEND */
|
||||||
|
idle = handler_cov_fsm();
|
||||||
|
zassert_false(idle, NULL);
|
||||||
|
/* SEND -> IDLE */
|
||||||
|
idle = handler_cov_fsm();
|
||||||
|
zassert_true(idle, NULL);
|
||||||
|
|
||||||
|
/* Subscribe Confirmed */
|
||||||
|
cov_data.subscriberProcessIdentifier = 2;
|
||||||
|
cov_data.issueConfirmedNotifications = true;
|
||||||
|
len = cov_subscribe_service_request_encode(
|
||||||
|
service_request, sizeof(service_request), &cov_data);
|
||||||
|
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||||
|
|
||||||
|
/* Run FSM again */
|
||||||
|
handler_cov_fsm(); /* IDLE -> MARK */
|
||||||
|
handler_cov_fsm(); /* MARK -> CLEAR */
|
||||||
|
handler_cov_fsm(); /* CLEAR -> FREE */
|
||||||
|
handler_cov_fsm(); /* FREE -> SEND */
|
||||||
|
handler_cov_fsm(); /* SEND -> IDLE */
|
||||||
|
|
||||||
|
/* Trigger Confirmed free branch by expiring */
|
||||||
|
cov_data.lifetime = 10;
|
||||||
|
len = cov_subscribe_service_request_encode(
|
||||||
|
service_request, sizeof(service_request), &cov_data);
|
||||||
|
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||||
|
|
||||||
|
handler_cov_fsm(); /* IDLE -> MARK */
|
||||||
|
handler_cov_fsm(); /* MARK -> CLEAR */
|
||||||
|
handler_cov_fsm(); /* CLEAR -> FREE */
|
||||||
|
handler_cov_fsm(); /* FREE -> SEND */
|
||||||
|
handler_cov_fsm(); /* SEND -> IDLE */
|
||||||
|
|
||||||
|
/* Expire */
|
||||||
|
handler_cov_timer_seconds(15);
|
||||||
|
|
||||||
|
handler_cov_task();
|
||||||
|
zassert_true(true, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test test_h_cov_subscribe_error_cases
|
||||||
|
*/
|
||||||
|
#if defined(CONFIG_ZTEST_NEW_API)
|
||||||
|
ZTEST(h_cov_tests, test_h_cov_subscribe_error_cases)
|
||||||
|
#else
|
||||||
|
static void test_h_cov_subscribe_error_cases(void)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
uint8_t service_request[128] = { 0 };
|
||||||
|
BACNET_ADDRESS src = { 0 };
|
||||||
|
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
||||||
|
BACNET_SUBSCRIBE_COV_DATA cov_data = { 0 };
|
||||||
|
int len;
|
||||||
|
|
||||||
|
handler_cov_init();
|
||||||
|
|
||||||
|
/* test segment abort */
|
||||||
|
service_data.segmented_message = true;
|
||||||
|
handler_cov_subscribe(service_request, 10, &src, &service_data);
|
||||||
|
zassert_true(true, NULL);
|
||||||
|
|
||||||
|
/* test missing required parameter */
|
||||||
|
service_data.segmented_message = false;
|
||||||
|
handler_cov_subscribe(service_request, 0, &src, &service_data);
|
||||||
|
zassert_true(true, NULL);
|
||||||
|
|
||||||
|
/* test bad request decode */
|
||||||
|
handler_cov_subscribe(service_request, 1, &src, &service_data);
|
||||||
|
zassert_true(true, NULL);
|
||||||
|
|
||||||
|
/* test valid decode but object valid (by mock) */
|
||||||
|
cov_data.subscriberProcessIdentifier = 1;
|
||||||
|
cov_data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT;
|
||||||
|
cov_data.monitoredObjectIdentifier.instance = 12345;
|
||||||
|
cov_data.cancellationRequest = false;
|
||||||
|
cov_data.issueConfirmedNotifications = false;
|
||||||
|
cov_data.lifetime = 0;
|
||||||
|
len = cov_subscribe_service_request_encode(
|
||||||
|
service_request, sizeof(service_request), &cov_data);
|
||||||
|
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||||
|
zassert_true(true, NULL);
|
||||||
|
|
||||||
|
/* test cancellation of non-existing */
|
||||||
|
cov_data.cancellationRequest = true;
|
||||||
|
len = cov_subscribe_service_request_encode(
|
||||||
|
service_request, sizeof(service_request), &cov_data);
|
||||||
|
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||||
|
zassert_true(true, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test test_h_cov_subscribe_out_of_space
|
||||||
|
*/
|
||||||
|
#if defined(CONFIG_ZTEST_NEW_API)
|
||||||
|
ZTEST(h_cov_tests, test_h_cov_subscribe_out_of_space)
|
||||||
|
#else
|
||||||
|
static void test_h_cov_subscribe_out_of_space(void)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
uint8_t service_request[128] = { 0 };
|
||||||
|
BACNET_ADDRESS src = { 0 };
|
||||||
|
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
||||||
|
BACNET_SUBSCRIBE_COV_DATA cov_data = { 0 };
|
||||||
|
int len;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
handler_cov_init();
|
||||||
|
|
||||||
|
cov_data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT;
|
||||||
|
cov_data.monitoredObjectIdentifier.instance = 0;
|
||||||
|
cov_data.cancellationRequest = false;
|
||||||
|
cov_data.issueConfirmedNotifications = false;
|
||||||
|
cov_data.lifetime = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < 130; i++) {
|
||||||
|
cov_data.subscriberProcessIdentifier = i;
|
||||||
|
len = cov_subscribe_service_request_encode(
|
||||||
|
service_request, sizeof(service_request), &cov_data);
|
||||||
|
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||||
|
}
|
||||||
|
zassert_true(true, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test test_h_cov_timer_expiration
|
||||||
|
*/
|
||||||
|
#if defined(CONFIG_ZTEST_NEW_API)
|
||||||
|
ZTEST(h_cov_tests, test_h_cov_timer_expiration)
|
||||||
|
#else
|
||||||
|
static void test_h_cov_timer_expiration(void)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
uint8_t service_request[128] = { 0 };
|
||||||
|
BACNET_ADDRESS src = { 0 };
|
||||||
|
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
||||||
|
BACNET_SUBSCRIBE_COV_DATA cov_data = { 0 };
|
||||||
|
int len;
|
||||||
|
|
||||||
|
handler_cov_init();
|
||||||
|
|
||||||
|
/* Subscribe with lifetime */
|
||||||
|
cov_data.subscriberProcessIdentifier = 1;
|
||||||
|
cov_data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT;
|
||||||
|
cov_data.monitoredObjectIdentifier.instance = 0;
|
||||||
|
cov_data.cancellationRequest = false;
|
||||||
|
cov_data.issueConfirmedNotifications = true;
|
||||||
|
cov_data.lifetime = 10;
|
||||||
|
len = cov_subscribe_service_request_encode(
|
||||||
|
service_request, sizeof(service_request), &cov_data);
|
||||||
|
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||||
|
|
||||||
|
/* Check timer decreasing */
|
||||||
|
handler_cov_timer_seconds(5);
|
||||||
|
handler_cov_timer_seconds(10); /* Should expire here */
|
||||||
|
zassert_true(true, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test test_h_cov_address_management
|
||||||
|
*/
|
||||||
|
#if defined(CONFIG_ZTEST_NEW_API)
|
||||||
|
ZTEST(h_cov_tests, test_h_cov_address_management)
|
||||||
|
#else
|
||||||
|
static void test_h_cov_address_management(void)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
uint8_t service_request[128] = { 0 };
|
||||||
|
BACNET_ADDRESS src = { 0 };
|
||||||
|
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
||||||
|
BACNET_SUBSCRIBE_COV_DATA cov_data = { 0 };
|
||||||
|
int len;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
handler_cov_init();
|
||||||
|
|
||||||
|
cov_data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT;
|
||||||
|
cov_data.monitoredObjectIdentifier.instance = 0;
|
||||||
|
cov_data.cancellationRequest = false;
|
||||||
|
cov_data.issueConfirmedNotifications = false;
|
||||||
|
cov_data.lifetime = 0;
|
||||||
|
|
||||||
|
/* Add subscriptions from 18 different sources */
|
||||||
|
for (i = 0; i < 18; i++) {
|
||||||
|
src.mac_len = 1;
|
||||||
|
src.mac[0] = i + 1;
|
||||||
|
cov_data.subscriberProcessIdentifier = i;
|
||||||
|
len = cov_subscribe_service_request_encode(
|
||||||
|
service_request, sizeof(service_request), &cov_data);
|
||||||
|
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cancel one, to free an address */
|
||||||
|
src.mac_len = 1;
|
||||||
|
src.mac[0] = 1;
|
||||||
|
cov_data.subscriberProcessIdentifier = 0;
|
||||||
|
cov_data.cancellationRequest = true;
|
||||||
|
len = cov_subscribe_service_request_encode(
|
||||||
|
service_request, sizeof(service_request), &cov_data);
|
||||||
|
handler_cov_subscribe(service_request, len, &src, &service_data);
|
||||||
|
|
||||||
|
zassert_true(true, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(CONFIG_ZTEST_NEW_API)
|
||||||
|
ZTEST_SUITE(h_cov_tests, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
#else
|
||||||
|
void test_main(void)
|
||||||
|
{
|
||||||
|
ztest_test_suite(
|
||||||
|
h_cov_tests, ztest_unit_test(test_h_cov_init_encode),
|
||||||
|
ztest_unit_test(test_h_cov_timer), ztest_unit_test(test_h_cov_fsm),
|
||||||
|
ztest_unit_test(test_h_cov_subscribe_error_cases),
|
||||||
|
ztest_unit_test(test_h_cov_subscribe_out_of_space),
|
||||||
|
ztest_unit_test(test_h_cov_timer_expiration),
|
||||||
|
ztest_unit_test(test_h_cov_address_management));
|
||||||
|
|
||||||
|
ztest_run_test_suite(h_cov_tests);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -342,6 +342,69 @@ static void testCOVSubscribePropertyData(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void testCOVSubscriptionData(
|
||||||
|
const BACNET_COV_SUBSCRIPTION *data,
|
||||||
|
const BACNET_COV_SUBSCRIPTION *test_data)
|
||||||
|
{
|
||||||
|
bool status = false;
|
||||||
|
|
||||||
|
zassert_equal(
|
||||||
|
test_data->time_remaining, data->time_remaining,
|
||||||
|
"time-remaining mismatch");
|
||||||
|
zassert_equal(
|
||||||
|
test_data->issue_confirmed_notifications,
|
||||||
|
data->issue_confirmed_notifications,
|
||||||
|
"issue-confirmed-notifications mismatch");
|
||||||
|
zassert_equal(
|
||||||
|
test_data->cov_increment_present, data->cov_increment_present,
|
||||||
|
"cov-increment-present mismatch");
|
||||||
|
if (test_data->cov_increment_present) {
|
||||||
|
zassert_false(
|
||||||
|
islessgreater(test_data->cov_increment, data->cov_increment),
|
||||||
|
"cov-increment mismatch");
|
||||||
|
}
|
||||||
|
zassert_equal(
|
||||||
|
test_data->recipient.process_identifier,
|
||||||
|
data->recipient.process_identifier,
|
||||||
|
"recipient process-identifier mismatch");
|
||||||
|
status = bacnet_recipient_same(
|
||||||
|
&test_data->recipient.recipient, &data->recipient.recipient);
|
||||||
|
zassert_true(status, "recipient mismatch");
|
||||||
|
status = bacnet_object_property_reference_same(
|
||||||
|
&test_data->monitored_property_reference,
|
||||||
|
&data->monitored_property_reference);
|
||||||
|
zassert_true(status, "monitored-property-reference mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testCOVSubscriptionEncoding(const BACNET_COV_SUBSCRIPTION *data)
|
||||||
|
{
|
||||||
|
uint8_t apdu[480] = { 0 };
|
||||||
|
size_t len = 0, null_len = 0;
|
||||||
|
BACNET_COV_SUBSCRIPTION test_data = { 0 };
|
||||||
|
int decode_len = 0;
|
||||||
|
|
||||||
|
null_len = bacnet_cov_subscription_encode(NULL, sizeof(apdu), data);
|
||||||
|
len = bacnet_cov_subscription_encode(&apdu[0], sizeof(apdu), data);
|
||||||
|
zassert_true(len > 0, NULL);
|
||||||
|
zassert_equal(len, null_len, NULL);
|
||||||
|
|
||||||
|
if (len > 0) {
|
||||||
|
zassert_equal(
|
||||||
|
bacnet_cov_subscription_encode(&apdu[0], len - 1, data), 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
decode_len = bacnet_cov_subscription_decode(NULL, len, &test_data);
|
||||||
|
zassert_equal(decode_len, 0, NULL);
|
||||||
|
decode_len = bacnet_cov_subscription_decode(&apdu[0], 0, &test_data);
|
||||||
|
zassert_equal(decode_len, 0, NULL);
|
||||||
|
|
||||||
|
decode_len = bacnet_cov_subscription_decode(&apdu[0], len, NULL);
|
||||||
|
zassert_equal(decode_len, (int)len, NULL);
|
||||||
|
decode_len = bacnet_cov_subscription_decode(&apdu[0], len, &test_data);
|
||||||
|
zassert_equal(decode_len, (int)len, NULL);
|
||||||
|
testCOVSubscriptionData(data, &test_data);
|
||||||
|
}
|
||||||
|
|
||||||
static void testCOVSubscribeEncoding(
|
static void testCOVSubscribeEncoding(
|
||||||
uint8_t invoke_id, const BACNET_SUBSCRIBE_COV_DATA *data)
|
uint8_t invoke_id, const BACNET_SUBSCRIBE_COV_DATA *data)
|
||||||
{
|
{
|
||||||
@@ -450,6 +513,33 @@ static void testCOVSubscribeProperty(void)
|
|||||||
testCOVSubscribePropertyEncoding(invoke_id, &data);
|
testCOVSubscribePropertyEncoding(invoke_id, &data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_ZTEST_NEW_API)
|
||||||
|
ZTEST(cov_tests, testCOVSubscription)
|
||||||
|
#else
|
||||||
|
static void testCOVSubscription(void)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
BACNET_COV_SUBSCRIPTION data = { 0 };
|
||||||
|
|
||||||
|
data.time_remaining = 456;
|
||||||
|
data.issue_confirmed_notifications = true;
|
||||||
|
data.recipient.process_identifier = 1;
|
||||||
|
bacnet_recipient_device_set(&data.recipient.recipient, OBJECT_DEVICE, 123);
|
||||||
|
data.monitored_property_reference.object_identifier.type =
|
||||||
|
OBJECT_ANALOG_INPUT;
|
||||||
|
data.monitored_property_reference.object_identifier.instance = 321;
|
||||||
|
data.monitored_property_reference.property_identifier = PROP_PRESENT_VALUE;
|
||||||
|
data.monitored_property_reference.property_array_index = BACNET_ARRAY_ALL;
|
||||||
|
|
||||||
|
data.cov_increment_present = false;
|
||||||
|
data.cov_increment = 0.0f;
|
||||||
|
testCOVSubscriptionEncoding(&data);
|
||||||
|
|
||||||
|
data.cov_increment_present = true;
|
||||||
|
data.cov_increment = 1.5f;
|
||||||
|
testCOVSubscriptionEncoding(&data);
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_ZTEST_NEW_API)
|
#if defined(CONFIG_ZTEST_NEW_API)
|
||||||
ZTEST(cov_tests, test_COV_Value_List_Encode)
|
ZTEST(cov_tests, test_COV_Value_List_Encode)
|
||||||
#else
|
#else
|
||||||
@@ -606,6 +696,7 @@ void test_main(void)
|
|||||||
cov_tests, ztest_unit_test(testCOVNotify),
|
cov_tests, ztest_unit_test(testCOVNotify),
|
||||||
ztest_unit_test(testCOVSubscribe),
|
ztest_unit_test(testCOVSubscribe),
|
||||||
ztest_unit_test(testCOVSubscribeProperty),
|
ztest_unit_test(testCOVSubscribeProperty),
|
||||||
|
ztest_unit_test(testCOVSubscription),
|
||||||
ztest_unit_test(test_COV_Value_List_Encode));
|
ztest_unit_test(test_COV_Value_List_Encode));
|
||||||
|
|
||||||
ztest_run_test_suite(cov_tests);
|
ztest_run_test_suite(cov_tests);
|
||||||
|
|||||||
Reference in New Issue
Block a user