00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 #include <cstdio>
00038 #include <cstdarg>
00039 #include <getopt.h>
00040 #include <execinfo.h>
00041 #include <csignal>
00042 #include <sys/mman.h>
00043 #include <sys/types.h>
00044 #include <sys/stat.h>
00045 #include <unistd.h>
00046 #include <fcntl.h>
00047 #include <pthread.h>
00048 #include <numeric>
00049
00050 #include "ros_ethercat_model/ros_ethercat.hpp"
00051 #include <controller_manager/controller_manager.h>
00052 #include <std_msgs/Float64.h>
00053 #include <diagnostic_updater/DiagnosticStatusWrapper.h>
00054
00055 using namespace boost::accumulators;
00056 using boost::ptr_vector;
00057 using std::string;
00058 using std::vector;
00059 using std::accumulate;
00060 using realtime_tools::RealtimePublisher;
00061
00062 static struct
00063 {
00064 char *program_;
00065 char *interface_;
00066 char *rosparam_;
00067 bool allow_unprogrammed_;
00068 bool stats_;
00069 double period;
00070 } g_options;
00071
00072 string g_robot_desc;
00073
00074 void Usage(const string &msg = "")
00075 {
00076 fprintf(stderr, "Usage: %s [options]\n", g_options.program_);
00077 fprintf(stderr, " Available options\n");
00078 fprintf(stderr, " -i, --interface <interface> Connect to EtherCAT devices on this interface\n");
00079 fprintf(stderr, " -p, --period RT loop period in msec\n");
00080 fprintf(stderr, " -s, --stats Publish statistics on the RT loop jitter on \"ros_ros_ethercat_eml/realtime\" in seconds\n");
00081 fprintf(stderr, " -r, --rosparam <param> Load the robot description from this parameter name\n");
00082 fprintf(stderr, " -u, --allow_unprogrammed Allow control loop to run with unprogrammed devices\n");
00083 fprintf(stderr, " -h, --help Print this message and exit\n");
00084 if (msg != "")
00085 {
00086 fprintf(stderr, "Error: %s\n", msg.c_str());
00087 exit(-1);
00088 }
00089 else
00090 exit(0);
00091 }
00092
00093 static int g_quit = 0;
00094 static const int SEC_2_NSEC = 1e+9;
00095 static const int SEC_2_USEC = 1e6;
00096
00097 static struct
00098 {
00099 accumulator_set<double, stats<tag::max, tag::mean> > ec_acc;
00100 accumulator_set<double, stats<tag::max, tag::mean> > cm_acc;
00101 accumulator_set<double, stats<tag::max, tag::mean> > loop_acc;
00102 accumulator_set<double, stats<tag::max, tag::mean> > jitter_acc;
00103 int overruns;
00104 int recent_overruns;
00105 int last_overrun;
00106 int last_severe_overrun;
00107 double overrun_loop_sec;
00108 double overrun_ec;
00109 double overrun_cm;
00110
00111
00112 bool rt_loop_not_making_timing;
00113 double halt_rt_loop_frequency;
00114 double rt_loop_frequency;
00115 } g_stats;
00116
00117 static void publishDiagnostics(RealtimePublisher<diagnostic_msgs::DiagnosticArray> &publisher)
00118 {
00119 if (publisher.trylock())
00120 {
00121 accumulator_set<double, stats<tag::max, tag::mean> > zero;
00122 vector<diagnostic_msgs::DiagnosticStatus> statuses;
00123 diagnostic_updater::DiagnosticStatusWrapper status;
00124
00125 static double max_ec = 0, max_cm = 0, max_loop = 0, max_jitter = 0;
00126 double avg_ec, avg_cm, avg_loop, avg_jitter;
00127
00128 avg_ec = extract_result<tag::mean>(g_stats.ec_acc);
00129 avg_cm = extract_result<tag::mean>(g_stats.cm_acc);
00130 avg_loop = extract_result<tag::mean>(g_stats.loop_acc);
00131 max_ec = std::max(max_ec, extract_result<tag::max>(g_stats.ec_acc));
00132 max_cm = std::max(max_cm, extract_result<tag::max>(g_stats.cm_acc));
00133 max_loop = std::max(max_loop, extract_result<tag::max>(g_stats.loop_acc));
00134 g_stats.ec_acc = zero;
00135 g_stats.cm_acc = zero;
00136 g_stats.loop_acc = zero;
00137
00138
00139 avg_jitter = extract_result<tag::mean>(g_stats.jitter_acc);
00140 max_jitter = std::max(max_jitter, extract_result<tag::max>(g_stats.jitter_acc));
00141 g_stats.jitter_acc = zero;
00142
00143 static bool first = true;
00144 if (first)
00145 {
00146 first = false;
00147 status.add("Robot Description", g_robot_desc);
00148 }
00149
00150 status.addf("Max EtherCAT roundtrip (us)", "%.2f", max_ec * SEC_2_USEC);
00151 status.addf("Avg EtherCAT roundtrip (us)", "%.2f", avg_ec * SEC_2_USEC);
00152 status.addf("Max Controller Manager roundtrip (us)", "%.2f", max_cm * SEC_2_USEC);
00153 status.addf("Avg Controller Manager roundtrip (us)", "%.2f", avg_cm * SEC_2_USEC);
00154 status.addf("Max Total Loop roundtrip (us)", "%.2f", max_loop * SEC_2_USEC);
00155 status.addf("Avg Total Loop roundtrip (us)", "%.2f", avg_loop * SEC_2_USEC);
00156 status.addf("Max Loop Jitter (us)", "%.2f", max_jitter * SEC_2_USEC);
00157 status.addf("Avg Loop Jitter (us)", "%.2f", avg_jitter * SEC_2_USEC);
00158 status.addf("Control Loop Overruns", "%d", g_stats.overruns);
00159 status.addf("Recent Control Loop Overruns", "%d", g_stats.recent_overruns);
00160 status.addf("Last Control Loop Overrun Cause", "ec: %.2fus, cm: %.2fus",
00161 g_stats.overrun_ec*SEC_2_USEC, g_stats.overrun_cm * SEC_2_USEC);
00162 status.addf("Last Overrun Loop Time (us)", "%.2f", g_stats.overrun_loop_sec * SEC_2_USEC);
00163 status.addf("Realtime Loop Frequency", "%.4f", g_stats.rt_loop_frequency);
00164
00165 status.name = "Realtime Control Loop";
00166 if (g_stats.overruns > 0 && g_stats.last_overrun < 30)
00167 {
00168 if (g_stats.last_severe_overrun < 30)
00169 status.level = 1;
00170 else
00171 status.level = 0;
00172 status.message = "Realtime loop used too much time in the last 30 seconds.";
00173 }
00174 else
00175 {
00176 status.level = 0;
00177 status.message = "OK";
00178 }
00179 g_stats.recent_overruns = 0;
00180 ++g_stats.last_overrun;
00181 ++g_stats.last_severe_overrun;
00182
00183 if (g_stats.rt_loop_not_making_timing)
00184 status.mergeSummaryf(status.ERROR, "realtime loop only ran at %.4f Hz", g_stats.halt_rt_loop_frequency);
00185
00186 statuses.push_back(status);
00187 publisher.msg_.status = statuses;
00188 publisher.msg_.header.stamp = ros::Time::now();
00189 publisher.unlockAndPublish();
00190 }
00191 }
00192
00193 static inline double now()
00194 {
00195 struct timespec n;
00196 clock_gettime(CLOCK_MONOTONIC, &n);
00197 return double(n.tv_nsec) / SEC_2_NSEC + n.tv_sec;
00198 }
00199
00200 void *diagnosticLoop(void *args)
00201 {
00202 ptr_vector<EthercatHardware>* ec = (ptr_vector<EthercatHardware>*) args;
00203 struct timespec tick;
00204 clock_gettime(CLOCK_MONOTONIC, &tick);
00205 while (!g_quit)
00206 {
00207 for (ptr_vector<EthercatHardware>::iterator eh = ec->begin(); eh != ec->end(); ++eh)
00208 {
00209 eh->collectDiagnostics();
00210 }
00211 ++tick.tv_sec;
00212 clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &tick, NULL);
00213 }
00214 return NULL;
00215 }
00216
00217 static void timespecInc(struct timespec &tick, int nsec)
00218 {
00219 tick.tv_nsec += nsec;
00220 while (tick.tv_nsec >= SEC_2_NSEC)
00221 {
00222 tick.tv_nsec -= SEC_2_NSEC;
00223 ++tick.tv_sec;
00224 }
00225 }
00226
00227 class RTLoopHistory
00228 {
00229 public:
00230
00231 RTLoopHistory(unsigned length, double default_value) :
00232 index_(0),
00233 length_(length),
00234 history_(length, default_value)
00235 {
00236 }
00237
00238 void sample(double value)
00239 {
00240 index_ = (index_ + 1) % length_;
00241 history_[index_] = value;
00242 }
00243
00244 double average() const
00245 {
00246 return accumulate(history_.begin(), history_.end(), 0.0) / (double) length_;
00247 }
00248
00249 protected:
00250 unsigned index_;
00251 unsigned length_;
00252 vector<double> history_;
00253 };
00254
00255 static void* terminate_control(RealtimePublisher<diagnostic_msgs::DiagnosticArray> *publisher,
00256 RealtimePublisher<std_msgs::Float64> *rtpublisher,
00257 const char* message,
00258 const char* data = NULL)
00259 {
00260 ROS_FATAL(message, data);
00261 publisher->stop();
00262 delete rtpublisher;
00263 ros::shutdown();
00264 return (void*) - 1;
00265 }
00266
00267 void *controlLoop(void *)
00268 {
00269 double last_published, last_loop_start;
00270 int policy;
00271 TiXmlElement *root;
00272 TiXmlElement *root_element;
00273
00274 ros::NodeHandle node(name);
00275
00276 RealtimePublisher<diagnostic_msgs::DiagnosticArray> publisher(node, "/diagnostics", 2);
00277 RealtimePublisher<std_msgs::Float64> *rtpublisher = NULL;
00278
00279
00280
00281 double period_in_secs = 1e+9 * g_options.period;
00282 double given_frequency = 1 / period_in_secs;
00283 double min_acceptable_rt_loop_frequency = 0.75 * given_frequency;
00284 if (node.getParam("min_acceptable_rt_loop_frequency", min_acceptable_rt_loop_frequency))
00285 ROS_WARN("min_acceptable_rt_loop_frequency changed to %f", min_acceptable_rt_loop_frequency);
00286
00287 unsigned rt_cycle_count = 0;
00288 double last_rt_monitor_time;
00289
00290
00291 double rt_loop_monitor_period = 0.2;
00292
00293 RTLoopHistory rt_loop_history(3, 1000.0);
00294
00295 if (g_options.stats_)
00296 rtpublisher = new RealtimePublisher<std_msgs::Float64>(node, "realtime", 2);
00297
00298
00299 TiXmlDocument xml;
00300 struct stat st;
00301
00302 if (ros::param::get(g_options.rosparam_, g_robot_desc))
00303 xml.Parse(g_robot_desc.c_str());
00304 else
00305 return terminate_control(&publisher, rtpublisher,
00306 "Could not load the xml from parameter server: %s", g_options.rosparam_);
00307
00308 root_element = xml.RootElement();
00309 root = xml.FirstChildElement("robot");
00310 if (!root || !root_element)
00311 return terminate_control(&publisher, rtpublisher, "Failed to parse the xml from %s", g_options.rosparam_);
00312
00313
00314 ros::NodeHandle nh;
00315 RosEthercat seth(nh, g_options.interface_, g_options.allow_unprogrammed_, root);
00316
00317
00318 controller_manager::ControllerManager cm(&seth);
00319
00320
00321 publishDiagnostics(publisher);
00322
00323
00324 static pthread_t diagnosticThread;
00325 int rv = pthread_create(&diagnosticThread, NULL, diagnosticLoop, &seth.ethercat_hardware_);
00326 if (rv != 0)
00327 return terminate_control(&publisher, rtpublisher,
00328 "Unable to create control thread: rv = %s", boost::lexical_cast<string>(rv).c_str());
00329
00330
00331 struct sched_param thread_param;
00332 policy = SCHED_FIFO;
00333 thread_param.sched_priority = sched_get_priority_max(policy);
00334 pthread_setschedparam(pthread_self(), policy, &thread_param);
00335
00336 struct timespec tick;
00337 clock_gettime(CLOCK_REALTIME, &tick);
00338 ros::Duration durp(g_options.period / 1e+9);
00339
00340
00341 tick.tv_nsec = (tick.tv_nsec / g_options.period + 1) * g_options.period;
00342 clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &tick, NULL);
00343
00344 last_published = now();
00345 last_rt_monitor_time = now();
00346 last_loop_start = now();
00347 while (!g_quit)
00348 {
00349
00350 double this_loop_start = now();
00351 g_stats.loop_acc(this_loop_start - last_loop_start);
00352 last_loop_start = this_loop_start;
00353
00354 double start = now();
00355
00356 ros::Time this_moment(tick.tv_sec, tick.tv_nsec);
00357 seth.read(this_moment, durp);
00358 double after_ec = now();
00359 cm.update(this_moment, durp);
00360 seth.write(this_moment, durp);
00361 double end = now();
00362
00363 g_stats.ec_acc(after_ec - start);
00364 g_stats.cm_acc(end - after_ec);
00365
00366 if ((end - last_published) > 1.0)
00367 {
00368 publishDiagnostics(publisher);
00369 last_published = end;
00370 }
00371
00372
00373
00374
00375 ++rt_cycle_count;
00376 if ((start - last_rt_monitor_time) > rt_loop_monitor_period)
00377 {
00378
00379 double rt_loop_frequency = double(rt_cycle_count) / rt_loop_monitor_period;
00380
00381
00382 rt_loop_history.sample(rt_loop_frequency);
00383 double avg_rt_loop_frequency = rt_loop_history.average();
00384 if (avg_rt_loop_frequency < min_acceptable_rt_loop_frequency)
00385 {
00386 if (!g_stats.rt_loop_not_making_timing)
00387 {
00388
00389 g_stats.halt_rt_loop_frequency = avg_rt_loop_frequency;
00390 }
00391 g_stats.rt_loop_not_making_timing = true;
00392 }
00393 g_stats.rt_loop_frequency = avg_rt_loop_frequency;
00394 rt_cycle_count = 0;
00395 last_rt_monitor_time = start;
00396 }
00397
00398
00399 timespecInc(tick, g_options.period);
00400
00401 struct timespec before;
00402 clock_gettime(CLOCK_REALTIME, &before);
00403 if ((before.tv_sec + double(before.tv_nsec) / SEC_2_NSEC) > (tick.tv_sec + double(tick.tv_nsec) / SEC_2_NSEC))
00404 {
00405
00406 g_stats.overrun_loop_sec = (before.tv_sec + double(before.tv_nsec) / SEC_2_NSEC) -
00407 (tick.tv_sec + double(tick.tv_nsec) / SEC_2_NSEC);
00408
00409
00410 tick.tv_sec = before.tv_sec;
00411 tick.tv_nsec = (before.tv_nsec / g_options.period) * g_options.period;
00412 timespecInc(tick, g_options.period);
00413
00414
00415 if (g_stats.overruns == 0)
00416 {
00417 g_stats.last_overrun = 1000;
00418 g_stats.last_severe_overrun = 1000;
00419 }
00420
00421 if (g_stats.recent_overruns > 10)
00422 g_stats.last_severe_overrun = 0;
00423 g_stats.last_overrun = 0;
00424
00425 ++g_stats.overruns;
00426 ++g_stats.recent_overruns;
00427 g_stats.overrun_ec = after_ec - start;
00428 g_stats.overrun_cm = end - after_ec;
00429 }
00430
00431
00432 clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &tick, NULL);
00433
00434
00435 struct timespec after;
00436 clock_gettime(CLOCK_REALTIME, &after);
00437 double jitter = (after.tv_sec - tick.tv_sec + double(after.tv_nsec - tick.tv_nsec) / SEC_2_NSEC);
00438
00439 g_stats.jitter_acc(jitter);
00440
00441
00442 if (rtpublisher && rtpublisher->trylock())
00443 {
00444 rtpublisher->msg_.data = jitter;
00445 rtpublisher->unlockAndPublish();
00446 }
00447 }
00448
00449
00450 seth.shutdown();
00451
00452 publisher.stop();
00453 delete rtpublisher;
00454 ros::shutdown();
00455 return NULL;
00456 }
00457
00458 void quitRequested(int sig)
00459 {
00460 g_quit = 1;
00461 }
00462
00463 static int lock_fd(int fd)
00464 {
00465 struct flock lock;
00466
00467 lock.l_type = F_WRLCK;
00468 lock.l_whence = SEEK_SET;
00469 lock.l_start = 0;
00470 lock.l_len = 0;
00471
00472 return fcntl(fd, F_SETLK, &lock);
00473 }
00474
00475
00476 static const char* PIDDIR = "/var/tmp/run/";
00477
00478 string generatePIDFilename(const char* interface)
00479 {
00480 string filename;
00481 filename = string(PIDDIR) + "EtherCAT_" + string(interface) + ".pid";
00482 return filename;
00483 }
00484
00485 static int setupPidFile(const char* interface)
00486 {
00487 pid_t pid;
00488 int fd;
00489 FILE *fp = NULL;
00490
00491 string filename = generatePIDFilename(interface);
00492
00493 umask(0);
00494 mkdir(PIDDIR, 0777);
00495 int PID_FLAGS = O_RDWR | O_CREAT | O_EXCL;
00496 int PID_MODE = S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP | S_IWOTH | S_IROTH;
00497 fd = open(filename.c_str(), PID_FLAGS, PID_MODE);
00498 if (fd == -1)
00499 {
00500 if (errno != EEXIST)
00501 {
00502 ROS_FATAL("Unable to create pid file '%s': %s", filename.c_str(), strerror(errno));
00503 return -1;
00504 }
00505
00506 if ((fd = open(filename.c_str(), O_RDWR)) < 0)
00507 {
00508 ROS_FATAL("Unable to open pid file '%s': %s", filename.c_str(), strerror(errno));
00509 return -1;
00510 }
00511
00512 if ((fp = fdopen(fd, "rw")) == NULL)
00513 {
00514 ROS_FATAL("Can't read from '%s': %s", filename.c_str(), strerror(errno));
00515 return -1;
00516 }
00517 pid = -1;
00518 if ((fscanf(fp, "%d", &pid) != 1) || (pid == getpid()) || (lock_fd(fileno(fp)) == 0))
00519 {
00520 int rc;
00521
00522 if ((rc = unlink(filename.c_str())) == -1)
00523 {
00524 ROS_FATAL("Can't remove stale pid file '%s': %s", filename.c_str(), strerror(errno));
00525 return -1;
00526 }
00527 }
00528 else
00529 {
00530 ROS_FATAL("Another instance of ros_ethercat is already running with pid: %d", pid);
00531 return -1;
00532 }
00533 }
00534
00535 unlink(filename.c_str());
00536 fd = open(filename.c_str(), PID_FLAGS, PID_MODE);
00537
00538 if (fd == -1)
00539 {
00540 ROS_FATAL("Unable to open pid file '%s': %s", filename.c_str(), strerror(errno));
00541 return -1;
00542 }
00543
00544 if (lock_fd(fd) == -1)
00545 {
00546 ROS_FATAL("Unable to lock pid file '%s': %s", filename.c_str(), strerror(errno));
00547 return -1;
00548 }
00549
00550 if ((fp = fdopen(fd, "w")) == NULL)
00551 {
00552 ROS_FATAL("fdopen failed: %s", strerror(errno));
00553 return -1;
00554 }
00555
00556 fprintf(fp, "%d\n", getpid());
00557
00558
00559 fflush(fp);
00560 fcntl(fd, F_SETFD, (long) 1);
00561
00562 return 0;
00563 }
00564
00565 static void cleanupPidFile(const char* interface)
00566 {
00567 string filename = generatePIDFilename(interface);
00568 unlink(filename.c_str());
00569 }
00570
00571 #define CLOCK_PRIO 0
00572 #define CONTROL_PRIO 0
00573
00574 static pthread_t controlThread;
00575 static pthread_attr_t controlThreadAttr;
00576
00577 int main(int argc, char *argv[])
00578 {
00579
00580 if (mlockall(MCL_CURRENT | MCL_FUTURE) < 0)
00581 {
00582 perror("Failed to lock memory. It is recommended to do rosrun ros_ethercat_loop ethercat_grant");
00583 exit(EXIT_FAILURE);
00584 }
00585
00586
00587 ros::init(argc, argv, "realtime_loop");
00588
00589
00590 g_options.program_ = argv[0];
00591 g_options.rosparam_ = NULL;
00592 g_options.period = 1e+6;
00593
00594 while (true)
00595 {
00596 static struct option long_options[] = {
00597 {"help", no_argument, 0, 'h'},
00598 {"stats", no_argument, 0, 's'},
00599 {"allow_unprogrammed", no_argument, 0, 'u'},
00600 {"interface", required_argument, 0, 'i'},
00601 {"rosparam", required_argument, 0, 'r'},
00602 {"period", no_argument, 0, 'p'},
00603 };
00604 int option_index = 0;
00605 int c = getopt_long(argc, argv, "hi:usx:r:", long_options, &option_index);
00606 if (c == -1)
00607 break;
00608
00609 switch (c)
00610 {
00611 case 'h':
00612 Usage();
00613 break;
00614 case 'u':
00615 g_options.allow_unprogrammed_ = true;
00616 break;
00617 case 'i':
00618 g_options.interface_ = optarg;
00619 break;
00620 case 'r':
00621 g_options.rosparam_ = optarg;
00622 break;
00623 case 's':
00624 g_options.stats_ = true;
00625 break;
00626 case 'p':
00627
00628 g_options.period = fabs(atof(optarg))*1e+6;
00629 break;
00630 }
00631 }
00632 if (optind < argc)
00633 Usage("Extra arguments");
00634
00635 if (!g_options.interface_)
00636 Usage("You must specify a network interface");
00637 if (!g_options.rosparam_)
00638 Usage("You must specify a rosparam for robot description");
00639
00640
00641 if (setupPidFile(g_options.interface_) < 0)
00642 exit(EXIT_FAILURE);
00643
00644 ros::NodeHandle node(name);
00645
00646
00647 signal(SIGTERM, quitRequested);
00648 signal(SIGINT, quitRequested);
00649 signal(SIGHUP, quitRequested);
00650
00651
00652 int rv = pthread_create(&controlThread, &controlThreadAttr, controlLoop, 0);
00653 if (rv != 0)
00654 {
00655 ROS_FATAL("Unable to create control thread: rv = %d", rv);
00656 exit(EXIT_FAILURE);
00657 }
00658
00659 ros::spin();
00660 pthread_join(controlThread, (void **) &rv);
00661
00662
00663 cleanupPidFile(NULL);
00664 cleanupPidFile(g_options.interface_);
00665
00666 return rv;
00667 }
00668