Hello Simone,

Am 03.06.19 um 20:14 schrieb Simone Comari:
Hi all,

Following Wilhelm's suggestion, I had a look at their libraries. The premises seem promising, meaning that it looks like it is exactly what we need. Only issue is that I really had some troubles understanding how I can integrate it into my program. As far as I understood, we should split our application into two separate programs, being one the server, running our ethercat-capable real time process, and the other the client, running our GUI. Then these processes should be started separately and would communicate through some (internal?) network protocol. Is it somewhat correct?

Yes, perfectly summarized. The advantage is, that the protocol is multiclient capable. It is is human readable; based on xml but efficient to also transport large amount of streaming data and tools from the EtherLab-suite can be used.


I will definitely have to look deeper into it, but a first question already comes to my mind: how do I enhance a real time task on the server side with pdserv? I'm using this <https://github.com/UNIBO-GRABLab/grab_common/tree/master/libgrabrt> library (our own) to start our real-time thread at the moment.

As I said a good pdserv example is missing, but work on this is on progress.

Please find here a snippet of our "boilerplate code" meanwhile:
(sorry I had to delete some pieces. It won't run like this, but I think you will get the idea behind it.)

Task.h
/****************************************************************************/

#ifndef TaskH
#define TaskH

/****************************************************************************/

#include <pdserv.h>

#include <functional>

/****************************************************************************/

class Task
{
    public:
        Task(double sampleTime, const std::string &configPath);

        struct pdserv *pdserv() const { return _pdserv; }
        struct pdtask *pdtask() const { return _pdtask; }

        typedef std::function<void(
                const struct timespec &worldTime
                )> UpdateFunction;

        int run(UpdateFunction);

    private:
        const double _sampleTime;
        ec_master_t * const _master;
        struct pdserv * const _pdserv;
        struct pdtask * const _pdtask;
};

/****************************************************************************/

#endif

/****************************************************************************/



Task.cpp

/****************************************************************************/

#include "Task.h"

#include <ecrt.h>

#include <iostream>
#include <sstream>
using namespace std;

#include <unistd.h> // sleep()
#include <string.h> // memset()
#include <sys/mman.h> // mlockall()
#include <signal.h> // sigaction()

/****************************************************************************/

/** The maximum stack size which is guranteed safe to access without faulting.
 */
#define MAX_SAFE_STACK (8 * 1024)

/** Nanoseconds per second. */
#define NSEC_PER_SEC (1000000000)

/** Timespec difference */
#define DIFF_NS(A, B) (((B).tv_sec - (A).tv_sec) * NSEC_PER_SEC + \
        (B).tv_nsec - (A).tv_nsec)

/** Timespec to ns. */
#define TIMESPEC2NS(T) ((uint64_t) (T).tv_sec * NSEC_PER_SEC + (T).tv_nsec)

/****************************************************************************/

static bool run = true;

/****************************************************************************/

void stack_prefault(void)
{
    unsigned char dummy[MAX_SAFE_STACK];
    memset(dummy, 0, MAX_SAFE_STACK);
}

/****************************************************************************/

void signal_handler(int signum)
{
    cerr << "Received signal " << signum << "." << endl;

    switch (signum) {
        case SIGINT:
        case SIGTERM:
            run = false;
            break;

        default:
            break;
    }
}

/****************************************************************************/

Task::Task(
        double sampleTime,
        const std::string &configPath
        ):
    _sampleTime(sampleTime),
    _master(ecrt_request_master(0)),


    _pdtask(pdserv_create_task(_pdserv, _sampleTime, "main"))
{
    struct sigaction sa;
    memset(&sa, 0x00, sizeof(sa));
    sa.sa_handler = signal_handler;
    sigaction(SIGTERM, &sa, NULL);
    sigaction(SIGINT, &sa, NULL);

}

/****************************************************************************/

int Task::run(UpdateFunction update)
{
    if (_master) {
        if (ecrt_master_activate(_master)) {
            stringstream err;
            err << "Failed to activate master.";
            throw std::runtime_error(err.str());
        }
    }

    /* Finalize PdServ */

    if (pdserv_prepare(_pdserv)) {
        cerr << "Failed to prepare PdServ." << endl;
        return 1;
    }

    /* Set priority */

    struct sched_param param = {};
    param.sched_priority = sched_get_priority_max(SCHED_FIFO);

    if (sched_setscheduler(0, SCHED_FIFO, &param) == -1) {
        cerr << "sched_setscheduler() failed: " << strerror(errno) << endl;
    }

    /* Lock memory */

    if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
        cerr << "mlockall() failed: " << strerror(errno) << endl;
    }

    stack_prefault();

    struct timespec wakeupTime, startTime, lastStartTime = {}, worldTime,
                    endTime;
    clock_gettime(CLOCK_MONOTONIC, &wakeupTime);
    wakeupTime.tv_sec += 1; /* start in future */
    wakeupTime.tv_nsec = 0;

    double cycleTime, execTime = 0.0;

    cout << "Starting cycle with T = " << _sampleTime << " s." << endl;

/* cyclic code **********************************************************/

    while (::run) {
        int ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
                &wakeupTime, NULL);
        if (ret) {
            cerr << "clock_nanosleep(): " << strerror(ret) << endl;
            break;
        }

        clock_gettime(CLOCK_MONOTONIC, &startTime);
        clock_gettime(CLOCK_REALTIME, &worldTime);

        cycleTime = DIFF_NS(lastStartTime, startTime) * 1e-9;
        lastStartTime = startTime;

        pdserv_get_parameters(_pdserv, _pdtask, &startTime);

        if (_master) {
            // receive process data
            ecrt_master_receive(_master);
        }


/* function code ****************************************************/

        if (update) {
            update(worldTime);
        }

/* ~function code ***************************************************/


        if (_master) {
            // synchronisation
            struct timespec appTime;
            clock_gettime(CLOCK_MONOTONIC, &appTime);
            ecrt_master_application_time(_master, TIMESPEC2NS(appTime));
            ecrt_master_sync_reference_clock(_master);
            ecrt_master_sync_slave_clocks(_master);

            // send frame(s)
            ecrt_master_send(_master);
        }

        pdserv_update_statistics(_pdtask, execTime, cycleTime, 0);
        pdserv_update(_pdtask, &worldTime);

        wakeupTime.tv_nsec += _sampleTime * NSEC_PER_SEC;
        while (wakeupTime.tv_nsec >= NSEC_PER_SEC) {
            wakeupTime.tv_nsec -= NSEC_PER_SEC;
            wakeupTime.tv_sec++;
        }

        clock_gettime(CLOCK_MONOTONIC, &endTime);
        execTime = (double) DIFF_NS(startTime, endTime) / NSEC_PER_SEC;
    }

/* ~cyclic code *********************************************************/

    pdserv_exit(_pdserv);

    cout << "Exiting." << endl;
    return 0;
}

/****************************************************************************/



Regards Wilhelm


_______________________________________________
etherlab-users mailing list
etherlab-users@etherlab.org
http://lists.etherlab.org/mailman/listinfo/etherlab-users

Reply via email to