Skip to content

5 Service User Example

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

A Complete Service User

The previous pages of this tutorial explained how services work and parts of the Skinware API related to service users. This page will build upon that knowledge to create a complete service user which attaches to the service provider in this page and prints out its results.

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 attach to the service. In this program, the skin sensor data themselves are not read, so the application only needs to attach to the sample service provider.

The sample service provider came with a header file that declared the type of the service results. Here is that header file (service.h) again:

#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 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 user:

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

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

/* read from sample service */
static unsigned int print_frequency = 2;

URT_MODULE_PARAM_START()
URT_MODULE_PARAM(print_frequency, uint, "Frequency with which data is dumped (default: 2)")
URT_MODULE_PARAM_END()

struct data
{
    struct skin *skin;
    struct skin_reader *service_user;
    urt_task *read_request_task;
};

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)
{
    urt_task_delete(d->read_request_task);
    /* clean up Skinware if initialized */
    skin_free(d->skin);
    /* clean up URT */
    urt_exit();
}

static void service_read(struct skin_reader *reader, void *mem, size_t size, void *user_data)
{
    struct service_data *sdata = mem;

    urt_out("Maximum sensor response: %u\n", sdata->max_response);
    if (sdata->active_patch_count == 0)
        urt_out("No active patches\n");
    else
    {
        urt_out("Maximum sensor responses from active patches: {");
        for (skin_patch_id i = 0; i < sdata->active_patch_count; ++i)
            urt_out_cont("%s%u", i?", ":"", sdata->active_patch_max_response[i]);
        urt_out_cont("}\n");
    }
}

/*
 * the read_request task soft real-time thread is only spawned if the
 * reader is sporadic and requires requests to be sent to it
 */
static void read_request_task(urt_task *task, void *user_data)
{
    struct data *d = user_data;

    while (!interrupted)
    {
        skin_reader_request(d->service_user);
        urt_task_wait_period(task);
    }
}

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;

    if (print_frequency > 0)
        period = 1000000000 / print_frequency;
    else
        print_frequency = 1;

    urt_task_attr sattr = {
        .period = period,
    };
    struct skin_reader_callbacks sucb = {
        .read = service_read,
        .user_data = d,
    };

    if (period == 0)
    {
        /* create a soft real-time task from which skin_request can be called */
        d->read_request_task = urt_task_new(read_request_task, d, &(urt_task_attr){
                                                                       .period = 1000000000 / print_frequency,
                                                                       .soft = true,
                                                                   });
        if (d->read_request_task == NULL)
        {
            urt_err("Could not create task to send requests to sporadic reader\n");
            goto exit_fail;
        }
        urt_task_start(d->read_request_task);
    }

    while (!interrupted)
    {
        /* if the service is not active, detach from it */
        if (d->service_user != NULL && !skin_reader_is_active(d->service_user))
        {
            skin_service_detach(d->service_user);
            d->service_user = NULL;
        }

        /* if the service is not attached to, try to attach to it */
        if (d->service_user == NULL)
        {
            d->service_user = skin_service_attach(d->skin,
                                                  &(struct skin_reader_attr){
                                                      .name = "SRV",
                                                   },
                                                  &sattr, &sucb);
            if (d->service_user != NULL)
                skin_reader_resume(d->service_user);
        }

        urt_sleep(period);
    }

exit_fail:
    done = 1;
}

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

This application tries to connect to the service identified by the name "SRV". It will simply periodically retry to connect to the service if it cannot. If the service goes inactive, it will also detach from it and retry later. It does not read sensor data from the driver directly since it relies on the service to be provided with processed data. The application allows the reader to be either periodic or sporadic. If the reader is sporadic, it needs to be requested to provide data. In this example, a read request is sent to the reader every 1 second. Since a call to skin_reader_request() requires real-time context, similar to the user application in the user applications tutorial, a soft real-time task is created in case the reader is sporadic, just for this action.

The service reader automatically calls the service_read() function when new data is available. That function then prints out the service results, which in this case was the highest sensor response, as well as highest sensor responses in what the service provider had considered active.

Let's build and execute this service user:

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

The application is now running, but there is no service to attach to, so we don't see anything happening. Take [[the corresponding service provider | 4-Service-Provider-Example]] 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 driver running, so there is no data at all. If you use the skin_info tool, you could see that the writer now has one reader attached to it:

$ skin_info

Take the emulation driver in the drivers tutorial and run it as well. The service provider restarts itself as the skin changes and the service user reattaches to the service provider. You should now be able to see some results getting printed out.

Hit CTRL+C to close the applications and party as you have mastered Skinware.


Next: See other tutorials.

Clone this wiki locally