Add property_list_read_only_member function to check for READ-ONLY properties (#1258)
This commit is contained in:
@@ -77,6 +77,8 @@ CMakeCache.txt
|
|||||||
CMakeFiles/
|
CMakeFiles/
|
||||||
cmake_install.cmake
|
cmake_install.cmake
|
||||||
CTestTestfile.cmake
|
CTestTestfile.cmake
|
||||||
|
CMakeSettings.json
|
||||||
|
|
||||||
# zephyr artifacts
|
# zephyr artifacts
|
||||||
/twister-out/*
|
/twister-out/*
|
||||||
/test/build/
|
/test/build/
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ The git repositories are hosted at the following sites:
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
* Added property_list_read_only_member function to check for READ-ONLY
|
||||||
|
properties. (#1258)
|
||||||
* Added WriteProperty support in the basic Structured View object.
|
* Added WriteProperty support in the basic Structured View object.
|
||||||
Converted Structured View internal storage to dynamically allocated
|
Converted Structured View internal storage to dynamically allocated
|
||||||
character strings and a keylist-based subordinate list that is resizable
|
character strings and a keylist-based subordinate list that is resizable
|
||||||
|
|||||||
@@ -601,3 +601,188 @@ bool property_list_commandable_member(
|
|||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Determine if the object property is a READ-ONLY property
|
||||||
|
* @param object_type - object-type to be checked
|
||||||
|
* @param object_property - object-property to be checked
|
||||||
|
* @return true if the property is a READ-ONLY property
|
||||||
|
* @note generally read-only per EPICS property gathering,
|
||||||
|
* sometimes stated explicitly in the standard and sometimes not.
|
||||||
|
* Used in the EPICS tools to determine if a property should
|
||||||
|
* avoid being written to determine writability of the property.
|
||||||
|
*/
|
||||||
|
bool property_list_read_only_member(
|
||||||
|
BACNET_OBJECT_TYPE object_type, BACNET_PROPERTY_ID object_property)
|
||||||
|
{
|
||||||
|
/* exceptions where the property is READ-ONLY only in specific objects */
|
||||||
|
switch (object_type) {
|
||||||
|
case OBJECT_AVERAGING:
|
||||||
|
switch (object_property) {
|
||||||
|
case PROP_MINIMUM_VALUE:
|
||||||
|
case PROP_MINIMUM_VALUE_TIMESTAMP:
|
||||||
|
case PROP_AVERAGE_VALUE:
|
||||||
|
case PROP_VARIANCE_VALUE:
|
||||||
|
case PROP_MAXIMUM_VALUE:
|
||||||
|
case PROP_MAXIMUM_VALUE_TIMESTAMP:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OBJECT_CALENDAR:
|
||||||
|
switch (object_property) {
|
||||||
|
case PROP_PRESENT_VALUE:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OBJECT_COMMAND:
|
||||||
|
switch (object_property) {
|
||||||
|
case PROP_IN_PROCESS:
|
||||||
|
case PROP_ALL_WRITES_SUCCESSFUL:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OBJECT_DEVICE:
|
||||||
|
switch (object_property) {
|
||||||
|
case PROP_SYSTEM_STATUS:
|
||||||
|
case PROP_VENDOR_NAME:
|
||||||
|
case PROP_VENDOR_IDENTIFIER:
|
||||||
|
case PROP_MODEL_NAME:
|
||||||
|
case PROP_FIRMWARE_REVISION:
|
||||||
|
case PROP_APPLICATION_SOFTWARE_VERSION:
|
||||||
|
case PROP_PROTOCOL_VERSION:
|
||||||
|
case PROP_PROTOCOL_REVISION:
|
||||||
|
case PROP_PROTOCOL_SERVICES_SUPPORTED:
|
||||||
|
case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED:
|
||||||
|
case PROP_SEGMENTATION_SUPPORTED:
|
||||||
|
case PROP_ACTIVE_VT_SESSIONS:
|
||||||
|
case PROP_DATABASE_REVISION:
|
||||||
|
case PROP_LAST_RESTORE_TIME:
|
||||||
|
case PROP_TIME_OF_DEVICE_RESTART:
|
||||||
|
case PROP_BACKUP_AND_RESTORE_STATE:
|
||||||
|
case PROP_ACTIVE_COV_MULTIPLE_SUBSCRIPTIONS:
|
||||||
|
case PROP_ACTIVE_COV_SUBSCRIPTIONS:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OBJECT_PROGRAM:
|
||||||
|
switch (object_property) {
|
||||||
|
case PROP_REASON_FOR_HALT:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OBJECT_FILE:
|
||||||
|
switch (object_property) {
|
||||||
|
case PROP_MODIFICATION_DATE:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OBJECT_PULSE_CONVERTER:
|
||||||
|
switch (object_property) {
|
||||||
|
case PROP_COUNT_BEFORE_CHANGE:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OBJECT_TRENDLOG:
|
||||||
|
case OBJECT_EVENT_LOG:
|
||||||
|
case OBJECT_AUDIT_LOG:
|
||||||
|
case OBJECT_TREND_LOG_MULTIPLE:
|
||||||
|
switch (object_property) {
|
||||||
|
case PROP_TOTAL_RECORD_COUNT:
|
||||||
|
case PROP_RECORDS_SINCE_NOTIFICATION:
|
||||||
|
case PROP_LAST_NOTIFY_RECORD:
|
||||||
|
case PROP_LOG_BUFFER:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OBJECT_GLOBAL_GROUP:
|
||||||
|
switch (object_property) {
|
||||||
|
case PROP_MEMBER_STATUS_FLAGS:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OBJECT_CHANNEL:
|
||||||
|
switch (object_property) {
|
||||||
|
case PROP_WRITE_STATUS:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OBJECT_LIGHTING_OUTPUT:
|
||||||
|
switch (object_property) {
|
||||||
|
case PROP_IN_PROGRESS:
|
||||||
|
case PROP_EGRESS_ACTIVE:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OBJECT_BINARY_LIGHTING_OUTPUT:
|
||||||
|
switch (object_property) {
|
||||||
|
case PROP_EGRESS_ACTIVE:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OBJECT_NETWORK_PORT:
|
||||||
|
switch (object_property) {
|
||||||
|
case PROP_CHANGES_PENDING:
|
||||||
|
case PROP_SLAVE_ADDRESS_BINDING:
|
||||||
|
case PROP_WRITE_STATUS:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OBJECT_LOAD_CONTROL:
|
||||||
|
switch (object_property) {
|
||||||
|
case PROP_PRESENT_VALUE:
|
||||||
|
case PROP_EXPECTED_SHED_LEVEL:
|
||||||
|
case PROP_ACTUAL_SHED_LEVEL:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((object_property >= PROP_PROPRIETARY_RANGE_MIN) &&
|
||||||
|
(object_property <= PROP_PROPRIETARY_RANGE_MAX)) {
|
||||||
|
/* all proprietary properties could be read-only */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/* Some properties, like Present_Value and Reliability,
|
||||||
|
may be temporarily writable under specific test
|
||||||
|
conditions (per Addendum 2020ci), but are defined
|
||||||
|
as read-only by default.*/
|
||||||
|
if (object_property == PROP_PRESENT_VALUE) {
|
||||||
|
if (!property_list_commandable_member(object_type, object_property)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (object_property == PROP_RELIABILITY) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -83,6 +83,9 @@ bool property_list_bacnet_list_member(
|
|||||||
BACNET_STACK_EXPORT
|
BACNET_STACK_EXPORT
|
||||||
bool property_list_commandable_member(
|
bool property_list_commandable_member(
|
||||||
BACNET_OBJECT_TYPE object_type, BACNET_PROPERTY_ID object_property);
|
BACNET_OBJECT_TYPE object_type, BACNET_PROPERTY_ID object_property);
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool property_list_read_only_member(
|
||||||
|
BACNET_OBJECT_TYPE object_type, BACNET_PROPERTY_ID object_property);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
* @copyright SPDX-License-Identifier: MIT
|
* @copyright SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
#include <zephyr/ztest.h>
|
#include <zephyr/ztest.h>
|
||||||
|
#include <bacnet/bacdcode.h>
|
||||||
#include <bacnet/property.h>
|
#include <bacnet/property.h>
|
||||||
#include <bacnet/bactext.h>
|
#include <bacnet/bactext.h>
|
||||||
|
|
||||||
@@ -106,6 +107,352 @@ static void testPropList(void)
|
|||||||
}
|
}
|
||||||
count = property_list_count(property_list_bacnet_array());
|
count = property_list_count(property_list_bacnet_array());
|
||||||
zassert_true(count > 0, NULL);
|
zassert_true(count > 0, NULL);
|
||||||
|
|
||||||
|
/* property is read-only */
|
||||||
|
zassert_true(
|
||||||
|
property_list_read_only_member(OBJECT_AVERAGING, PROP_MINIMUM_VALUE),
|
||||||
|
"Averaging minimum-value should be read-only");
|
||||||
|
zassert_true(
|
||||||
|
property_list_read_only_member(OBJECT_CALENDAR, PROP_PRESENT_VALUE),
|
||||||
|
"Calendar present-value should be read-only");
|
||||||
|
zassert_true(
|
||||||
|
property_list_read_only_member(OBJECT_COMMAND, PROP_IN_PROCESS),
|
||||||
|
"Command in-process should be read-only");
|
||||||
|
zassert_true(
|
||||||
|
property_list_read_only_member(OBJECT_ANALOG_INPUT, PROP_PRESENT_VALUE),
|
||||||
|
"Analog Input present-value should be read-only");
|
||||||
|
zassert_false(
|
||||||
|
property_list_read_only_member(
|
||||||
|
OBJECT_ANALOG_OUTPUT, PROP_PRESENT_VALUE),
|
||||||
|
"Analog Output present-value should not be read-only");
|
||||||
|
zassert_true(
|
||||||
|
property_list_read_only_member(OBJECT_DEVICE, PROP_VENDOR_NAME),
|
||||||
|
"Device vendor-name should be read-only");
|
||||||
|
zassert_true(
|
||||||
|
property_list_read_only_member(OBJECT_PROGRAM, PROP_REASON_FOR_HALT),
|
||||||
|
"Program reason-for-halt should be read-only");
|
||||||
|
zassert_true(
|
||||||
|
property_list_read_only_member(OBJECT_FILE, PROP_MODIFICATION_DATE),
|
||||||
|
"File modification-date should be read-only");
|
||||||
|
zassert_true(
|
||||||
|
property_list_read_only_member(
|
||||||
|
OBJECT_PULSE_CONVERTER, PROP_COUNT_BEFORE_CHANGE),
|
||||||
|
"Pulse Converter count-before-change should be read-only");
|
||||||
|
zassert_true(
|
||||||
|
property_list_read_only_member(
|
||||||
|
OBJECT_TRENDLOG, PROP_TOTAL_RECORD_COUNT),
|
||||||
|
"Trend Log total-record-count should be read-only");
|
||||||
|
zassert_true(
|
||||||
|
property_list_read_only_member(
|
||||||
|
OBJECT_GLOBAL_GROUP, PROP_MEMBER_STATUS_FLAGS),
|
||||||
|
"Global Group member-status-flags should be read-only");
|
||||||
|
zassert_true(
|
||||||
|
property_list_read_only_member(OBJECT_CHANNEL, PROP_WRITE_STATUS),
|
||||||
|
"Channel write-status should be read-only");
|
||||||
|
zassert_true(
|
||||||
|
property_list_read_only_member(
|
||||||
|
OBJECT_LIGHTING_OUTPUT, PROP_IN_PROGRESS),
|
||||||
|
"Lighting Output in-progress should be read-only");
|
||||||
|
zassert_true(
|
||||||
|
property_list_read_only_member(
|
||||||
|
OBJECT_BINARY_LIGHTING_OUTPUT, PROP_EGRESS_ACTIVE),
|
||||||
|
"Binary Lighting Output egress-active should be read-only");
|
||||||
|
zassert_true(
|
||||||
|
property_list_read_only_member(
|
||||||
|
OBJECT_NETWORK_PORT, PROP_CHANGES_PENDING),
|
||||||
|
"Network Port changes-pending should be read-only");
|
||||||
|
zassert_true(
|
||||||
|
property_list_read_only_member(OBJECT_LOAD_CONTROL, PROP_PRESENT_VALUE),
|
||||||
|
"Load Control present-value should be read-only");
|
||||||
|
zassert_true(
|
||||||
|
property_list_read_only_member(OBJECT_DEVICE, PROP_RELIABILITY),
|
||||||
|
"Reliability should be read-only");
|
||||||
|
zassert_true(
|
||||||
|
property_list_read_only_member(
|
||||||
|
OBJECT_DEVICE, PROP_PROPRIETARY_RANGE_MIN),
|
||||||
|
"Proprietary properties should be read-only");
|
||||||
|
for (i = 0; i < OBJECT_PROPRIETARY_MIN; i++) {
|
||||||
|
object_type = i;
|
||||||
|
status = property_list_read_only_member(object_type, PROP_OBJECT_NAME);
|
||||||
|
zassert_false(
|
||||||
|
status, "Object name should not be read-only for object type %d",
|
||||||
|
object_type);
|
||||||
|
}
|
||||||
|
/* test property_lists_member() - checks all three lists at once */
|
||||||
|
property_list_special(OBJECT_ANALOG_INPUT, &property_list);
|
||||||
|
/* required property must be found */
|
||||||
|
zassert_true(
|
||||||
|
property_lists_member(
|
||||||
|
property_list.Required.pList, property_list.Optional.pList,
|
||||||
|
property_list.Proprietary.pList, PROP_PRESENT_VALUE),
|
||||||
|
"Analog Input present-value should be in property lists");
|
||||||
|
/* optional property must be found */
|
||||||
|
zassert_true(
|
||||||
|
property_lists_member(
|
||||||
|
property_list.Required.pList, property_list.Optional.pList,
|
||||||
|
property_list.Proprietary.pList, PROP_COV_INCREMENT),
|
||||||
|
"Analog Input cov-increment should be in property lists");
|
||||||
|
/* property not in any list must not be found */
|
||||||
|
zassert_false(
|
||||||
|
property_lists_member(
|
||||||
|
property_list.Required.pList, property_list.Optional.pList,
|
||||||
|
property_list.Proprietary.pList, PROP_PRIORITY_ARRAY),
|
||||||
|
"Analog Input priority-array should not be in property lists");
|
||||||
|
/* NULL lists must not find any property */
|
||||||
|
zassert_false(
|
||||||
|
property_lists_member(NULL, NULL, NULL, PROP_PRESENT_VALUE),
|
||||||
|
"NULL lists should not find any property");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test property_list_common() - common properties for all objects
|
||||||
|
*/
|
||||||
|
#if defined(CONFIG_ZTEST_NEW_API)
|
||||||
|
ZTEST(property_tests, testPropListCommon)
|
||||||
|
#else
|
||||||
|
static void testPropListCommon(void)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
/* object-identifier and object-type are common (handled by this module) */
|
||||||
|
zassert_true(
|
||||||
|
property_list_common(PROP_OBJECT_IDENTIFIER),
|
||||||
|
"object-identifier should be a common property");
|
||||||
|
zassert_true(
|
||||||
|
property_list_common(PROP_OBJECT_TYPE),
|
||||||
|
"object-type should be a common property");
|
||||||
|
/* other properties are not common */
|
||||||
|
zassert_false(
|
||||||
|
property_list_common(PROP_OBJECT_NAME),
|
||||||
|
"object-name should not be a common property");
|
||||||
|
zassert_false(
|
||||||
|
property_list_common(PROP_PRESENT_VALUE),
|
||||||
|
"present-value should not be a common property");
|
||||||
|
zassert_false(
|
||||||
|
property_list_common(PROP_DESCRIPTION),
|
||||||
|
"description should not be a common property");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test property_list_encode() and property_list_common_encode()
|
||||||
|
*/
|
||||||
|
#if defined(CONFIG_ZTEST_NEW_API)
|
||||||
|
ZTEST(property_tests, testPropListEncode)
|
||||||
|
#else
|
||||||
|
static void testPropListEncode(void)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
/* known property lists with 8 required, 2 optional, 0 proprietary */
|
||||||
|
static const int32_t pRequired[] = {
|
||||||
|
PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE,
|
||||||
|
PROP_PRESENT_VALUE, PROP_STATUS_FLAGS, PROP_EVENT_STATE,
|
||||||
|
PROP_OUT_OF_SERVICE, PROP_UNITS, -1
|
||||||
|
};
|
||||||
|
static const int32_t pOptional[] = { PROP_DESCRIPTION, PROP_COV_INCREMENT,
|
||||||
|
-1 };
|
||||||
|
static const int32_t pProprietary[] = { -1 };
|
||||||
|
/* count: 8 required - 3 standard + 2 optional = 7 entries in
|
||||||
|
* PROP_PROPERTY_LIST */
|
||||||
|
const uint32_t expected_count = 7;
|
||||||
|
uint8_t apdu[MAX_APDU] = { 0 };
|
||||||
|
BACNET_READ_PROPERTY_DATA rpdata = { 0 };
|
||||||
|
int len = 0;
|
||||||
|
BACNET_UNSIGNED_INTEGER decoded_count = 0;
|
||||||
|
BACNET_OBJECT_TYPE decoded_type = 0;
|
||||||
|
uint32_t decoded_instance = 0;
|
||||||
|
|
||||||
|
/* --- property_list_encode --- */
|
||||||
|
/* NULL rpdata returns 0 */
|
||||||
|
len = property_list_encode(NULL, pRequired, pOptional, pProprietary);
|
||||||
|
zassert_equal(len, 0, "NULL rpdata should return 0");
|
||||||
|
|
||||||
|
/* no application data buffer returns 0 */
|
||||||
|
rpdata.object_property = PROP_PROPERTY_LIST;
|
||||||
|
rpdata.array_index = BACNET_ARRAY_ALL;
|
||||||
|
rpdata.application_data = NULL;
|
||||||
|
rpdata.application_data_len = 0;
|
||||||
|
len = property_list_encode(&rpdata, pRequired, pOptional, pProprietary);
|
||||||
|
zassert_equal(len, 0, "no buffer should return 0");
|
||||||
|
|
||||||
|
/* array_index == 0: encode count of entries */
|
||||||
|
rpdata.application_data = apdu;
|
||||||
|
rpdata.application_data_len = sizeof(apdu);
|
||||||
|
rpdata.array_index = 0;
|
||||||
|
len = property_list_encode(&rpdata, pRequired, pOptional, pProprietary);
|
||||||
|
zassert_true(len > 0, "array_index=0 should encode the count");
|
||||||
|
zassert_equal(
|
||||||
|
bacnet_unsigned_application_decode(apdu, len, &decoded_count), len,
|
||||||
|
"decoded length should match encoded length");
|
||||||
|
zassert_equal(
|
||||||
|
decoded_count, expected_count, "decoded count should match expected");
|
||||||
|
|
||||||
|
/* array_index == BACNET_ARRAY_ALL: encode all non-standard entries */
|
||||||
|
rpdata.array_index = BACNET_ARRAY_ALL;
|
||||||
|
len = property_list_encode(&rpdata, pRequired, pOptional, pProprietary);
|
||||||
|
zassert_true(len > 0, "array_index=ALL should encode all entries");
|
||||||
|
|
||||||
|
/* array_index == 1: encode first non-standard required property */
|
||||||
|
rpdata.array_index = 1;
|
||||||
|
len = property_list_encode(&rpdata, pRequired, pOptional, pProprietary);
|
||||||
|
zassert_true(len > 0, "array_index=1 should encode first entry");
|
||||||
|
|
||||||
|
/* array_index == last valid index */
|
||||||
|
rpdata.array_index = expected_count;
|
||||||
|
len = property_list_encode(&rpdata, pRequired, pOptional, pProprietary);
|
||||||
|
zassert_true(len > 0, "array_index=last should encode the last entry");
|
||||||
|
|
||||||
|
/* array_index out of range: error */
|
||||||
|
rpdata.array_index = expected_count + 1;
|
||||||
|
len = property_list_encode(&rpdata, pRequired, pOptional, pProprietary);
|
||||||
|
zassert_equal(
|
||||||
|
len, BACNET_STATUS_ERROR,
|
||||||
|
"out-of-range array_index should return BACNET_STATUS_ERROR");
|
||||||
|
|
||||||
|
/* non-PROP_PROPERTY_LIST property: error */
|
||||||
|
rpdata.array_index = BACNET_ARRAY_ALL;
|
||||||
|
rpdata.object_property = PROP_OBJECT_NAME;
|
||||||
|
len = property_list_encode(&rpdata, pRequired, pOptional, pProprietary);
|
||||||
|
zassert_equal(
|
||||||
|
len, BACNET_STATUS_ERROR,
|
||||||
|
"unknown property should return BACNET_STATUS_ERROR");
|
||||||
|
|
||||||
|
/* --- property_list_common_encode --- */
|
||||||
|
/* NULL rpdata returns 0 */
|
||||||
|
len = property_list_common_encode(NULL, 0);
|
||||||
|
zassert_equal(len, 0, "NULL rpdata should return 0");
|
||||||
|
|
||||||
|
/* no buffer returns 0 */
|
||||||
|
rpdata.application_data = NULL;
|
||||||
|
rpdata.application_data_len = 0;
|
||||||
|
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
|
||||||
|
len = property_list_common_encode(&rpdata, 1);
|
||||||
|
zassert_equal(len, 0, "no buffer should return 0");
|
||||||
|
|
||||||
|
/* PROP_OBJECT_IDENTIFIER encodes object type + instance */
|
||||||
|
rpdata.object_type = OBJECT_ANALOG_INPUT;
|
||||||
|
rpdata.object_instance = 1;
|
||||||
|
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
|
||||||
|
rpdata.application_data = apdu;
|
||||||
|
rpdata.application_data_len = sizeof(apdu);
|
||||||
|
len = property_list_common_encode(&rpdata, 0);
|
||||||
|
zassert_true(len > 0, "PROP_OBJECT_IDENTIFIER should encode successfully");
|
||||||
|
zassert_equal(
|
||||||
|
bacnet_object_id_application_decode(
|
||||||
|
apdu, len, &decoded_type, &decoded_instance),
|
||||||
|
len, "decoded length should match encoded length");
|
||||||
|
zassert_equal(
|
||||||
|
decoded_type, OBJECT_ANALOG_INPUT, "decoded object type should match");
|
||||||
|
zassert_equal(
|
||||||
|
decoded_instance, (uint32_t)1, "decoded object instance should match");
|
||||||
|
|
||||||
|
/* PROP_OBJECT_IDENTIFIER on Device uses device_instance_number */
|
||||||
|
rpdata.object_type = OBJECT_DEVICE;
|
||||||
|
rpdata.object_instance = BACNET_MAX_INSTANCE;
|
||||||
|
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
|
||||||
|
len = property_list_common_encode(&rpdata, 1234);
|
||||||
|
zassert_true(len > 0, "Device PROP_OBJECT_IDENTIFIER should encode");
|
||||||
|
zassert_equal(
|
||||||
|
bacnet_object_id_application_decode(
|
||||||
|
apdu, len, &decoded_type, &decoded_instance),
|
||||||
|
len, "decoded length should match encoded length");
|
||||||
|
zassert_equal(
|
||||||
|
decoded_type, OBJECT_DEVICE, "decoded type should be OBJECT_DEVICE");
|
||||||
|
zassert_equal(
|
||||||
|
decoded_instance, (uint32_t)1234,
|
||||||
|
"Device instance should use device_instance_number parameter");
|
||||||
|
|
||||||
|
/* PROP_OBJECT_TYPE encodes the object type enumeration */
|
||||||
|
rpdata.object_type = OBJECT_ANALOG_OUTPUT;
|
||||||
|
rpdata.object_property = PROP_OBJECT_TYPE;
|
||||||
|
len = property_list_common_encode(&rpdata, 0);
|
||||||
|
zassert_true(len > 0, "PROP_OBJECT_TYPE should encode successfully");
|
||||||
|
|
||||||
|
/* unknown property returns BACNET_STATUS_ERROR */
|
||||||
|
rpdata.object_property = PROP_OBJECT_NAME;
|
||||||
|
len = property_list_common_encode(&rpdata, 0);
|
||||||
|
zassert_equal(
|
||||||
|
len, BACNET_STATUS_ERROR,
|
||||||
|
"unknown property should return BACNET_STATUS_ERROR");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test property_list_bacnet_list() and
|
||||||
|
* property_list_bacnet_list_member()
|
||||||
|
*/
|
||||||
|
#if defined(CONFIG_ZTEST_NEW_API)
|
||||||
|
ZTEST(property_tests, testPropListBACnetList)
|
||||||
|
#else
|
||||||
|
static void testPropListBACnetList(void)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
const int32_t *pList = NULL;
|
||||||
|
uint32_t count = 0;
|
||||||
|
|
||||||
|
/* list pointer is non-NULL and has at least one entry */
|
||||||
|
pList = property_list_bacnet_list();
|
||||||
|
zassert_not_null(pList, "BACnetLIST should not be NULL");
|
||||||
|
count = property_list_count(pList);
|
||||||
|
zassert_true(count > 0, "BACnetLIST should have at least one entry");
|
||||||
|
|
||||||
|
/* known BACnetLIST members - present in the global list */
|
||||||
|
zassert_true(
|
||||||
|
property_list_bacnet_list_member(OBJECT_DEVICE, PROP_DATE_LIST),
|
||||||
|
"date-list should be a BACnetLIST member");
|
||||||
|
zassert_true(
|
||||||
|
property_list_bacnet_list_member(
|
||||||
|
OBJECT_DEVICE, PROP_ACTIVE_COV_SUBSCRIPTIONS),
|
||||||
|
"active-cov-subscriptions should be a BACnetLIST member");
|
||||||
|
zassert_true(
|
||||||
|
property_list_bacnet_list_member(
|
||||||
|
OBJECT_DEVICE, PROP_DEVICE_ADDRESS_BINDING),
|
||||||
|
"device-address-binding should be a BACnetLIST member");
|
||||||
|
zassert_true(
|
||||||
|
property_list_bacnet_list_member(
|
||||||
|
OBJECT_NOTIFICATION_CLASS, PROP_RECIPIENT_LIST),
|
||||||
|
"recipient-list should be a BACnetLIST member");
|
||||||
|
zassert_true(
|
||||||
|
property_list_bacnet_list_member(OBJECT_TRENDLOG, PROP_LOG_BUFFER),
|
||||||
|
"log-buffer should be a BACnetLIST member");
|
||||||
|
|
||||||
|
/* BACnetARRAY properties are not BACnetLIST */
|
||||||
|
zassert_false(
|
||||||
|
property_list_bacnet_list_member(
|
||||||
|
OBJECT_ANALOG_OUTPUT, PROP_PRIORITY_ARRAY),
|
||||||
|
"priority-array should not be a BACnetLIST member");
|
||||||
|
zassert_false(
|
||||||
|
property_list_bacnet_list_member(OBJECT_DEVICE, PROP_OBJECT_LIST),
|
||||||
|
"object-list should not be a BACnetLIST member");
|
||||||
|
|
||||||
|
/* standard scalar properties are not BACnetLIST */
|
||||||
|
zassert_false(
|
||||||
|
property_list_bacnet_list_member(
|
||||||
|
OBJECT_ANALOG_INPUT, PROP_PRESENT_VALUE),
|
||||||
|
"Analog Input present-value should not be a BACnetLIST member");
|
||||||
|
zassert_false(
|
||||||
|
property_list_bacnet_list_member(OBJECT_DEVICE, PROP_VENDOR_NAME),
|
||||||
|
"vendor-name should not be a BACnetLIST member");
|
||||||
|
|
||||||
|
/* object-type exceptions: Group present-value is a BACnetLIST */
|
||||||
|
zassert_true(
|
||||||
|
property_list_bacnet_list_member(OBJECT_GROUP, PROP_PRESENT_VALUE),
|
||||||
|
"Group present-value should be a BACnetLIST member");
|
||||||
|
|
||||||
|
/* object-type exceptions: Channel list-of-object-property-references
|
||||||
|
is NOT a BACnetLIST (it is a BACnetARRAY) */
|
||||||
|
zassert_false(
|
||||||
|
property_list_bacnet_list_member(
|
||||||
|
OBJECT_CHANNEL, PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES),
|
||||||
|
"Channel list-of-object-property-references should not be a "
|
||||||
|
"BACnetLIST member");
|
||||||
|
|
||||||
|
/* proprietary range properties are always considered BACnetLIST */
|
||||||
|
zassert_true(
|
||||||
|
property_list_bacnet_list_member(
|
||||||
|
OBJECT_ANALOG_INPUT, PROP_PROPRIETARY_RANGE_MIN),
|
||||||
|
"proprietary properties should be considered BACnetLIST members");
|
||||||
|
zassert_true(
|
||||||
|
property_list_bacnet_list_member(
|
||||||
|
OBJECT_DEVICE, PROP_PROPRIETARY_RANGE_MAX),
|
||||||
|
"proprietary properties should be considered BACnetLIST members");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,7 +464,11 @@ ZTEST_SUITE(property_tests, NULL, NULL, NULL, NULL, NULL);
|
|||||||
#else
|
#else
|
||||||
void test_main(void)
|
void test_main(void)
|
||||||
{
|
{
|
||||||
ztest_test_suite(property_tests, ztest_unit_test(testPropList));
|
ztest_test_suite(
|
||||||
|
property_tests, ztest_unit_test(testPropList),
|
||||||
|
ztest_unit_test(testPropListCommon),
|
||||||
|
ztest_unit_test(testPropListEncode),
|
||||||
|
ztest_unit_test(testPropListBACnetList));
|
||||||
|
|
||||||
ztest_run_test_suite(property_tests);
|
ztest_run_test_suite(property_tests);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user