PDI 0.6.1

Data exchange made easy

The user-code plugin

The user-code plugin enables one to call a user-defined function when a specified event occur or certain data becomes available.

Important notes

  • Make sure to compile your program with Wl,--export-dynamic or -rdynamic flag (in CMake set ENABLE_EXPORTS to TRUE using set_target_properties command) in order to generate necessary symbols.
  • Make sure you use the proper access rights in your function in PDI_access (PDI_IN for reading, PDI_OUT for writing).
  • Descriptor aliases enables one to use different descriptors without the need to recompile the code.

Dependencies between the code and the specification tree

To ensure proper work of the user-code plugin, there are several conventions to follow in the application code and the specification tree.

First, each function name in specification tree must be a valid function name (watch out for name mangling).

Second, these functions can not take any arguments or return any value (i.e. their type must be void(void)). Use descriptors for passing input/output variables instead.

Third, function's symbols must be exported to make them accessible by user-code plugin. To do this, compile your program as described in Important notes.

Use examples

This section shows a simple examples with the use of a user-code plugin.

Hello world!

First, we will call simple function without the use of descriptors to print "Hello world!" on "print" event.

hello_world.c:

#include <stdio.h>
#include <pdi.h>
void hello_world(void)
{
printf("Hello world!\n");
}
int main(int argc, char* argv[])
{
PC_tree_t conf = PC_parse_path("hello_world.yml");
PDI_init(conf);
PDI_event("print");
return 0;
}

hello_world.yml:

plugins:
user_code:
on_event:
print:
hello_world: {}

output:

[PDI][User-code][13:40:55] *** info: Plugin loaded successfully
[PDI][13:40:55] *** info: Initialization successful
Hello world!
[PDI][13:40:55] *** info: Finalization
[PDI][User-code][13:40:55] *** info: Closing plugin

Handling input

Now we will pass some input data to the function.

print_number.c:

#include <stdio.h>
#include <pdi.h>
void print_number(void)
{
void* input; PDI_access("input", &input, PDI_IN);
printf("I've got number %d.\n", *((int*)input));
PDI_release("input");
}
int main(int argc, char* argv[])
{
PC_tree_t conf = PC_parse_path("print_number.yml");
PDI_init(conf);
int number = 42;
PDI_share("number", &number, PDI_OUT);
PDI_event("print");
PDI_reclaim("number");
return 0;
}

print_number.yml:

data:
number: int
plugins:
user_code:
on_event:
print:
print_number: {input: $number}

output:

[PDI][User-code][13:49:30] *** info: Plugin loaded successfully
[PDI][13:49:30] *** info: Initialization successful
I've got number 42.
[PDI][13:49:30] *** info: Finalization
[PDI][User-code][13:49:30] *** info: Closing plugin

We can simplify this example by using on_data to print value of number when it is shared to PDI.

print_number.c:

#include <stdio.h>
#include <pdi.h>
void print_number(void)
{
void* input; PDI_access("input", &input, PDI_IN);
printf("I've got number %d.\n", *((int*)input));
PDI_release("input");
}
int main(int argc, char* argv[])
{
PC_tree_t conf = PC_parse_path("print_number.yml");
PDI_init(conf);
int number = 42;
PDI_expose("number", &number, PDI_OUT);
//PDI_event is no longer necessary and PDI_share/PDI_reclaim can be simplified to PDI_expose
return 0;
}

print_number.yml:

data:
number: int
plugins:
user_code:
on_data:
number:
print_number: {input: $number}

output does not change:

[PDI][User-code][13:52:15] *** info: Plugin loaded successfully
[PDI][13:52:15] *** info: Initialization successful
I've got number 42.
[PDI][13:52:15] *** info: Finalization
[PDI][User-code][13:52:15] *** info: Closing plugin

Handling output

Output handling is very similar to the input handling, the only difference are the access rights. In this example we will call add_ten function when number is shared to PDI.

adding_to_number.c:

#include <stdio.h>
#include <pdi.h>
void add_ten(void)
{
void* input; PDI_access("input", &input, PDI_OUT);
*((int*)input) += 10;
PDI_release("input");
}
int main(int argc, char* argv[])
{
PC_tree_t conf = PC_parse_path("adding_to_number.yml");
PDI_init(conf);
int number = 42;
printf("Before expose, number = %d.\n", number);
PDI_expose("number", &number, PDI_IN);
printf("After expose, number = %d.\n", number);
return 0;
}

adding_to_number.yml:

data:
number: int
plugins:
user_code:
on_data:
number:
add_ten: {input: $number}

output:

[PDI][User-code][13:53:51] *** info: Plugin loaded successfully
[PDI][13:53:51] *** info: Initialization successful
Before expose, number = 42.
After expose, number = 52.
[PDI][13:53:51] *** info: Finalization
[PDI][User-code][13:53:51] *** info: Closing plugin

Multiple input/output data

In this example we will use multiple data in function. We will add and multiply two given numbers and return the results on event "calculate".

calculate.c:

#include <stdio.h>
#include <pdi.h>
void sum_and_multiply(void)
{
void* number1; PDI_access("number1", &number1, PDI_IN);
void* number2; PDI_access("number2", &number2, PDI_IN);
void* sum; PDI_access("sum", &sum, PDI_OUT);
void* product; PDI_access("product", &product, PDI_OUT);
*((int*)sum) = *((int*)number1) + *((int*)number2);
*((int*)product) = *((int*)number1) * *((int*)number2);
PDI_release("number1");
PDI_release("number2");
PDI_release("sum");
PDI_release("product");
}
int main(int argc, char* argv[])
{
PC_tree_t conf = PC_parse_path("calculate.yml");
PDI_init(conf);
int foo = 4, bar = 5, res1 = 0, res2 = 0;
printf("Before calculation, foo = %d, bar = %d, res1 = %d, res2 = %d.\n", foo, bar, res1, res2);
PDI_multi_expose("calculate",
"foo", &foo, PDI_OUT,
"bar", &bar, PDI_OUT,
"res1", &res1, PDI_IN,
"res2", &res2, PDI_IN, NULL);
printf("After calculation, foo = %d, bar = %d, res1 = %d, res2 = %d.\n", foo, bar, res1, res2);
return 0;
}

calculate.yml:

data:
foo: int
bar: int
res1: int
res2: int
plugins:
user_code:
on_event:
calculate:
sum_and_multiply: {number1: $foo, number2: $bar, sum: $res1, product: $res2}

output:

[PDI][User-code][13:58:20] *** info: Plugin loaded successfully
[PDI][13:58:20] *** info: Initialization successful
Before calculation, foo = 4, bar = 5, res1 = 0, res2 = 0.
After calculation, foo = 4, bar = 5, res1 = 9, res2 = 20.
[PDI][13:58:20] *** info: Finalization
[PDI][User-code][13:58:20] *** info: Closing plugin

Configuration grammar

The root of user-code plugin is a dictionary that contains the following keys:

keyvalue
"on_data" (optional)a on_data
"on_event" (optional)a on_event
".*" (optional)anything
  • the on_data key specifies the list of descriptors that, when they become available, will cause the specified functions to be called,
  • the on_event key specifies the list of events on which to call the specified functions,
  • additional keys are ignored.

on_data

A on_data is a dictionary that contains the following keys:

keyvalue
".*" (optional)a function_list
  • each key identifies the name of a descriptor, which will trigger specified functions when it becomes available.

on_event

A on_event is a dictionary that contains the following keys:

keyvalue
".*" (optional)a function_list
  • each key identifies the name of an event, which will trigger specified functions when it occurs.

function_list

A function_list is a dictionary that contains the following keys:

keyvalue
".*" (optional)a function_param_list
  • each key identifies the name of a function, which will be called on specified event or data,
  • NOTE: these functions can not take any arguments or return any value (i.e. their type must be void(void)).

function_param_list

A function_param_list is a dictionary that contains the following keys:

keyvalue
".*" (optional)a $-expression referencing a data
  • each key identifies the name of a descriptor alias, which will be available during function execution.

Specification tree example

data:
desc1: int
desc2: float
desc3: double
plugins:
user_code:
on_data:
desc1:
fun1: {in: $desc2, out: $desc3}
desc2:
fun2: {}
fun3: {out: $desc2}
on_event:
event1:
fun2: {}
event2:
fun4: {param1: $desc2, param2: $desc1, param3: $desc3}