Fixed WriteProperty bypass which is only for present-value property of commandable objects. (#984)

This commit is contained in:
Steve Karg
2025-05-05 21:48:29 -05:00
committed by GitHub
parent 44d727c13b
commit 0f3cbe3ec5
7 changed files with 123 additions and 24 deletions
+16 -7
View File
@@ -760,14 +760,23 @@ does not support the Priority\_Array property.
## 8.24: Ignore Priority and NULL for Non-Commandable object writes ## 8.24: Ignore Priority and NULL for Non-Commandable object writes
If a property is writable but not commandable, the device shall process Clause 19.2.1.1 Commandable Properties specifies the list of
a WriteProperty, WriteGroup or WritePropertyMultiple request and ignore commandable object properties (only present-value for some objects),
the priority parameter if it is included in the request. and this table defines the list of commandable objects.
If the priority-array property for the object is optional
and not implemented for an object instance, then the object
is not commandable. The Channel object is commandable and does
not include a priority-array.
If an attempt is made to relinquish a property that is not commandable If any property is writable but the object is not commandable
and for which Null is not a supported datatype and no other error conditions (does not contain a priority-array or is a Channel object),
exist, the property shall not be changed, and the write shall be considered the device shall process a WriteProperty, WriteGroup or
successful [PR 21+]. WritePropertyMultiple request and ignore the priority parameter
if it is included in the request.
If an attempt is made to relinquish a present-value property but
the object is not commandable, the property shall not be changed,
and the write shall be considered successful [PR 21+].
## 8.25: Server devices are required to support all COV lifetime values up to 8 hours (28800 seconds) ## 8.25: Server devices are required to support all COV lifetime values up to 8 hours (28800 seconds)
+6
View File
@@ -40,6 +40,12 @@
* the property shall not be changed, and the write shall * the property shall not be changed, and the write shall
* be considered successful. * be considered successful.
* *
* @note There was an interpretation request in April 2025 that clarifies
* that the NULL bypass is only for present-value property of objects that
* optionally support a priority array but don't implement it.
* See 135-2024-19.2.1.1 Commandable Properties for the list of commandable
* properties of specific objects.
*
* @param wp_data [in] The WriteProperty data structure * @param wp_data [in] The WriteProperty data structure
* @return true if the write shall be considered successful * @return true if the write shall be considered successful
*/ */
+50
View File
@@ -534,3 +534,53 @@ bool property_list_bacnet_list_member(
return property_list_member(Properties_BACnetLIST, object_property); return property_list_member(Properties_BACnetLIST, object_property);
} }
/**
* @brief Determine if the object property is a commandable member
*
* 19.2.1.1 Commandable Properties
* The prioritization scheme is applied to certain properties of objects.
* The standard commandable properties and objects are as follows.
*
* @param object_type - object-type to be checked
* @param object_property - object-property to be checked
* @return true if the property is a commandable member
*/
bool property_list_commandable_member(
BACNET_OBJECT_TYPE object_type, BACNET_PROPERTY_ID object_property)
{
bool status = false;
switch (object_type) {
case OBJECT_ACCESS_DOOR:
case OBJECT_ANALOG_OUTPUT:
case OBJECT_ANALOG_VALUE:
case OBJECT_BINARY_LIGHTING_OUTPUT:
case OBJECT_BINARY_OUTPUT:
case OBJECT_BINARY_VALUE:
case OBJECT_BITSTRING_VALUE:
case OBJECT_CHANNEL:
case OBJECT_CHARACTERSTRING_VALUE:
case OBJECT_DATE_VALUE:
case OBJECT_DATE_PATTERN_VALUE:
case OBJECT_DATETIME_VALUE:
case OBJECT_DATETIME_PATTERN_VALUE:
case OBJECT_INTEGER_VALUE:
case OBJECT_LARGE_ANALOG_VALUE:
case OBJECT_LIGHTING_OUTPUT:
case OBJECT_MULTI_STATE_OUTPUT:
case OBJECT_MULTI_STATE_VALUE:
case OBJECT_OCTETSTRING_VALUE:
case OBJECT_POSITIVE_INTEGER_VALUE:
case OBJECT_TIME_VALUE:
case OBJECT_TIME_PATTERN_VALUE:
if (object_property == PROP_PRESENT_VALUE) {
status = true;
}
break;
default:
break;
}
return status;
}
+3
View File
@@ -62,6 +62,9 @@ const int *property_list_bacnet_list(void);
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
bool property_list_bacnet_list_member( bool property_list_bacnet_list_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_commandable_member(
BACNET_OBJECT_TYPE object_type, BACNET_PROPERTY_ID object_property);
#ifdef __cplusplus #ifdef __cplusplus
} }
+21 -15
View File
@@ -500,6 +500,12 @@ bool write_property_unsigned_decode(
* the property shall not be changed, and the write shall * the property shall not be changed, and the write shall
* be considered successful. * be considered successful.
* *
* @note There was an interpretation request in April 2025 that clarifies
* that the NULL bypass is only for present-value property of objects that
* optionally support a priority array but don't implement it.
* See 135-2024-19.2.1.1 Commandable Properties for the list of commandable
* properties of specific objects.
*
* @param wp_data [in] The WriteProperty data structure * @param wp_data [in] The WriteProperty data structure
* @param member_of_object [in] Function to check if a property is a member * @param member_of_object [in] Function to check if a property is a member
of an object instance of an object instance
@@ -520,25 +526,25 @@ bool write_property_relinquish_bypass(
wp_data->application_data, wp_data->application_data_len); wp_data->application_data, wp_data->application_data_len);
if ((len > 0) && (len == wp_data->application_data_len)) { if ((len > 0) && (len == wp_data->application_data_len)) {
/* single NULL */ /* single NULL */
/* check to see if this object property is commandable. if (property_list_commandable_member(
Does the property list contain a priority-array? */ wp_data->object_type, wp_data->object_property)) {
if (member_of_object) { if (member_of_object) {
has_priority_array = member_of_object( /* check to see if this object property is commandable.
wp_data->object_type, wp_data->object_instance, Does the property list contain a priority-array? */
PROP_PRIORITY_ARRAY); has_priority_array = member_of_object(
} wp_data->object_type, wp_data->object_instance,
if (has_priority_array || (wp_data->object_type == OBJECT_CHANNEL)) { PROP_PRIORITY_ARRAY);
if (wp_data->object_property != PROP_PRESENT_VALUE) { }
/* this property is not commandable, if (has_priority_array ||
(wp_data->object_type == OBJECT_CHANNEL)) {
/* this property is commandable and shall not be bypassed */
} else {
/* this property that is optionally commandable
is not commandable for this object instance,
so it "shall not be changed, and so it "shall not be changed, and
the write shall be considered successful." */ the write shall be considered successful." */
bypass = true; bypass = true;
} }
} else {
/* this object is not commandable, so any property
written with a NULL "shall not be changed, and
the write shall be considered successful." */
bypass = true;
} }
} }
+20
View File
@@ -69,6 +69,26 @@ static void testPropList(void)
property_list_member( property_list_member(
property_list.Required.pList, PROP_OBJECT_NAME), property_list.Required.pList, PROP_OBJECT_NAME),
NULL); NULL);
/* validate commandable object property list */
if ((i == OBJECT_CHANNEL) ||
(property_list_member(
property_list.Required.pList, PROP_PRESENT_VALUE) &&
(property_list_member(
property_list.Required.pList, PROP_PRIORITY_ARRAY) ||
property_list_member(
property_list.Optional.pList, PROP_PRIORITY_ARRAY)))) {
zassert_true(
property_list_commandable_member(
(BACNET_OBJECT_TYPE)i, PROP_PRESENT_VALUE),
"Object %s present-value is not listed as commandable",
bactext_object_type_name((BACNET_OBJECT_TYPE)i));
} else {
zassert_false(
property_list_commandable_member(
(BACNET_OBJECT_TYPE)i, PROP_PRESENT_VALUE),
"Object %s present-value is listed as commandable",
bactext_object_type_name((BACNET_OBJECT_TYPE)i));
}
} }
/* property is a BACnetARRAY */ /* property is a BACnetARRAY */
for (i = 0; i < OBJECT_PROPRIETARY_MIN; i++) { for (i = 0; i < OBJECT_PROPRIETARY_MIN; i++) {
+7 -2
View File
@@ -308,7 +308,7 @@ static void testWritePropertyNull(void)
bypass = write_property_relinquish_bypass(NULL, NULL); bypass = write_property_relinquish_bypass(NULL, NULL);
zassert_equal(bypass, false, NULL); zassert_equal(bypass, false, NULL);
wp_data.object_type = OBJECT_ANALOG_OUTPUT; wp_data.object_type = OBJECT_ANALOG_VALUE;
wp_data.object_instance = 0; wp_data.object_instance = 0;
wp_data.object_property = PROP_PRESENT_VALUE; wp_data.object_property = PROP_PRESENT_VALUE;
wp_data.application_data_len = wp_data.application_data_len =
@@ -321,11 +321,16 @@ static void testWritePropertyNull(void)
bypass = write_property_relinquish_bypass( bypass = write_property_relinquish_bypass(
&wp_data, test_write_property_member_of_object); &wp_data, test_write_property_member_of_object);
zassert_equal(bypass, true, NULL); zassert_equal(bypass, true, NULL);
wp_data.object_type = OBJECT_CHANNEL;
Is_Property_Member = false;
bypass = write_property_relinquish_bypass(
&wp_data, test_write_property_member_of_object);
zassert_equal(bypass, false, NULL);
wp_data.object_property = PROP_OUT_OF_SERVICE; wp_data.object_property = PROP_OUT_OF_SERVICE;
Is_Property_Member = true; Is_Property_Member = true;
bypass = write_property_relinquish_bypass( bypass = write_property_relinquish_bypass(
&wp_data, test_write_property_member_of_object); &wp_data, test_write_property_member_of_object);
zassert_equal(bypass, true, NULL); zassert_equal(bypass, false, NULL);
} }
#if defined(CONFIG_ZTEST_NEW_API) #if defined(CONFIG_ZTEST_NEW_API)