Repository navigation
2 User Skinware
In the previous page in this tutorial, some of the facilities provided by Skinware for the end-user were shown. In this page, some of the relevant parts of the API are shown. This is not an exhaustive list of available API.
Before using Skinware, it needs to be set up using skin_init() and in the end, cleaned up using skin_free().
Please see details in this small page.
If the application is interested in a particular driver, it could use the skin_driver_attach() function to attach
only to that driver. In this tutorial however, we will simply attach to all drivers that are possible. This can be
done through the skin_load() function. Here's the prototype:
int skin_load(struct skin *skin, const urt_task_attr *task_attr);This function attaches to any newly introduced driver, so it can be called again every now and then to start acquiring
data from recently attached drivers as well. Before explaining this function, let's also take a look at
skin_update(). This function not only attaches to the new drivers, but also detaches from the ones that have left.
What's more, skin_update() also reattaches to the each driver where the reader is created in a different acquisition
mode as indicated through the task attribute. Here is the prototype of skin_update() and sample usages:
int skin_update(struct skin *skin, const urt_task_attr *task_attr);
/* use periodic readers */
skin_update(skin, &(urt_task_attr){ .period = 1000000000 / acquisition_rate });
/* use sporadic readers */
skin_update(skin, &(urt_task_attr){ .period = 0 });
/* use soft readers */
skin_update(skin, &(urt_task_attr){ .soft = true });The type of reader is thus decided based on its task attributes:
- If
softis set, the reader is a soft reader, - Otherwise if
periodis non-zero, the reader is periodic, - Otherwise the reader is sporadic.
The return value of skin_load() tells whether any new drivers are attached to, and the return value of skin_update()
tells whether any change has been made, i.e., whether there have been attachments, detachments or reattachments.
The readers created when attaching to drivers are started in suspended mode. To actually make the readers run, they need to be resumed:
skin_resume(skin);Skinware uses callbacks to iterate over entities, such as sensors. This is done so the complexities and changes inside
Skinware can be hidden from the end-user. To iterate over all sensors of the skin, the skin_for_each_sensor()
function can be used. The prototype of this function is as follows:
int skin_for_each_sensor(struct skin *skin, skin_callback_sensor callback, void *user_data);The skin_callback_sensor type is a function pointer type with the following signature:
typedef int (*skin_callback_sensor)(struct skin_sensor *s, void *data);The callback thus receives the current sensor being iterated and the user_data parameter passed to
skin_for_each_sensor(). The user_data pointer serves to avoid having to use global variables. The order in which
the sensors are traversed is guaranteed to be the same in each call, unless there has been a change in the skin, that
is for example through a call to skin_load(), skin_update() or other functions that attach to or detach from
drivers.
Here is an example:
static int print_sensor_response(struct skin_sensor *s, void *d)
{
bool *first = d;
printf("%s%u", *first?"":", ", skin_sensor_get_response(s));
*first = false;
return SKIN_CALLBACK_CONTINUE;
}
bool first = true;
printf("Sensor responses: {");
skin_for_each_sensor(skin, print_sensor_response, &first);
printf("}\n");In the above example, the callback prints out the sensor responses one after the other. The user_data given in this
case is a boolean so it can correctly print ", " between the responses, but not before the first one. The return
value of the callback, SKIN_CALLBACK_CONTINUE tells Skinware that it should continue iterating. If the callback
decides to stop the iteration early, for example because it has found the sensor it was looking for, it can return
SKIN_CALLBACK_STOP. The return value of skin_for_each_sensor() would indicate whether this has been the case.
Advanced Tip
The prototype of
skin_for_each_sensoris in fact the following:int skin_for_each_sensor(struct skin *skin, skin_callback_sensor callback, void *user_data, ...);With a bit of macro magic, the last parameter (
user_data) is made optional and defaults toNULL. The same mechanism is used with otherfor_eachfunctions.
To iterate sensors of a particular type, the skin_for_each_sensor_of_type() function can be user. For example:
skin_for_each_sensor_of_type(struct skin *skin, SKIN_SENSOR_TYPE_CYSKIN_TAXEL,
print_sensor_response, &first);The sensor types are arbitrary values assigned to sensors by the driver. Skinware tries to assign fixed values for
known sensor types in known robot skin technologies, only for easier identification of the sensor. The
SKIN_SENSOR_TYPE_* constants refer to these known sensor types.
Next: Build a complete user application.