Fixed CreateObject service list-of-initial-values encoding and decoding. (#1199)

Fixes the CreateObject service list-of-initial-values encoding and decoding by refactoring the data structure to be similar to WriteProperty. The implementation changes from using a linked list of property values to using a flat buffer approach with delayed decoding.

Changes:

* Refactored BACNET_CREATE_OBJECT_DATA structure to use an application_data buffer instead of a linked list for initial values
* Added a new create_object_process() and create_object_initializer_list_process() functions to centralize object creation logic and error reporting
* Updated all device implementations to use the new centralized creation functions
* Enhanced the create-object example application to support command-line specification of initial property values
* Added comprehensive test coverage for the new encoding/decoding and processing functions
This commit is contained in:
Steve Karg
2026-01-15 14:50:59 -06:00
committed by GitHub
parent 21daea9eb4
commit 5802bd0ecc
18 changed files with 921 additions and 212 deletions
+5 -2
View File
@@ -721,9 +721,12 @@ static bool Channel_Write_Members(
"channel[%lu].Channel_Write_Member[%u] coerced\n",
(unsigned long)object_instance, m);
if (Write_Property_Internal_Callback) {
status = Write_Property_Internal_Callback(&wp_data);
status = write_property_bacnet_array_valid(&wp_data);
if (status) {
wp_data.error_code = ERROR_CODE_SUCCESS;
status = Write_Property_Internal_Callback(&wp_data);
if (status) {
wp_data.error_code = ERROR_CODE_SUCCESS;
}
}
debug_printf(
"channel[%lu].Channel_Write_Member[%u] "
+14 -39
View File
@@ -2339,51 +2339,26 @@ bool Device_Create_Object(BACNET_CREATE_OBJECT_DATA *data)
{
bool status = false;
struct object_functions *pObject = NULL;
uint32_t object_instance;
bool object_exists = false;
bool object_supported = false;
pObject = Device_Object_Functions_Find(data->object_type);
if (pObject != NULL) {
if (!pObject->Object_Create) {
/* The device supports the object type and may have
sufficient space, but does not support the creation of the
object for some other reason.*/
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_DYNAMIC_CREATION_NOT_SUPPORTED;
} else if (
pObject->Object_Valid_Instance &&
if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(data->object_instance)) {
/* The object being created already exists */
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_OBJECT_IDENTIFIER_ALREADY_EXISTS;
} else {
if (data->list_of_initial_values) {
/* FIXME: add support for writing to list of initial values */
/* A property specified by the Property_Identifier in the
List of Initial Values does not support initialization
during the CreateObject service. */
data->first_failed_element_number = 1;
data->error_class = ERROR_CLASS_PROPERTY;
data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
/* and the object shall not be created */
} else {
object_instance = pObject->Object_Create(data->object_instance);
if (object_instance == BACNET_MAX_INSTANCE) {
/* The device cannot allocate the space needed
for the new object.*/
data->error_class = ERROR_CLASS_RESOURCES;
data->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT;
} else {
/* required by ACK */
data->object_instance = object_instance;
Device_Inc_Database_Revision();
status = true;
}
}
object_exists = true;
}
object_supported = true;
status = create_object_process(
data, object_supported, object_exists, pObject->Object_Create,
pObject->Object_Delete, pObject->Object_Write_Property);
} else {
/* The device does not support the specified object type. */
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE;
/* fill in the error values */
status = create_object_process(
data, object_supported, object_exists, NULL, NULL, NULL);
}
if (status) {
Device_Inc_Database_Revision();
}
return status;
+5 -2
View File
@@ -2051,9 +2051,12 @@ static bool Loop_Write_Manipulated_Variable(
wp_data.application_data_len =
encode_application_real(wp_data.application_data, value);
if (Write_Property_Internal_Callback) {
status = Write_Property_Internal_Callback(&wp_data);
status = write_property_bacnet_array_valid(&wp_data);
if (status) {
wp_data.error_code = ERROR_CODE_SUCCESS;
status = Write_Property_Internal_Callback(&wp_data);
if (status) {
wp_data.error_code = ERROR_CODE_SUCCESS;
}
}
}
}
+5 -2
View File
@@ -496,9 +496,12 @@ static bool Timer_Write_Members(
wp_data.application_data, sizeof(wp_data.application_data),
value);
if (Write_Property_Internal_Callback) {
status = Write_Property_Internal_Callback(&wp_data);
status = write_property_bacnet_array_valid(&wp_data);
if (status) {
wp_data.error_code = ERROR_CODE_SUCCESS;
status = Write_Property_Internal_Callback(&wp_data);
if (status) {
wp_data.error_code = ERROR_CODE_SUCCESS;
}
}
}
}
+14 -39
View File
@@ -2763,51 +2763,26 @@ bool Device_Create_Object(BACNET_CREATE_OBJECT_DATA *data)
{
bool status = false;
struct object_functions *pObject = NULL;
uint32_t object_instance;
bool object_exists = false;
bool object_supported = false;
pObject = Device_Object_Functions_Find(data->object_type);
if (pObject != NULL) {
if (!pObject->Object_Create) {
/* The device supports the object type and may have
sufficient space, but does not support the creation of the
object for some other reason.*/
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_DYNAMIC_CREATION_NOT_SUPPORTED;
} else if (
pObject->Object_Valid_Instance &&
if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(data->object_instance)) {
/* The object being created already exists */
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_OBJECT_IDENTIFIER_ALREADY_EXISTS;
} else {
if (data->list_of_initial_values) {
/* FIXME: add support for writing to list of initial values */
/* A property specified by the Property_Identifier in the
List of Initial Values does not support initialization
during the CreateObject service. */
data->first_failed_element_number = 1;
data->error_class = ERROR_CLASS_PROPERTY;
data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
/* and the object shall not be created */
} else {
object_instance = pObject->Object_Create(data->object_instance);
if (object_instance == BACNET_MAX_INSTANCE) {
/* The device cannot allocate the space needed
for the new object.*/
data->error_class = ERROR_CLASS_RESOURCES;
data->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT;
} else {
/* required by ACK */
data->object_instance = object_instance;
Device_Inc_Database_Revision();
status = true;
}
}
object_exists = true;
}
object_supported = true;
status = create_object_process(
data, object_supported, object_exists, pObject->Object_Create,
pObject->Object_Delete, pObject->Object_Write_Property);
} else {
/* The device does not support the specified object type. */
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE;
/* fill in the error values */
status = create_object_process(
data, object_supported, object_exists, NULL, NULL, NULL);
}
if (status) {
Device_Inc_Database_Revision();
}
return status;
+1 -1
View File
@@ -34,7 +34,7 @@
* - an Abort if
* - the message is segmented
* - if decoding fails
* - a SimpleACK if Device_Create_Object() succeeds
* - a ComplexACK if Device_Create_Object() succeeds
* - an Error if Device_Create_Object() fails
*
* @param service_request [in] The contents of the service request.
+13 -1
View File
@@ -56,6 +56,8 @@ uint8_t Send_Create_Object_Request_Data(
BACNET_CREATE_OBJECT_DATA data = { 0 };
BACNET_NPDU_DATA npdu_data = { 0 };
uint8_t service = SERVICE_CONFIRMED_CREATE_OBJECT;
BACNET_PROPERTY_VALUE *value;
uint8_t *application_data = NULL;
if (!dcc_communication_enabled()) {
return 0;
@@ -81,7 +83,17 @@ uint8_t Send_Create_Object_Request_Data(
/* encode the APDU service */
data.object_type = object_type;
data.object_instance = object_instance;
data.list_of_initial_values = values;
data.application_data_len = 0;
value = values;
#if BACNET_CREATE_OBJECT_LIST_VALUES_ENABLED
application_data = data.application_data;
#endif
while (value) {
len = create_object_encode_initial_value(
application_data, data.application_data_len, value);
data.application_data_len += len;
value = value->next;
}
/* get the length of the APDU */
len = create_object_encode_service_request(NULL, &data);
pdu_len += len;