ros_ethercat.hpp
Go to the documentation of this file.
00001 /*
00002  * ros_ethercat.hpp
00003  *
00004  *  Created on: 7 Jan 2014
00005  *      Author: Manos Nikolaidis
00006  *
00007  * Software License Agreement (BSD License)
00008  *
00009  *  Copyright (c) 2014, Shadow Robot Company Ltd.
00010  *  All rights reserved.
00011  *
00012  *  Redistribution and use in source and binary forms, with or without
00013  *  modification, are permitted provided that the following conditions
00014  *  are met:
00015  *
00016  *   * Redistributions of source code must retain the above copyright
00017  *     notice, this list of conditions and the following disclaimer.
00018  *   * Redistributions in binary form must reproduce the above
00019  *     copyright notice, this list of conditions and the following
00020  *     disclaimer in the documentation and/or other materials provided
00021  *     with the distribution.
00022  *   * Neither the name of the Willow Garage nor the names of its
00023  *     contributors may be used to endorse or promote products derived
00024  *     from this software without specific prior written permission.
00025  *
00026  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00027  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00028  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00029  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00030  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00031  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00032  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00033  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00034  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00035  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00036  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00037  *  POSSIBILITY OF SUCH DAMAGE.
00038  *********************************************************************/
00039 
00040 #ifndef ROS_ETHERCAT_MODEL_ROS_ETHERCAT_HPP_
00041 #define ROS_ETHERCAT_MODEL_ROS_ETHERCAT_HPP_
00042 
00043 #include <fcntl.h>
00044 #include <sys/stat.h>
00045 #include <boost/algorithm/string/split.hpp>
00046 #include <boost/algorithm/string.hpp>
00047 #include <boost/thread/thread.hpp>
00048 #include <ros/ros.h>
00049 #include <ros/console.h>
00050 #include <hardware_interface/robot_hw.h>
00051 #include <hardware_interface/joint_state_interface.h>
00052 #include <hardware_interface/joint_command_interface.h>
00053 #include <pr2_mechanism_msgs/MechanismStatistics.h>
00054 #include "ros_ethercat_model/robot_state.hpp"
00055 #include "ros_ethercat_model/robot_state_interface.hpp"
00056 #include "ros_ethercat_model/mech_stats_publisher.hpp"
00057 #include "ros_ethercat_hardware/ethercat_hardware.h"
00058 
00059 
00077 using std::string;
00078 using std::vector;
00079 using boost::ptr_unordered_map;
00080 using boost::ptr_vector;
00081 using ros_ethercat_model::JointState;
00082 using ros_ethercat_model::Actuator;
00083 using ros_ethercat_model::Transmission;
00084 using ros_ethercat_model::CustomHW;
00085 
00086 static const string name = "ros_ethercat";
00087 
00088 class RosEthercat : public hardware_interface::RobotHW
00089 {
00090 public:
00091   RosEthercat() :
00092     compatibility_mode_(false),
00093     collect_diagnostics_running_(false),
00094     run_diagnostics_(false)
00095   {
00096 
00097   }
00098 
00099   RosEthercat(ros::NodeHandle &nh, const string &eth, bool allow, TiXmlElement* config) :
00100     compatibility_mode_(true),
00101     collect_diagnostics_running_(false),
00102     run_diagnostics_(false)
00103   {
00104     model_.reset(new RobotState(config));
00105     vector<string> port_names;
00106     boost::split(port_names, eth, boost::is_any_of("_, "));
00107     for (vector<string>::const_iterator port_name = port_names.begin();
00108          port_name != port_names.end();
00109          ++port_name)
00110     {
00111       if (!port_name->empty())
00112       {
00113         ethercat_hardware_.push_back(new EthercatHardware(name,
00114                                                           static_cast<hardware_interface::HardwareInterface*> (model_.get()),
00115                                                           *port_name,
00116                                                           allow));
00117         ROS_INFO_STREAM("Added Ethernet port " << *port_name);
00118       }
00119     }
00120 
00121     for (ptr_unordered_map<string, JointState>::iterator it = model_->joint_states_.begin();
00122          it != model_->joint_states_.end();
00123          ++it)
00124     {
00125       hardware_interface::JointStateHandle jsh(it->first,
00126                                                &it->second->position_,
00127                                                &it->second->velocity_,
00128                                                &it->second->effort_);
00129       joint_state_interface_.registerHandle(jsh);
00130 
00131       joint_position_command_interface_.registerHandle(hardware_interface::JointHandle(jsh,
00132                                                                                        & it->second->commanded_position_));
00133       joint_velocity_command_interface_.registerHandle(hardware_interface::JointHandle(jsh,
00134                                                                                        & it->second->commanded_velocity_));
00135       joint_effort_command_interface_.registerHandle(hardware_interface::JointHandle(jsh,
00136                                                                                      & it->second->commanded_effort_));
00137     }
00138 
00139     if (!model_->joint_states_.empty())
00140       mech_stats_publisher_.reset(new MechStatsPublisher(nh, *model_));
00141 
00142     robot_state_interface_.registerHandle(ros_ethercat_model::RobotStateHandle("unique_robot_hw", model_.get()));
00143 
00144     registerInterface(&robot_state_interface_);
00145     registerInterface(&joint_state_interface_);
00146     registerInterface(&joint_position_command_interface_);
00147     registerInterface(&joint_velocity_command_interface_);
00148     registerInterface(&joint_effort_command_interface_);
00149   }
00150 
00151   virtual ~RosEthercat()
00152   {
00153     if (!compatibility_mode_)
00154     {
00155       // Shutdown all of the motors on exit
00156       shutdown();
00157       // Cleanup pid files
00158       cleanupPidFile(NULL);
00159       cleanupPidFile(eth_.c_str());
00160 
00161       stop_collect_diagnostics();
00162       while (is_collect_diagnostics_running())
00163       {
00164         usleep(100);
00165       }
00166     }
00167   }
00168 
00169   virtual bool init(ros::NodeHandle& root_nh, ros::NodeHandle &robot_hw_nh)
00170   {
00171     // Load robot description
00172     TiXmlDocument xml;
00173     TiXmlElement *root;
00174     TiXmlElement *root_element;
00175 
00176     std::string robot_description;
00177     std::string robot_description_param;
00178     bool allow;
00179 
00180     robot_state_name_ = robot_hw_nh.getNamespace().substr(1); // remove the leading slash of the namespace
00181     ROS_INFO_STREAM("Robot State Name: " << robot_state_name_);
00182 
00183     if (!robot_hw_nh.getParam("robot_description_param", robot_description_param))
00184     {
00185       ROS_ERROR("robot_description_param not found (namespace: %s)", robot_hw_nh.getNamespace().c_str());
00186       return false;
00187     }
00188 
00189     if (robot_description_param == "None")
00190     {
00191       root = NULL;
00192     }
00193     else
00194     {
00195       if (!root_nh.getParam(robot_description_param, robot_description))
00196       {
00197         ROS_ERROR("Robot description: %s not found (namespace: %s)", robot_description_param.c_str(), root_nh.getNamespace().c_str());
00198         return false;
00199       }
00200       xml.Parse(robot_description.c_str());
00201       root_element = xml.RootElement();
00202       root = xml.FirstChildElement("robot");
00203       if (!root || !root_element)
00204       {
00205         ROS_ERROR("Robot description %s has no root",robot_description_param.c_str());
00206         return false;
00207       }
00208     }
00209 
00210     model_.reset(new RobotState(root));
00211 
00212     if (!robot_hw_nh.getParam("ethercat_port", eth_))
00213     {
00214       ROS_ERROR("ethercat_port param not found (namespace: %s)", robot_hw_nh.getNamespace().c_str());
00215       return false;
00216     }
00217 
00218     // EtherCAT lock for this interface (e.g. Ethernet port)
00219     if (setupPidFile(eth_.c_str()) < 0)
00220     {
00221       return false;
00222     }
00223 
00224     robot_hw_nh.param<bool>("allow_unprogrammed", allow, false);
00225 
00226     vector<string> port_names;
00227     boost::split(port_names, eth_, boost::is_any_of("_, "));
00228     for (vector<string>::const_iterator port_name = port_names.begin();
00229          port_name != port_names.end();
00230          ++port_name)
00231     {
00232       if (!port_name->empty())
00233       {
00234         ethercat_hardware_.push_back(new EthercatHardware(name,
00235                                                           static_cast<hardware_interface::HardwareInterface*> (model_.get()),
00236                                                           *port_name,
00237                                                           allow));
00238         ROS_INFO_STREAM("Added Ethernet port " << *port_name);
00239       }
00240     }
00241 
00242     for (ptr_unordered_map<string, JointState>::iterator it = model_->joint_states_.begin();
00243          it != model_->joint_states_.end();
00244          ++it)
00245     {
00246       hardware_interface::JointStateHandle jsh(it->first,
00247                                                &it->second->position_,
00248                                                &it->second->velocity_,
00249                                                &it->second->effort_);
00250       joint_state_interface_.registerHandle(jsh);
00251 
00252       joint_position_command_interface_.registerHandle(hardware_interface::JointHandle(jsh,
00253                                                                                        & it->second->commanded_position_));
00254       joint_velocity_command_interface_.registerHandle(hardware_interface::JointHandle(jsh,
00255                                                                                        & it->second->commanded_velocity_));
00256       joint_effort_command_interface_.registerHandle(hardware_interface::JointHandle(jsh,
00257                                                                                      & it->second->commanded_effort_));
00258     }
00259 
00260     if (!model_->joint_states_.empty())
00261       mech_stats_publisher_.reset(new MechStatsPublisher(root_nh, *model_));
00262 
00263     robot_state_interface_.registerHandle(ros_ethercat_model::RobotStateHandle(robot_state_name_, model_.get()));
00264 
00265     registerInterface(&robot_state_interface_);
00266     registerInterface(&joint_state_interface_);
00267     registerInterface(&joint_position_command_interface_);
00268     registerInterface(&joint_velocity_command_interface_);
00269     registerInterface(&joint_effort_command_interface_);
00270 
00271     // Start a thread to collect diagnostics. This could actually be inside the EthercatHardware class
00272     // but until we remove the compatibility mode this will do.
00273     collect_diagnostics_thread_ = boost::thread(&RosEthercat::collect_diagnostics_loop, this);
00274 
00275     return true;
00276   }
00277 
00279   void read(const ros::Time &time, const ros::Duration& period)
00280   {
00281     for (ptr_vector<EthercatHardware>::iterator eh = ethercat_hardware_.begin();
00282          eh != ethercat_hardware_.end();
00283          ++eh)
00284     {
00285       eh->update(false, false);
00286     }
00287 
00288     model_->current_time_ = time;
00289     model_->propagateActuatorPositionToJointPosition();
00290 
00291     for (ptr_unordered_map<std::string, CustomHW>::iterator it = model_->custom_hws_.begin();
00292          it != model_->custom_hws_.end();
00293          ++it)
00294     {
00295       it->second->read(time);
00296     }
00297 
00298     for (ptr_unordered_map<string, JointState>::iterator it = model_->joint_states_.begin();
00299          it != model_->joint_states_.end();
00300          ++it)
00301     {
00302       it->second->joint_statistics_.update(it->second);
00303       it->second->commanded_effort_ = 0;
00304     }
00305   }
00306 
00308   void write(const ros::Time &time, const ros::Duration& period)
00309   {
00311     for (ptr_unordered_map<string, JointState>::iterator it = model_->joint_states_.begin();
00312          it != model_->joint_states_.end();
00313          ++it)
00314     {
00315       it->second->enforceLimits();
00316     }
00317 
00318     model_->propagateJointEffortToActuatorEffort();
00319 
00320     for (ptr_unordered_map<std::string, CustomHW>::iterator it = model_->custom_hws_.begin();
00321          it != model_->custom_hws_.end();
00322          ++it)
00323     {
00324       it->second->write(time);
00325     }
00326 
00327     if (!model_->joint_states_.empty())
00328       mech_stats_publisher_->publish(time);
00329   }
00330 
00332   void shutdown()
00333   {
00334     for (ptr_vector<Transmission>::iterator it = model_->transmissions_.begin();
00335          it != model_->transmissions_.end();
00336          ++it)
00337     {
00338       it->actuator_->command_.enable_ = false;
00339       it->actuator_->command_.effort_ = 0;
00340     }
00341 
00342     for (ptr_vector<EthercatHardware>::iterator eh = ethercat_hardware_.begin();
00343          eh != ethercat_hardware_.end();
00344          ++eh)
00345     {
00346       eh->update(false, true);
00347     }
00348   }
00349 
00350 
00351   static const string pid_dir;
00352   string eth_;
00353   boost::shared_ptr<ros_ethercat_model::RobotState> model_;
00354   ptr_vector<EthercatHardware> ethercat_hardware_;
00355   boost::scoped_ptr<MechStatsPublisher> mech_stats_publisher_;
00356 
00357   // robot state interface
00358   ros_ethercat_model::RobotStateInterface robot_state_interface_;
00359 
00360   // joint state interface
00361   hardware_interface::JointStateInterface joint_state_interface_;
00362 
00363   // joint command interface
00364   hardware_interface::PositionJointInterface joint_position_command_interface_;
00365   hardware_interface::VelocityJointInterface joint_velocity_command_interface_;
00366   hardware_interface::EffortJointInterface joint_effort_command_interface_;
00367 
00368 protected:
00369   static int lock_fd(int fd)
00370   {
00371     struct flock lock;
00372 
00373     lock.l_type = F_WRLCK;
00374     lock.l_whence = SEEK_SET;
00375     lock.l_start = 0;
00376     lock.l_len = 0;
00377 
00378     return fcntl(fd, F_SETLK, &lock);
00379   }
00380 
00381   static string generatePIDFilename(const char* interface)
00382   {
00383     string filename;
00384     filename = pid_dir + "EtherCAT_" + string(interface) + ".pid";
00385     return filename;
00386   }
00387 
00388   static int setupPidFile(const char* interface)
00389   {
00390     pid_t pid;
00391     int fd;
00392     FILE *fp = NULL;
00393 
00394     string filename = generatePIDFilename(interface);
00395 
00396     umask(0);
00397     mkdir(pid_dir.c_str(), 0777);
00398     int PID_FLAGS = O_RDWR | O_CREAT | O_EXCL;
00399     int PID_MODE = S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP | S_IWOTH | S_IROTH;
00400     fd = open(filename.c_str(), PID_FLAGS, PID_MODE);
00401     if (fd == -1)
00402     {
00403       if (errno != EEXIST)
00404       {
00405         ROS_FATAL("Unable to create pid file '%s': %s", filename.c_str(), strerror(errno));
00406         return -1;
00407       }
00408 
00409       if ((fd = open(filename.c_str(), O_RDWR)) < 0)
00410       {
00411         ROS_FATAL("Unable to open pid file '%s': %s", filename.c_str(), strerror(errno));
00412         return -1;
00413       }
00414 
00415       if ((fp = fdopen(fd, "rw")) == NULL)
00416       {
00417         ROS_FATAL("Can't read from '%s': %s", filename.c_str(), strerror(errno));
00418         return -1;
00419       }
00420       pid = -1;
00421       if ((fscanf(fp, "%d", &pid) != 1) || (pid == getpid()) || (lock_fd(fileno(fp)) == 0))
00422       {
00423         int rc;
00424 
00425         if ((rc = unlink(filename.c_str())) == -1)
00426         {
00427           ROS_FATAL("Can't remove stale pid file '%s': %s", filename.c_str(), strerror(errno));
00428           return -1;
00429         }
00430       }
00431       else
00432       {
00433         ROS_FATAL("Another instance of ros_ethercat is already running with pid: %d", pid);
00434         return -1;
00435       }
00436     }
00437 
00438     unlink(filename.c_str());
00439     fd = open(filename.c_str(), PID_FLAGS, PID_MODE);
00440 
00441     if (fd == -1)
00442     {
00443       ROS_FATAL("Unable to open pid file '%s': %s", filename.c_str(), strerror(errno));
00444       return -1;
00445     }
00446 
00447     if (lock_fd(fd) == -1)
00448     {
00449       ROS_FATAL("Unable to lock pid file '%s': %s", filename.c_str(), strerror(errno));
00450       return -1;
00451     }
00452 
00453     if ((fp = fdopen(fd, "w")) == NULL)
00454     {
00455       ROS_FATAL("fdopen failed: %s", strerror(errno));
00456       return -1;
00457     }
00458 
00459     fprintf(fp, "%d\n", getpid());
00460 
00461     /* We do NOT close fd, since we want to keep the lock. */
00462     fflush(fp);
00463     fcntl(fd, F_SETFD, (long) 1);
00464 
00465     return 0;
00466   }
00467 
00468   static void cleanupPidFile(const char* interface)
00469   {
00470     string filename = generatePIDFilename(interface);
00471     unlink(filename.c_str());
00472   }
00473 
00474   void collect_diagnostics_loop()
00475   {
00476     run_diagnostics_ = true;
00477     collect_diagnostics_running_ = true;
00478     ros::Rate diag_rate(1.0); // Send diagnostics at 1Hz
00479     while (run_diagnostics_)
00480     {
00481       for (ptr_vector<EthercatHardware>::iterator eh = ethercat_hardware_.begin(); eh != ethercat_hardware_.end(); ++eh)
00482       {
00483         eh->collectDiagnostics();
00484       }
00485       diag_rate.sleep();
00486     }
00487     collect_diagnostics_running_ = false;
00488   }
00489 
00490   void stop_collect_diagnostics()
00491   {
00492     run_diagnostics_ = false;
00493   }
00494 
00495   bool is_collect_diagnostics_running()
00496   {
00497     return collect_diagnostics_running_;
00498   }
00499 
00500   bool compatibility_mode_;
00501   bool run_diagnostics_;
00502   bool collect_diagnostics_running_;
00503   boost::thread collect_diagnostics_thread_;
00504   std::string robot_state_name_;
00505 };
00506 
00507 const string RosEthercat::pid_dir = "/var/tmp/run/";
00508 
00509 #endif


ros_ethercat_model
Author(s): Manos Nikolaidis
autogenerated on Thu Jul 4 2019 20:01:55