[etherlab-users] R: R: System randomly freezes in multi-thread Qt application with a RT process

Dr.-Ing. Wilhelm Hagemeister hm at igh.de
Wed Jun 5 10:58:14 CEST 2019


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




More information about the etherlab-users mailing list