Skip to content
Shahbaz Youssefi edited this page May 27, 2015 · 2 revisions

A Complete User

The previous pages of this tutorial explained the facilities provided to user applications and parts of the Skinware API related to users. This page will build upon that knowledge to create a complete user application which periodically prints out sensor data.

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 start using the skin.

#include <skin.h>

URT_MODULE_LICENSE("GPL");
URT_MODULE_AUTHOR("Shahbaz Youssefi");
URT_MODULE_DESCRIPTION("Sample emulation driver\n");

/* read from robot skin */
static unsigned int acq_frequency = 10;
static unsigned int print_frequency = 2;
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(print_frequency, uint, "Frequency with which data is dumped (default: 2)")
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;
    urt_task *print_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)
{
    /* clean up Skinware if initialized */
    skin_free(d->skin);
    /* clean up URT */
    urt_exit();
}

static int print_sensor_response(struct skin_sensor *s, void *d)
{
    bool *first = d;

    urt_out_cont("%s%u", *first?"":", ", skin_sensor_get_response(s));

    *first = false;
    return SKIN_CALLBACK_CONTINUE;
}

static void print_task(urt_task *task, void *user_data)
{
    struct data *d = user_data;

    while (!interrupted)
    {
        /* if sporadic, then request for the data */
        if (!soft_acq && acq_frequency == 0)
            skin_request(d->skin);

        if (skin_sensor_count(d->skin) == 0)
            urt_out("No sensors available\n");
        else
        {
            bool first = true;

            urt_out("Sensor responses: {");
            skin_for_each_sensor(d->skin, print_sensor_response, &first);
            urt_out_cont("}\n");
        }

        urt_task_wait_period(task);
    }
}

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

    /* sanity checks */
    if (print_frequency == 0)
        print_frequency = 1;

    /* 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 print_period = 1000000000 / print_frequency;

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

    urt_task_attr tattr = {
        .period = period,
        .soft = soft_acq,
    };
    urt_task_attr pattr = {
        .period = print_period,
        .soft = true,
    };

    /* create a soft real-time task from which skin_request can be called */
    d->print_task = urt_task_new(print_task, d, &pattr);
    if (d->print_task == NULL)
    {
        urt_err("Could not create print task\n");
        goto exit_fail;
    }
    urt_task_start(d->print_task);

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

        urt_sleep(print_period);
    }

exit_fail:
    done = 1;
}

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

This program acquires data in periodic, sporadic or soft mode from Skinware, which is configurable from command line. Let's just use the default parameters for now. In the body() of the program, this user application periodically calls skin_update() to attach to new drivers or detach from the ones that no longer exist. The body() also spawns a soft real-time thread for the printing task. The reason for this is that in the case of sporadic readers, the printing task requires to call skin_request(), which requires real-time context. The code that prints out sensor responses is similar to the example in the previous page in this tutorial.

Advanced Tip

urt_out() and urt_err() can be used for output, printf-style. They write to stdout and stderr in user-space and to the kernel logs in kernel space. In debug mode, which is the default unless you defined NDEBUG, they also prepend the file and line where the log originates from. urt_out_cont() and urt_err_cont() can be used to make sure this extra information is not printed. In the example above, in each line, the first output is written with urt_out() and the rest of the line is output with urt_out_cont(). The cont suffix is short for continue.

These macros also output a prefix to the log, so that multiple users of URT in the same application can have distinguishable logs. If you define URT_LOG_PREFIX before including <skin.h> to some string, then your prefix would be used instead of the default. For example:

#define URT_LOG_PREFIX "my app: "
#include <skin.h>

Let's build and execute this user application:

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

The application is now running, but there is no driver to attach to, so we keep getting messages that tell us just that.

In another terminal, you can try to run a driver, and this application would automatically attach to it. You can take a sample emulation driver from the drivers tutorial and run it.

You should now be able to see the sensor responses periodically dumped on the screen. Not a pretty sight.

If you use the skin_info tool, you could see that your driver now has one user:

$ skin_info

Hit CTRL+C to close the application. The same for the driver. The order doesn't matter.


Next: See other tutorials.

Clone this wiki locally