[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, ¶m) == -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