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
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 ð, 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
00156 shutdown();
00157
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
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);
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
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
00272
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
00358 ros_ethercat_model::RobotStateInterface robot_state_interface_;
00359
00360
00361 hardware_interface::JointStateInterface joint_state_interface_;
00362
00363
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
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);
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