main.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2008, Willow Garage, Inc.
00005  *  All rights reserved.
00006  *
00007  *  Modified 2014, by Shadow Robot Company Ltd.
00008  *
00009  *  Redistribution and use in source and binary forms, with or without
00010  *  modification, are permitted provided that the following conditions
00011  *  are met:
00012  *
00013  *   * Redistributions of source code must retain the above copyright
00014  *     notice, this list of conditions and the following disclaimer.
00015  *   * Redistributions in binary form must reproduce the above
00016  *     copyright notice, this list of conditions and the following
00017  *     disclaimer in the documentation and/or other materials provided
00018  *     with the distribution.
00019  *   * Neither the name of the Willow Garage nor the names of its
00020  *     contributors may be used to endorse or promote products derived
00021  *     from this software without specific prior written permission.
00022  *
00023  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00024  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00025  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00026  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00027  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00028  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00029  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00030  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00031  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00032  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00033  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00034  *  POSSIBILITY OF SUCH DAMAGE.
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   // These values are set when realtime loop does not meet performance expectations
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     // Publish average loop jitter
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   // Realtime loop should be running at least 3/4 of given frequency
00280   // or at specified min acceptable frequency
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   // Calculate realtime loop frequency every 200msec
00291   double rt_loop_monitor_period = 0.2;
00292   // Keep history of last 3 calculation intervals.
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   // Load robot description
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   // Initialize the hardware interface
00314   ros::NodeHandle nh;
00315   RosEthercat seth(nh, g_options.interface_, g_options.allow_unprogrammed_, root);
00316 
00317   // Create controller manager
00318   controller_manager::ControllerManager cm(&seth);
00319 
00320   // Publish one-time before entering real-time to pre-allocate message vectors
00321   publishDiagnostics(publisher);
00322 
00323   //Start Non-realtime diagnostic thread
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   // Set to realtime scheduler for this thread
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   // Snap to the nearest second
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     // Track how long the actual loop takes
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     // Realtime loop should run about with the set frequency by default 1000Hz.
00373     // Missing timing on control cycles usually causes a controller glitch and actuators to jerk.
00374     // When realtime loop misses a lot of cycles controllers will perform poorly and may cause robot to shake.
00375     ++rt_cycle_count;
00376     if ((start - last_rt_monitor_time) > rt_loop_monitor_period)
00377     {
00378       // Calculate new average rt loop frequency
00379       double rt_loop_frequency = double(rt_cycle_count) / rt_loop_monitor_period;
00380 
00381       // Use last X samples of frequency when deciding whether or not to halt
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           // Only update this value if motors when this first occurs (used for diagnostics error message)
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     // Compute end of next g_options.period
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       // Total amount of time the loop took to run
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       // We overran, snap to next "g_options.period"
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       // initialize overruns
00415       if (g_stats.overruns == 0)
00416       {
00417         g_stats.last_overrun = 1000;
00418         g_stats.last_severe_overrun = 1000;
00419       }
00420       // check for overruns
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     // Sleep until end of g_options.period
00432     clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &tick, NULL);
00433 
00434     // Calculate RT loop jitter
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     // Publish realtime loops statistics, if requested
00442     if (rtpublisher && rtpublisher->trylock())
00443     {
00444       rtpublisher->msg_.data = jitter;
00445       rtpublisher->unlockAndPublish();
00446     }
00447   }
00448 
00449   // Shutdown all of the motors on exit
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   /* We do NOT close fd, since we want to keep the lock. */
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   // Keep the kernel from swapping us out
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   // Initialize ROS and parse command-line arguments
00587   ros::init(argc, argv, "realtime_loop");
00588 
00589   // Parse options
00590   g_options.program_ = argv[0];
00591   g_options.rosparam_ = NULL;
00592   g_options.period = 1e+6; // 1 ms in nanoseconds
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         // convert period given in msec to nsec
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   // EtherCAT lock for this interface (e.g. Ethernet port)
00641   if (setupPidFile(g_options.interface_) < 0)
00642     exit(EXIT_FAILURE);
00643 
00644   ros::NodeHandle node(name);
00645 
00646   // Catch attempts to quit
00647   signal(SIGTERM, quitRequested);
00648   signal(SIGINT, quitRequested);
00649   signal(SIGHUP, quitRequested);
00650 
00651   //Start thread
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   // Cleanup pid files
00663   cleanupPidFile(NULL);
00664   cleanupPidFile(g_options.interface_);
00665 
00666   return rv;
00667 }
00668 


ros_ethercat_loop
Author(s): Manos Nikolaidis
autogenerated on Thu Jul 4 2019 20:01:57