Skip to content

4 Service Provider Example

Shahbaz Youssefi edited this page Jun 3, 2015 · 2 revisions

A Complete Service Provider

The previous pages of this tutorial explained how services work and parts of the Skinware API related to service providers. This page will build upon that knowledge to create a complete service provider which provides processed data from the robot skin.

First, let's create a skeleton for the program. This skeleton would be the same for drivers, user applications and services, so please take a look at this page describing this common skeleton.

Once you are comfortable with the skeleton program, let's go ahead and add a service. The service data could be anything the user wants, so it is the responsibility of the service provider to let the user know how to interpret the data. Let's do this by providing a header file that the service provider shares with the service user. Call this service.h:

#ifndef SAMPLE_SERVICE_H
#define SAMPLE_SERVICE_H

#include <skin.h>

struct service_data
{
    skin_sensor_response max_response;

    skin_patch_size active_patch_count;
    skin_sensor_response active_patch_max_response[];
};

#endif

According to this example header, the service data consist of the maximum response in the whole skin, as well as maximum responses from patches that the service considers active. Here is the sample service provider:

#include <skin.h>
#include "service.h"

URT_MODULE_LICENSE("GPL");
URT_MODULE_AUTHOR("Shahbaz Youssefi");
URT_MODULE_DESCRIPTION("Sample service provider\n");

/* provide processed data */
static unsigned int acq_frequency = 10;
static unsigned int service_frequency = 5;
static bool soft_acq = false;

URT_MODULE_PARAM_START()
URT_MODULE_PARAM(acq_frequency, uint, "Frequency with which data is acquired (0 for sporadic) (default: 10)")
URT_MODULE_PARAM(service_frequency, uint, "Frequency with which processed data is provided (default: 5)")
URT_MODULE_PARAM(soft_acq, bool, "If set, acquisition is done in soft real-time (overrides acq_frequency) (default: no)")
URT_MODULE_PARAM_END()

struct data
{
    struct skin *skin;
    struct skin_writer *service;
};

static int start(struct data *d);
static void body(struct data *d);
static void stop(struct data *d);

URT_GLUE(start, body, stop, struct data, interrupted, done)

static void cleanup(struct data *d)
{
    /* clean up Skinware if initialized */
    skin_free(d->skin);
    /* clean up URT */
    urt_exit();
}

static int find_max_response(struct skin_sensor *s, void *d)
{
    skin_sensor_response *max_response = d;
    skin_sensor_response cur = skin_sensor_get_response(s);

    if (cur > *max_response)
        *max_response = cur;

    return SKIN_CALLBACK_CONTINUE;
}

static int mark_active_patch(struct skin_patch *m, void *d)
{
    struct service_data *sdata = d;
    skin_sensor_response max_response = 0;

    /* find the maximum sensor response in this patch */
    skin_patch_for_each_sensor(m, find_max_response, &max_response);

    if (max_response > sdata->max_response)
        sdata->max_response = max_response;

    /* if the maximum response is higher than a threshold, mark the patch as active */
    if (max_response > 30000)
        sdata->active_patch_max_response[sdata->active_patch_count++] = max_response;

    return SKIN_CALLBACK_CONTINUE;
}

static int service_write(struct skin_writer *writer, void *mem, size_t size, void *user_data)
{
    struct service_data *sdata = mem;
    struct data *d = user_data;

    sdata->max_response = 0;
    sdata->active_patch_count = 0;

    /* find active patches and in the process also find the response of the most active sensor */
    skin_for_each_patch(d->skin, mark_active_patch, sdata);

    return 0;
}

static int start(struct data *d)
{
    *d = (struct data){0};

    /* start up URT */
    if (urt_init())
        return EXIT_FAILURE;

    /* start up Skinware */
    d->skin = skin_init();
    if (d->skin == NULL)
        goto exit_fail;

    return 0;
exit_fail:
    cleanup(d);
    return EXIT_FAILURE;
}

static void body(struct data *d)
{
    urt_time period = 0;
    urt_time service_period = 0;
    bool warned = false;

    if (!soft_acq && acq_frequency > 0)
        period = 1000000000 / acq_frequency;

    if (service_frequency > 0)
        service_period = 1000000000 / service_frequency;

    urt_task_attr tattr = {
        .period = period,
        .soft = soft_acq,
    };
    urt_task_attr sattr = {
        .period = service_period,
    };
    struct skin_writer_callbacks scb = {
        .write = service_write,
        .user_data = d,
    };

    while (!interrupted)
    {
        /* attach to new drivers, and detach from the ones that are gone */
        bool changed = skin_update(d->skin, &tattr) == 0;
        skin_resume(d->skin);

        /* if skin is changed, try to restart the service */
        if (changed)
        {
            if (d->service)
            {
                skin_service_remove(d->service);
                d->service = NULL;
                warned = false;
            }
        }

        /* if service is stopped, try to start it */
        if (d->service == NULL)
        {
            skin_patch_size pc = skin_patch_count(d->skin);
            d->service = skin_service_add(d->skin,
                                          &(struct skin_writer_attr){
                                               .buffer_size = sizeof(struct service_data)
                                                            + sizeof(skin_sensor_response[pc]),
                                               .name = "SRV",
                                           },
                                          &sattr, &scb);
            if (d->service)
                urt_out("Service is up\n");
            else if (!warned)
                urt_err("Service name 'SRV' is busy.  Waiting...\n");
            warned = true;

            if (d->service)
                skin_writer_resume(d->service);
        }

        urt_sleep(service_period);
    }

    done = 1;
}

static void stop(struct data *d)
{
    cleanup(d);
}

This application is similar to the user application in the user applications tutorial, in which skin_update() is used to automatically attach to new drivers and detach from the ones that are inactive. Every time skin_update() indicates that there is a change in the robot skin, the service restarts itself. Once it tries starting again, it may fail due to service users not yet having detached from the service. In this case, the application gives out a warning and retries.

The service writer automatically calls the service_write() function as necessary. That function then goes about calculating the sensor data, which in this case was the highest sensor response, as well as highest sensor responses in what the service considers active.

Let's build and execute this service:

$ gcc -std=gnu11 -c $(urt-config --user-cflags) main.c
$ gcc -std=gnu11 -o test_service_provider main.o -lskin -lurt $(urt-config --user-ldflags)
$ ./test_service_provider

The service is now running, but there is no service user attached to it, so we can't see what it's actually doing. You can see some information about the service provider using the skin_info tool, though:

$ skin_info

Take the corresponding service user and run it. You would see now that there are actually no active patches and the highest sensor response is zero. This is because there is no data to begin with!

So let's also run the emulation driver in the drivers tutorial. The service provider restarts itself as the skin changes, which is evident by the "Service is up" message. The service user reattaches to the service as well and it should now start printing out stuff.

Congratulations! Hit CTRL+C to close the applications.


Next: See other tutorials.

Clone this wiki locally