PDI 1.7.1

the PDI data interface

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;
}
PDI_status_t PDI_event(const char *event)
Triggers a PDI "event".
PDI_status_t PDI_init(PC_tree_t conf)
Initializes PDI.
PDI_status_t PDI_finalize(void)
Finalizes PDI.

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;
}
PDI_status_t PDI_release(const char *name)
Releases ownership of a data shared with PDI.
PDI_status_t PDI_share(const char *name, void *data, PDI_inout_t access)
Shares some data with PDI.
PDI_status_t PDI_access(const char *name, void **buffer, PDI_inout_t inout)
Requests for PDI to access a data buffer.
PDI_status_t PDI_reclaim(const char *name)
Reclaims ownership of a data buffer shared with PDI.
@ PDI_OUT
data transfer from the main code to PDI
Definition: pdi.h:187
@ PDI_IN
data tranfer from PDI to the main code
Definition: pdi.h:185

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;
}
PDI_status_t PDI_expose(const char *name, void *data, PDI_inout_t access)
Shortly exposes some data to PDI.

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;
}
PDI_status_t PDI_multi_expose(const char *event_name, const char *name, void *data, PDI_inout_t access,...)
Performs multiple exposes at once.

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:

key value
"logging" (optional) a logging
"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:

key value
".*" (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:

key value
".*" (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:

key value
".*" (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:

key value
".*" (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}