High Performance OPC UA Server SDK  1.7.1.383
Creating Jagged Arrays

OPC UA Variants can store scalar values, arrays, multidimensional arrays (aka matrix) and also nested arrays. A nested array is simply an array of type UA_VT_VARIANT, where each elements is again an array. This allows to use different array lengths for each element (hence the name "jagged array") and also different types.

jagged_array.png
Jagged Array Example

The following example shows how to create such an array using ua_variant utility functions.

int create_jagged_array(struct ua_variant *value)
{
/* Create a nested array of variants aka jagged array.
* It is possible in UA to nest arrays, which should not be confused
* with multidimensional arrays.
* Every array element simply contains another array. These can also have
* a different types.
*/
struct ua_variant array[5];
uint32_t ints5[5] = { 1, 2, 3, 4, 5};
uint32_t ints4[4] = { 11, 12, 13, 14 };
uint32_t ints3[3] = { 100, 200, 200};
float floats5[5] = { 1.1, 1.2, 1.3, 1.4, 1.5 };
float floats4[4] = { 4.1, 4.2, 4.3, 4.4 };
unsigned int i;
int ret;
ua_memset(array, 0, sizeof(array));
/* fill inner variants */
ret = ua_variant_set_array(&array[0], UA_VT_INT32, ints5, countof(ints5));
if (ret != 0) goto out;
ret = ua_variant_set_array(&array[1], UA_VT_INT32, ints4, countof(ints4));
if (ret != 0) goto out;
ret = ua_variant_set_array(&array[2], UA_VT_INT32, ints3, countof(ints3));
if (ret != 0) goto out;
ret = ua_variant_set_array(&array[3], UA_VT_FLOAT, floats5, countof(floats5));
if (ret != 0) goto out;
ret = ua_variant_set_array(&array[4], UA_VT_FLOAT, floats4, countof(floats4));
if (ret != 0) goto out;
/* create outer (jagged) array (deep copy) */
ret = ua_variant_set_array(value, UA_VT_VARIANT, array, countof(array));
out:
/* free array */
for (i = 0; i < countof(array); ++i) {
ua_variant_clear(&array[i]);
}
return ret;
}

The downside of the example above are the memory allocations and deep-copy operations, which are not efficient in terms of performance or resource consumption. When you need better efficiency you can make use of ua_variant_attach_array as shown in the next example.

int create_jagged_array2(struct ua_variant *value)
{
/* This works like the function above but avoids a deep copy using
* ua_variant_attach_array.
*/
struct ua_variant *array;
const unsigned int count = 5;
uint32_t ints5[5] = { 1, 2, 3, 4, 5};
uint32_t ints4[4] = { 11, 12, 13, 14 };
uint32_t ints3[3] = { 100, 200, 200};
float floats5[5] = { 1.1, 1.2, 1.3, 1.4, 1.5 };
float floats4[4] = { 4.1, 4.2, 4.3, 4.4 };
unsigned int i;
int ret;
/* create array in IPC heap for attaching to variant */
array = IPC_CALLOC_ARRAY(array, count);
if (array == NULL) return UA_EBADNOMEM;
/* fill inner variants */
ret = ua_variant_set_array(&array[0], UA_VT_INT32, ints5, countof(ints5));
if (ret != 0) goto error;
ret = ua_variant_set_array(&array[1], UA_VT_INT32, ints4, countof(ints4));
if (ret != 0) goto error;
ret = ua_variant_set_array(&array[2], UA_VT_INT32, ints3, countof(ints3));
if (ret != 0) goto error;
ret = ua_variant_set_array(&array[3], UA_VT_FLOAT, floats5, countof(floats5));
if (ret != 0) goto error;
ret = ua_variant_set_array(&array[4], UA_VT_FLOAT, floats4, countof(floats4));
if (ret != 0) goto error;
/* create outer (jagged) array by attaching */
ret = ua_variant_attach_array(value, UA_VT_VARIANT, array, count);
if (ret != 0) goto error;
return 0;
error:
/* free array */
for (i = 0; i < count; ++i) {
ua_variant_clear(&array[i]);
}
ipc_free(array);
return ret;
}

This avoids the deep copy of the ua_variant_set_array call, but still you have the copy operations of the single array variables. It is possible to replace this as well using ua_variant_attach_array function. But this would require to dynamically allocate all the variables (ints5, ints4, ints4, floats5, floats4), which would make the code more complicated.