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
_______________________________________________
etherlab-users mailing list
etherlab-users@etherlab.org
http://lists.etherlab.org/mailman/listinfo/etherlab-users