Data link driversπ
Overviewπ
The VisionAppster data link driver API consists of a set of C functions that provide a generic interface for working with different types of external devices such as digital I/O devices, programmable logic controllers (PLC), distributed control systems (DCS) and SQL databases. Broadly, it can be used as a general way of implementing custom machine-to-machine data link protocols.
This documentation specifies the required functionality for a compatible data link driver. The following sections describe some key concepts. Please refer to the API documentation for further details.
Master and slave operationπ
When working as a master device, a data link driver communicates with a remote device by sending commands through a communication bus such as serial line, digital I/O lines, field bus or Ethernet. This way, it is able to read and write variables on the external device. If a device master must react to changes in the value of a variable in a remote device, it must repeatedly poll the variable.
In slave device mode, the driver works as a server that listens to commands coming from a communication bus. It interprets the commands and either sets or returns the values of local variables.
Handlesπ
The API works with generic handles that are expressed as typeless
pointers (void*
). The actual meaning of the handles is up to the
driver implementation. However, the implementation must ensure that it
has a way of distinguishing handles that represent devices,
variables, and handle lists.
A handle list can hold any number of handles (including zero). On
success, the va_datalink_dev_list()
and
va_datalink_var_list()
functions always return a non-null
handle. To iterate a handle list, the
va_datalink_handle_next()
function is used.
See the va_datalink_dev_create()
and
va_datalink_var_create()
functions for more details.
Variablesπ
The representation of variables is implementation-specific. Thus, the
API just uses handles. The value of a variable can be changed with
va_datalink_var_write()
and retrieved with
va_datalink_var_read()
.
Some data link devices provide a fixed set of variables. For example, a
SQL table has a fixed set of columns that may not be changeable by the
driver. The same applies to the tags provided by a remote PLC slave. In
such a case, the driver may provide a
va_datalink_var_list()
function for listing the
variables.
In master mode, setting a variable pushes it to an external data link device. Similarly, reading a variable makes a read request over the communication bus.
In slave mode, variables are stored locally. Changing the value of a variable has no direct effect on remote masters, but theyβll get the updated value on the next request. Reading a variable returns the last value set either locally or by a master device.
The number of data types supported by the API does not cover all possible data types supported by every imaginable data link device. The driver implementation makes sure the variable values are correctly converted to the types supported by the API. It must take into account things like byte order and overflow handling.
Whenever the value of a variable is passed to an API function, a vararg list (β¦) will be used. The interpretation of the list depends on the type of the variable; variable types are preceded by a type ID. For example:
va_datalink_var_write(var1, va_int32_t_id, 3000);
va_datalink_var_write(var2, va_double_id, 3.141593);
va_datalink_var_write(var3, va_string_id, "COM1");
When reading values with va_datalink_var_read()
, the
argument following a type ID must be a pointer to the type. For example:
int32_t i_value;
va_string* str;
va_datalink_var_read(var1, va_int32_t_id, &i_value);
va_datalink_var_read(var2, datalink_string, &str);
va_string_free(str);
Configuration valuesπ
Devices and variables have a number of configuration values that can be
used to control their behavior. Configuration variables can be queried
using the va_datalink_config_get()
function and set with
the va_datalink_var_write()
function.
Device configuration valuesπ
Configuration values may be needed in order to successfully connect to a data link device. While it is possible to encode these properties to the device address, using configuration values should be the preferred choice as it lets the VisionAppster Engine to store device-specific parameters in its device database and thus change them without modifying the app that uses the data link driver.
The following table lists supported configuration values.
Name |
Type |
Writab le |
Description |
---|---|---|---|
address |
va_string* |
No |
The address of the device. |
flags |
int32_t |
No |
A bit mask of device capabilities (` <:cpp:type:va_d atalink_dev_flag>` __) |
mode |
int32_t |
Y/N |
The operation mode. Writable if driver supports both modes. |
timeout |
int32_t |
Yes |
The maximum number of milliseconds any operation (connection attempt, write or read) can last. |
Variable configuration valuesπ
In general, variable configuration values are read-only and defined
either by the data link device or at creation time
(va_datalink_var_create()
).
The following table lists supported variable configuration values. Optional values are in cursive.
Name |
Type |
Writa ble |
Description |
---|---|---|---|
name |
va_string* |
No |
The name of the variable. |
type |
int32_t |
No |
The type of the
variable. One of
|
min |
int32_t/double |
No |
Minimum value of a range-limited numeric variable. |
max |
int32_t/double |
No |
Maximum value of a range-limited numeric variable. |
count |
int32_t |
No |
Fixed number of items in an array variable. Required if the variable is an array. |
Error handlingπ
All functions in the data link API return a status code or a value whose validity can be checked. If the return value is a pointer, NULL indicates a failure, any other value means success. If the returned value is a status code, one indicates success and zero indicates failure.
To indicate the reason for a failure, the driver implementation stores a
device-specific failure code internally. The user of the driver can get
a textual representation of the error by calling
va_datalink_error()
. The implementation can use for
example errno
constants and pass them to strerror_r()
in the
implementation of va_datalink_error()
.
Thread-safetyπ
The functions in the data link driver do not need to be thread-safe, but they need to be re-entrant. In other words, the driver implementation must not hold state in global variables, but only in the created handles.
Implementing a driverπ
A valid driver must implement all non-optional functions in the API. The VisionAppster Engine refuses to load a driver that is missing any of them. Below is an example of an incomplete driver that implements just two of the API functions:
#include <va_datalink.h>
// Required for all plugins dynamically loaded by the VisionAppster Engine.
VA_IMPLEMENT_PLUGIN("my.example.driver", "1.0.0", datalink_driver)
// Gives the driver a unique name in the context of the component.
VA_REGISTER_DATALINK_DRIVER("MyDriver")
typedef enum
{
dev_handle,
var_handle,
list_handle
} dummy_handle_type;
typedef struct
{
dummy_handle_type type;
char name[256];
} dummy_device;
void* va_datalink_dev_create(const va_string* device_address)
{
dummy_device* dev = (dummy_device*)malloc(sizeof(dummy_device));
if (dev)
{
dev->type = dev_handle;
va_string_to_utf8(device_address, dev->name, 256);
dev->name[255] = 0;
}
return dev;
}
int32_t va_datalink_handle_destroy(void* handle)
{
if (handle && *(dummy_handle_type*)handle == dev_handle)
printf("Destroying device handle.\n");
free(handle);
return 1;
}
// Rest of required functions here
The interface is defined in C, which makes it relatively safe to use
with many different compilers. Using
va-cross is however suggested. The
driver needs to be compiled into a shared library and renamed so that it
has a datalink
filename suffix, for example mydriver.datalink
.
To install the driver, you have three options:
System-wide installation: copy the plugin to the
plugins/datalink/
directory under the VisionAppster installation directoryUser-scope installation: copy the plugin to the
VisionAppster/plugins/datalink/
directory under your home directory.Install from a component package: put the plugin into a component package and install.
In the last case, you must ensure that the component ID and version
given as parameters to the VA_IMPLEMENT_PLUGIN
macro match those of
the component you build.
If you are going to distribute your driver through the VisionAppster
Store and want to make sure the driver cannot be loaded without a valid
license, replace the VA_IMPLEMENT_PLUGIN
macro with
VA_IMPLEMENT_LICENSED_PLUGIN
. See
va_plugin.h for details.
Using the driverπ
When the driver has been successfully installed, all devices listed by it will be available to the VisionAppster Engine. In the Builder, youβll see the detected devices in tools such as Output external data.