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 #include "ros_ethercat_hardware/ethercat_hardware.h"
00036
00037 #include <ros_ethercat_eml/ethercat_xenomai_drv.h>
00038
00039 #include <sstream>
00040
00041 #include <net/if.h>
00042 #include <sys/ioctl.h>
00043 #include <boost/foreach.hpp>
00044 #include <boost/regex.hpp>
00045
00046 EthercatHardwareDiagnostics::EthercatHardwareDiagnostics() :
00047
00048 txandrx_errors_(0),
00049 device_count_(0),
00050 pd_error_(false),
00051 halt_after_reset_(false),
00052 reset_motors_service_count_(0),
00053 halt_motors_service_count_(0),
00054 halt_motors_error_count_(0),
00055 motors_halted_(false),
00056 motors_halted_reason_("")
00057 {
00058 resetMaxTiming();
00059 }
00060
00061 void EthercatHardwareDiagnostics::resetMaxTiming()
00062 {
00063 max_pack_command_ = 0.0;
00064 max_txandrx_ = 0.0;
00065 max_unpack_state_ = 0.0;
00066 max_publish_ = 0.0;
00067 }
00068
00069 EthercatHardware::EthercatHardware(const std::string& name,
00070 hardware_interface::HardwareInterface *hw,
00071 const std::string& eth, bool allow_unprogrammed) :
00072 hw_(hw),
00073 node_(ros::NodeHandle(name)),
00074 ni_(0),
00075 interface_(eth),
00076 this_buffer_(0),
00077 prev_buffer_(0),
00078 buffer_size_(0),
00079 halt_motors_(true),
00080 reset_state_(0),
00081 max_pd_retries_(10),
00082 diagnostics_publisher_(node_),
00083 motor_publisher_(node_, "motors_halted", 1, true),
00084 device_loader_("ros_ethercat_hardware", "EthercatDevice"),
00085 allow_unprogrammed_(allow_unprogrammed)
00086 {
00087 if (!interface_.empty())
00088 init();
00089 else
00090 ROS_DEBUG("No ethercat interface given. EthercatHardware will not be initialized");
00091 }
00092
00093 EthercatHardware::~EthercatHardware()
00094 {
00095 diagnostics_publisher_.stop();
00096 for (uint32_t i = 0; i < slaves_.size(); ++i)
00097 {
00098 EC_FixedStationAddress fsa(i + 1);
00099 EtherCAT_SlaveHandler *sh = em_->get_slave_handler(fsa);
00100 if (sh)
00101 sh->to_state(EC_PREOP_STATE);
00102 }
00103 if (ni_)
00104 {
00105 close_socket(ni_);
00106 }
00107 delete[] buffers_;
00108 delete oob_com_;
00109 motor_publisher_.stop();
00110 }
00111
00112 void EthercatHardware::changeState(EtherCAT_SlaveHandler *sh, EC_State new_state)
00113 {
00114 unsigned product_code = sh->get_product_code();
00115 unsigned serial = sh->get_serial();
00116 uint32_t revision = sh->get_revision();
00117 unsigned slave = sh->get_station_address() - 1;
00118
00119 if (!sh->to_state(new_state))
00120 {
00121 ROS_FATAL("Cannot goto state %d for slave #%d, product code: %u (0x%X), serial: %u (0x%X), revision: %d (0x%X)",
00122 new_state,
00123 slave, product_code, product_code, serial, serial, revision, revision);
00124 if ((product_code == 0xbaddbadd) || (serial == 0xbaddbadd) || (revision == 0xbaddbadd))
00125 ROS_FATAL("Note: 0xBADDBADD indicates that the value was not read correctly from device.");
00126 exit(EXIT_FAILURE);
00127 }
00128 }
00129
00130 void EthercatHardware::init()
00131 {
00132
00133 int sock = socket(PF_INET, SOCK_DGRAM, 0);
00134 if (sock < 0)
00135 {
00136 int error = errno;
00137 ROS_FATAL("Couldn't open temp socket : %s", strerror(error));
00138 sleep(1);
00139 exit(EXIT_FAILURE);
00140 }
00141
00142 struct ifreq ifr;
00143 strncpy(ifr.ifr_name, interface_.c_str(), IFNAMSIZ);
00144 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
00145 {
00146 int error = errno;
00147 ROS_FATAL("Cannot get interface_.c_str() flags for %s: %s", interface_.c_str(),
00148 strerror(error));
00149 sleep(1);
00150 exit(EXIT_FAILURE);
00151 }
00152
00153 close(sock);
00154 sock = -1;
00155
00156 if (!(ifr.ifr_flags & IFF_UP))
00157 {
00158 ROS_FATAL("Interface %s is not UP. Try : ifup %s", interface_.c_str(), interface_.c_str());
00159 sleep(1);
00160 exit(EXIT_FAILURE);
00161 }
00162 if (!(ifr.ifr_flags & IFF_RUNNING))
00163 {
00164 ROS_FATAL("Interface %s is not RUNNING. Is cable plugged in and device powered?",
00165 interface_.c_str());
00166 sleep(1);
00167 exit(EXIT_FAILURE);
00168 }
00169
00170
00171 if ((ni_ = init_ec(interface_.c_str())) == NULL)
00172 {
00173 ROS_FATAL("Unable to initialize interface_.c_str(): %s", interface_.c_str());
00174 sleep(1);
00175 exit(EXIT_FAILURE);
00176 }
00177
00178 oob_com_ = new EthercatOobCom(ni_);
00179
00180
00181 EtherCAT_DataLinkLayer::instance()->attach(ni_);
00182 if ((al_ = EtherCAT_AL::instance()) == NULL)
00183 {
00184 ROS_FATAL("Unable to initialize Application Layer (AL): %p", al_);
00185 sleep(1);
00186 exit(EXIT_FAILURE);
00187 }
00188
00189 int num_ethercat_devices_ = al_->get_num_slaves();
00190 if (num_ethercat_devices_ == 0)
00191 {
00192 ROS_FATAL("Unable to locate any slaves");
00193 sleep(1);
00194 exit(EXIT_FAILURE);
00195 }
00196
00197
00198 if ((em_ = EtherCAT_Master::instance()) == NULL)
00199 {
00200 ROS_FATAL("Unable to initialize EtherCAT_Master: %p", em_);
00201 sleep(1);
00202 exit(EXIT_FAILURE);
00203 }
00204
00205
00206 slaves_.resize(num_ethercat_devices_);
00207
00208
00209 std::vector<EtherCAT_SlaveHandler*> slave_handles;
00210 for (unsigned int slave = 0; slave < slaves_.size(); ++slave)
00211 {
00212 EC_FixedStationAddress fsa(slave + 1);
00213 EtherCAT_SlaveHandler *sh = em_->get_slave_handler(fsa);
00214 if (sh == NULL)
00215 {
00216 ROS_FATAL("Unable to get slave handler #%d", slave);
00217 sleep(1);
00218 exit(EXIT_FAILURE);
00219 }
00220 slave_handles.push_back(sh);
00221 }
00222
00223
00224
00225 BOOST_FOREACH(EtherCAT_SlaveHandler *sh, slave_handles)
00226 {
00227 unsigned slave = sh->get_station_address() - 1;
00228 if ((slaves_[slave] = configSlave(sh)) == NULL)
00229 {
00230 ROS_FATAL("Unable to configure slave #%d", slave);
00231 sleep(1);
00232 exit(EXIT_FAILURE);
00233 }
00234 buffer_size_ += slaves_[slave]->command_size_ + slaves_[slave]->status_size_;
00235 }
00236
00237
00238 loadNonEthercatDevices();
00239
00240
00241
00242 BOOST_FOREACH(EtherCAT_SlaveHandler *sh, slave_handles)
00243 {
00244 changeState(sh, EC_PREOP_STATE);
00245 }
00246
00247
00248
00249 BOOST_FOREACH(EtherCAT_SlaveHandler *sh, slave_handles)
00250 {
00251 changeState(sh, EC_SAFEOP_STATE);
00252 }
00253
00254
00255
00256
00257 BOOST_FOREACH(EtherCAT_SlaveHandler *sh, slave_handles)
00258 {
00259 changeState(sh, EC_OP_STATE);
00260 }
00261
00262
00263 buffers_ = new unsigned char[2 * buffer_size_];
00264 this_buffer_ = buffers_;
00265 prev_buffer_ = buffers_ + buffer_size_;
00266
00267
00268 memset(this_buffer_, 0, 2 * buffer_size_);
00269 if (!txandrx_PD(buffer_size_, this_buffer_, 20))
00270 {
00271 ROS_FATAL("No communication with devices");
00272 sleep(1);
00273 exit(EXIT_FAILURE);
00274 }
00275
00276
00277 memcpy(prev_buffer_, this_buffer_, buffer_size_);
00278
00279 last_published_ = ros::Time::now();
00280
00281
00282
00283 for (unsigned int slave = 0; slave < slaves_.size(); ++slave)
00284 {
00285 if (slaves_[slave]->initialize(hw_, allow_unprogrammed_) < 0)
00286 {
00287 EtherCAT_SlaveHandler *sh = slaves_[slave]->sh_;
00288 if (sh != NULL)
00289 {
00290 ROS_FATAL("Unable to initialize slave #%d, product code: %d, revision: %d, serial: %d",
00291 slave,
00292 sh->get_product_code(), sh->get_revision(), sh->get_serial());
00293 sleep(1);
00294 }
00295 else
00296 {
00297 ROS_FATAL("Unable to initialize slave #%d", slave);
00298 }
00299 exit(EXIT_FAILURE);
00300 }
00301 }
00302
00303 {
00304
00305
00306 static const int MAX_TIMEOUT = 100000;
00307 static const int DEFAULT_TIMEOUT = 20000;
00308 int timeout;
00309 if (!node_.getParam("realtime_socket_timeout", timeout))
00310 {
00311 timeout = DEFAULT_TIMEOUT;
00312 }
00313 if ((timeout <= 1) || (timeout > MAX_TIMEOUT))
00314 {
00315 int old_timeout = timeout;
00316 timeout = std::max(1, std::min(MAX_TIMEOUT, timeout));
00317 ROS_WARN("Invalid timeout (%d) for socket, using %d", old_timeout, timeout);
00318 }
00319 if (set_socket_timeout(ni_, timeout))
00320 {
00321 ROS_FATAL("Error setting socket timeout to %d", timeout);
00322 sleep(1);
00323 exit(EXIT_FAILURE);
00324 }
00325 timeout_ = timeout;
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336 int max_pd_retries = MAX_TIMEOUT / timeout;
00337 static const int MAX_RETRIES = 50, MIN_RETRIES = 1;
00338 node_.getParam("max_pd_retries", max_pd_retries);
00339
00340 if ((max_pd_retries * timeout) > (MAX_TIMEOUT))
00341 {
00342 max_pd_retries = MAX_TIMEOUT / timeout;
00343 ROS_WARN("Max PD retries is too large for given timeout. Limiting value to %d",
00344 max_pd_retries);
00345 }
00346 if ((max_pd_retries < MIN_RETRIES) || (max_pd_retries > MAX_RETRIES))
00347 {
00348 max_pd_retries = std::max(MIN_RETRIES, std::min(MAX_RETRIES, max_pd_retries));
00349 ROS_WARN("Limiting max PD retries to %d", max_pd_retries);
00350 }
00351 max_pd_retries = std::max(MIN_RETRIES, std::min(MAX_RETRIES, max_pd_retries));
00352 max_pd_retries_ = max_pd_retries;
00353 }
00354
00355 diagnostics_publisher_.initialize(interface_, buffer_size_, slaves_, num_ethercat_devices_,
00356 timeout_,
00357 max_pd_retries_);
00358 }
00359
00360 EthercatHardwareDiagnosticsPublisher::EthercatHardwareDiagnosticsPublisher(ros::NodeHandle &node) :
00361 node_(node),
00362 diagnostics_ready_(false),
00363 publisher_(node_.advertise<diagnostic_msgs::DiagnosticArray>("/diagnostics", 1)),
00364 diagnostics_buffer_(NULL),
00365 last_dropped_packet_count_(0),
00366 last_dropped_packet_time_(0)
00367 {
00368 }
00369
00370 EthercatHardwareDiagnosticsPublisher::~EthercatHardwareDiagnosticsPublisher()
00371 {
00372 delete[] diagnostics_buffer_;
00373 }
00374
00375 void EthercatHardwareDiagnosticsPublisher::initialize(const string &interface, unsigned int buffer_size,
00376 const std::vector<boost::shared_ptr<EthercatDevice> > &slaves,
00377 unsigned int num_ethercat_devices,
00378 unsigned timeout,
00379 unsigned max_pd_retries)
00380 {
00381 interface_ = interface;
00382 buffer_size_ = buffer_size;
00383 slaves_ = slaves;
00384 num_ethercat_devices_ = num_ethercat_devices;
00385 timeout_ = timeout;
00386 max_pd_retries_ = max_pd_retries;
00387
00388 diagnostics_buffer_ = new unsigned char[buffer_size_];
00389
00390
00391 diagnostic_array_.status.reserve(slaves_.size() + 1);
00392 values_.reserve(10);
00393
00394 ethernet_interface_info_.initialize(interface);
00395
00396 diagnostics_thread_ = boost::thread(boost::bind(&EthercatHardwareDiagnosticsPublisher::diagnosticsThreadFunc, this));
00397 }
00398
00399 void EthercatHardwareDiagnosticsPublisher::publish(const unsigned char *buffer,
00400 const EthercatHardwareDiagnostics &diagnostics)
00401 {
00402 boost::try_to_lock_t try_lock;
00403 boost::unique_lock<boost::mutex> lock(diagnostics_mutex_, try_lock);
00404 if (lock.owns_lock())
00405 {
00406
00407 memcpy(diagnostics_buffer_, buffer, buffer_size_);
00408 diagnostics_ = diagnostics;
00409
00410 diagnostics_ready_ = true;
00411 diagnostics_cond_.notify_one();
00412 }
00413 }
00414
00415 void EthercatHardwareDiagnosticsPublisher::stop()
00416 {
00417 diagnostics_thread_.interrupt();
00418 diagnostics_thread_.join();
00419 publisher_.shutdown();
00420 }
00421
00422 void EthercatHardwareDiagnosticsPublisher::diagnosticsThreadFunc()
00423 {
00424 try
00425 {
00426 while (1)
00427 {
00428 boost::unique_lock<boost::mutex> lock(diagnostics_mutex_);
00429 while (!diagnostics_ready_)
00430 {
00431 diagnostics_cond_.wait(lock);
00432 }
00433 diagnostics_ready_ = false;
00434 publishDiagnostics();
00435 }
00436 }
00437 catch (boost::thread_interrupted const&)
00438 {
00439 return;
00440 }
00441 }
00442
00443 void EthercatHardwareDiagnosticsPublisher::timingInformation(diagnostic_updater::DiagnosticStatusWrapper &status,
00444 const string &key,
00445 const accumulator_set<double, stats<tag::max, tag::mean> > &acc,
00446 double max)
00447 {
00448 status.addf(key + " Avg (us)", "%5.4f", extract_result<tag::mean>(acc) * 1e6);
00449 status.addf(key + " 1 Sec Max (us)", "%5.4f", extract_result<tag::max>(acc) * 1e6);
00450 status.addf(key + " Max (us)", "%5.4f", max * 1e6);
00451 }
00452
00453 void EthercatHardwareDiagnosticsPublisher::publishDiagnostics()
00454 {
00455 ros::Time now(ros::Time::now());
00456
00457
00458 status_.clearSummary();
00459 status_.clear();
00460
00461 status_.name = "EtherCAT Master";
00462 if (diagnostics_.motors_halted_)
00463 {
00464 std::ostringstream desc;
00465 desc << "Motors halted";
00466 if (diagnostics_.halt_after_reset_)
00467 {
00468 desc << " soon after reset";
00469 }
00470 desc << " (" << diagnostics_.motors_halted_reason_ << ")";
00471 status_.summary(status_.ERROR, desc.str());
00472 }
00473 else
00474 {
00475 status_.summary(status_.OK, "OK");
00476 }
00477
00478 if (diagnostics_.pd_error_)
00479 {
00480 status_.mergeSummary(status_.ERROR, "Error sending proccess data");
00481 }
00482
00483 status_.add("Motors halted", diagnostics_.motors_halted_ ? "true" : "false");
00484 status_.addf("EtherCAT devices (expected)", "%d", num_ethercat_devices_);
00485 status_.addf("EtherCAT devices (current)", "%d", diagnostics_.device_count_);
00486 ethernet_interface_info_.publishDiagnostics(status_);
00487
00488
00489 status_.addf("Timeout (us)", "%d", timeout_);
00490 status_.addf("Max PD Retries", "%d", max_pd_retries_);
00491
00492
00493 if (num_ethercat_devices_ != diagnostics_.device_count_)
00494 {
00495 status_.mergeSummary(status_.WARN, "Number of EtherCAT devices changed");
00496 }
00497
00498 timingInformation(status_, "Roundtrip time", diagnostics_.txandrx_acc_,
00499 diagnostics_.max_txandrx_);
00500 if (diagnostics_.collect_extra_timing_)
00501 {
00502 timingInformation(status_, "Pack command time", diagnostics_.pack_command_acc_,
00503 diagnostics_.max_pack_command_);
00504 timingInformation(status_, "Unpack state time", diagnostics_.unpack_state_acc_,
00505 diagnostics_.max_unpack_state_);
00506 timingInformation(status_, "Publish time", diagnostics_.publish_acc_,
00507 diagnostics_.max_publish_);
00508 }
00509
00510 status_.addf("EtherCAT Process Data txandrx errors", "%d", diagnostics_.txandrx_errors_);
00511
00512 status_.addf("Reset motors service count", "%d", diagnostics_.reset_motors_service_count_);
00513 status_.addf("Halt motors service count", "%d", diagnostics_.halt_motors_service_count_);
00514 status_.addf("Halt motors error count", "%d", diagnostics_.halt_motors_error_count_);
00515
00516 {
00517 const struct netif_counters *c = &diagnostics_.counters_;
00518 status_.add("Input Thread", (diagnostics_.input_thread_is_stopped_ ? "Stopped" : "Running"));
00519 status_.addf("Sent Packets", "%llu", (unsigned long long) c->sent);
00520 status_.addf("Received Packets", "%llu", (unsigned long long) c->received);
00521 status_.addf("Collected Packets", "%llu", (unsigned long long) c->collected);
00522 status_.addf("Dropped Packets", "%llu", (unsigned long long) c->dropped);
00523 status_.addf("TX Errors", "%llu", (unsigned long long) c->tx_error);
00524 status_.addf("TX Network Down", "%llu", (unsigned long long) c->tx_net_down);
00525 status_.addf("TX Would Block", "%llu", (unsigned long long) c->tx_would_block);
00526 status_.addf("TX No Buffers", "%llu", (unsigned long long) c->tx_no_bufs);
00527 status_.addf("TX Queue Full", "%llu", (unsigned long long) c->tx_full);
00528 status_.addf("RX Runt Packet", "%llu", (unsigned long long) c->rx_runt_pkt);
00529 status_.addf("RX Not EtherCAT", "%llu", (unsigned long long) c->rx_not_ecat);
00530 status_.addf("RX Other EML", "%llu", (unsigned long long) c->rx_other_eml);
00531 status_.addf("RX Bad Index", "%llu", (unsigned long long) c->rx_bad_index);
00532 status_.addf("RX Bad Sequence", "%llu", (unsigned long long) c->rx_bad_seqnum);
00533 status_.addf("RX Duplicate Sequence", "%llu", (unsigned long long) c->rx_dup_seqnum);
00534 status_.addf("RX Duplicate Packet", "%llu", (unsigned long long) c->rx_dup_pkt);
00535 status_.addf("RX Bad Order", "%llu", (unsigned long long) c->rx_bad_order);
00536 status_.addf("RX Late Packet", "%llu", (unsigned long long) c->rx_late_pkt);
00537 status_.addf("RX Late Packet RTT", "%llu", (unsigned long long) c->rx_late_pkt_rtt_us);
00538
00539 double rx_late_pkt_rtt_us_avg = 0.0;
00540 if (c->rx_late_pkt > 0)
00541 {
00542 rx_late_pkt_rtt_us_avg = ((double) c->rx_late_pkt_rtt_us_sum) / ((double) c->rx_late_pkt);
00543 }
00544 status_.addf("RX Late Packet Avg RTT", "%f", rx_late_pkt_rtt_us_avg);
00545
00546
00547 if (c->dropped > last_dropped_packet_count_)
00548 {
00549 last_dropped_packet_time_ = now;
00550 last_dropped_packet_count_ = c->dropped;
00551 }
00552 }
00553
00554
00555 if ((last_dropped_packet_count_ > 0)
00556 && ((now - last_dropped_packet_time_).toSec() < dropped_packet_warning_hold_time_))
00557 {
00558 status_.mergeSummaryf(status_.WARN, "Dropped packets in last %d seconds",
00559 dropped_packet_warning_hold_time_);
00560 }
00561
00562 diagnostic_array_.status.clear();
00563 diagnostic_array_.status.push_back(status_);
00564
00565
00566 unsigned char *current = diagnostics_buffer_;
00567 for (unsigned int s = 0; s < slaves_.size(); ++s)
00568 {
00569 slaves_[s]->multiDiagnostics(diagnostic_array_.status, current);
00570 current += slaves_[s]->command_size_ + slaves_[s]->status_size_;
00571 }
00572
00573
00574 diagnostic_array_.header.stamp = ros::Time::now();
00575 publisher_.publish(diagnostic_array_);
00576 }
00577
00578 void EthercatHardware::update(bool reset, bool halt)
00579 {
00580
00581 ros::Time update_start_time(ros::Time::now());
00582
00583 unsigned char *this_buffer, *prev_buffer;
00584
00585
00586 this_buffer = this_buffer_;
00587
00588 if (halt)
00589 {
00590 ++diagnostics_.halt_motors_service_count_;
00591 haltMotors(false , "service request");
00592 }
00593
00594
00595
00596 const unsigned CYCLES_PER_HALT_RELEASE = 2;
00597 if (reset)
00598 {
00599 ++diagnostics_.reset_motors_service_count_;
00600 reset_state_ = CYCLES_PER_HALT_RELEASE * slaves_.size() + 5;
00601 last_reset_ = update_start_time;
00602 diagnostics_.halt_after_reset_ = false;
00603 }
00604 bool reset_devices = reset_state_ == CYCLES_PER_HALT_RELEASE * slaves_.size() + 3;
00605 if (reset_devices)
00606 {
00607 halt_motors_ = false;
00608 diagnostics_.motors_halted_ = false;
00609 diagnostics_.motors_halted_reason_ = "";
00610 diagnostics_.resetMaxTiming();
00611 diagnostics_.pd_error_ = false;
00612 }
00613
00614 for (unsigned int s = 0; s < slaves_.size(); ++s)
00615 {
00616
00617
00618 bool halt_device = halt_motors_ || ((s * CYCLES_PER_HALT_RELEASE + 1) < reset_state_);
00619 slaves_[s]->packCommand(this_buffer, halt_device, reset_devices);
00620 this_buffer += slaves_[s]->command_size_ + slaves_[s]->status_size_;
00621 }
00622
00623
00624 ros::Time txandrx_start_time(ros::Time::now());
00625 diagnostics_.pack_command_acc_((txandrx_start_time - update_start_time).toSec());
00626
00627
00628 bool success = txandrx_PD(buffer_size_, this_buffer_, max_pd_retries_);
00629
00630 ros::Time txandrx_end_time(ros::Time::now());
00631 diagnostics_.txandrx_acc_((txandrx_end_time - txandrx_start_time).toSec());
00632
00633 if (!success)
00634 {
00635
00636 haltMotors(true , "communication error");
00637 diagnostics_.pd_error_ = true;
00638 }
00639 else
00640 {
00641
00642 this_buffer = this_buffer_;
00643 prev_buffer = prev_buffer_;
00644 for (unsigned int s = 0; s < slaves_.size(); ++s)
00645 {
00646 if (!slaves_[s]->unpackState(this_buffer, prev_buffer) && !reset_devices)
00647 {
00648 haltMotors(true , "device error");
00649 }
00650 this_buffer += slaves_[s]->command_size_ + slaves_[s]->status_size_;
00651 prev_buffer += slaves_[s]->command_size_ + slaves_[s]->status_size_;
00652 }
00653
00654 if (reset_state_)
00655 --reset_state_;
00656
00657 unsigned char *tmp = this_buffer_;
00658 this_buffer_ = prev_buffer_;
00659 prev_buffer_ = tmp;
00660 }
00661
00662 ros::Time unpack_end_time;
00663 if (diagnostics_.collect_extra_timing_)
00664 {
00665 unpack_end_time = ros::Time::now();
00666 diagnostics_.unpack_state_acc_((unpack_end_time - txandrx_end_time).toSec());
00667 }
00668
00669 if ((update_start_time - last_published_) > ros::Duration(1.0))
00670 {
00671 last_published_ = update_start_time;
00672 publishDiagnostics();
00673 motor_publisher_.lock();
00674 motor_publisher_.msg_.data = halt_motors_;
00675 motor_publisher_.unlockAndPublish();
00676 }
00677
00678 if (diagnostics_.collect_extra_timing_)
00679 {
00680 ros::Time publish_end_time(ros::Time::now());
00681 diagnostics_.publish_acc_((publish_end_time - unpack_end_time).toSec());
00682 }
00683 }
00684
00685 void EthercatHardware::haltMotors(bool error, const char* reason)
00686 {
00687 if (!halt_motors_)
00688 {
00689
00690 motor_publisher_.lock();
00691 motor_publisher_.msg_.data = halt_motors_;
00692 motor_publisher_.unlockAndPublish();
00693
00694 diagnostics_.motors_halted_reason_ = reason;
00695 if (error)
00696 {
00697 ++diagnostics_.halt_motors_error_count_;
00698 if ((ros::Time::now() - last_reset_) < ros::Duration(0.5))
00699 {
00700
00701 diagnostics_.halt_after_reset_ = true;
00702 }
00703 }
00704 }
00705
00706 diagnostics_.motors_halted_ = true;
00707 halt_motors_ = true;
00708 }
00709
00710 void EthercatHardware::updateAccMax(double &max,
00711 const accumulator_set<double, stats<tag::max, tag::mean> > &acc)
00712 {
00713 max = std::max(max, extract_result<tag::max>(acc));
00714 }
00715
00716 void EthercatHardware::publishDiagnostics()
00717 {
00718
00719 updateAccMax(diagnostics_.max_pack_command_, diagnostics_.pack_command_acc_);
00720 updateAccMax(diagnostics_.max_txandrx_, diagnostics_.txandrx_acc_);
00721 updateAccMax(diagnostics_.max_unpack_state_, diagnostics_.unpack_state_acc_);
00722 updateAccMax(diagnostics_.max_publish_, diagnostics_.publish_acc_);
00723
00724
00725 diagnostics_.counters_ = ni_->counters;
00726 diagnostics_.input_thread_is_stopped_ = bool(ni_->is_stopped);
00727
00728 diagnostics_.motors_halted_ = halt_motors_;
00729
00730
00731 diagnostics_publisher_.publish(this_buffer_, diagnostics_);
00732
00733
00734 static accumulator_set<double, stats<tag::max, tag::mean> > blank;
00735 diagnostics_.pack_command_acc_ = blank;
00736 diagnostics_.txandrx_acc_ = blank;
00737 diagnostics_.unpack_state_acc_ = blank;
00738 diagnostics_.publish_acc_ = blank;
00739 }
00740
00741 boost::shared_ptr<EthercatDevice>
00742 EthercatHardware::configSlave(EtherCAT_SlaveHandler *sh)
00743 {
00744 static int start_address = 0x00010000;
00745 boost::shared_ptr<EthercatDevice> p;
00746 unsigned product_code = sh->get_product_code();
00747 unsigned serial = sh->get_serial();
00748 uint32_t revision = sh->get_revision();
00749 unsigned slave = sh->get_station_address() - 1;
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775
00776
00777
00778
00779
00780
00781 stringstream class_name_regex_str;
00782 class_name_regex_str << "(.*/)?" << product_code;
00783 boost::regex class_name_regex(class_name_regex_str.str(), boost::regex::extended);
00784
00785 std::vector<std::string> classes = device_loader_.getDeclaredClasses();
00786 std::string matching_class_name;
00787
00788 BOOST_FOREACH(const std::string &class_name, classes)
00789 {
00790 if (regex_match(class_name, class_name_regex))
00791 {
00792 if (matching_class_name.size() != 0)
00793 {
00794 ROS_ERROR("Found more than 1 EtherCAT driver for device with product code : %d", product_code);
00795 ROS_ERROR("First class name = '%s'. Second class name = '%s'",
00796 class_name.c_str(), matching_class_name.c_str());
00797 }
00798 matching_class_name = class_name;
00799 }
00800 }
00801
00802 if (matching_class_name.size() != 0)
00803 {
00804 ROS_WARN("Using device '%s' with product code %d",
00805 device_loader_.getClassDescription(matching_class_name).c_str(),
00806 product_code);
00807 try
00808 {
00809 p = device_loader_.createInstance(matching_class_name);
00810 }
00811 catch (pluginlib::LibraryLoadException &e)
00812 {
00813 p.reset();
00814 ROS_FATAL("Unable to load plugin for slave #%d, product code: %u (0x%X), serial: %u (0x%X), revision: %d (0x%X)",
00815 slave,
00816 product_code, product_code, serial, serial, revision, revision);
00817 ROS_FATAL("%s", e.what());
00818 }
00819 }
00820 else
00821 {
00822 if ((product_code == 0xbaddbadd) || (serial == 0xbaddbadd) || (revision == 0xbaddbadd))
00823 {
00824 ROS_FATAL("Note: 0xBADDBADD indicates that the value was not read correctly from device.");
00825 ROS_FATAL("Perhaps you should power-cycle the MCBs");
00826 }
00827 else
00828 {
00829 ROS_ERROR("Unable to load plugin for slave #%d, product code: %u (0x%X), serial: %u (0x%X), revision: %d (0x%X)",
00830 slave,
00831 product_code, product_code, serial, serial, revision, revision);
00832 ROS_ERROR("Possible classes:");
00833
00834 BOOST_FOREACH(const std::string &class_name, classes)
00835 {
00836 ROS_ERROR(" %s", class_name.c_str());
00837 }
00838
00839
00840
00841 }
00842 }
00843
00844 if (p != NULL)
00845 {
00846 p->construct(sh, start_address);
00847 }
00848
00849 return p;
00850 }
00851
00852 boost::shared_ptr<EthercatDevice>
00853 EthercatHardware::configNonEthercatDevice(const std::string &name, const std::string &type)
00854 {
00855 boost::shared_ptr<EthercatDevice> p;
00856 try
00857 {
00858 p = device_loader_.createInstance(type);
00859 }
00860 catch (pluginlib::LibraryLoadException &e)
00861 {
00862 p.reset();
00863 ROS_FATAL("Unable to load plugin for non-EtherCAT device '%s' with type: %s : %s",
00864 name.c_str(), type.c_str(), e.what());
00865 }
00866 if (p)
00867 {
00868 ROS_INFO("Creating non-EtherCAT device '%s' of type '%s'", name.c_str(), type.c_str());
00869 ros::NodeHandle nh(node_, "non_ethercat_devices/" + name);
00870 p->construct(nh);
00871 }
00872 return p;
00873 }
00874
00875
00876
00877 class MyXmlRpcValue : public XmlRpc::XmlRpcValue
00878 {
00879 public:
00880
00881 MyXmlRpcValue(XmlRpc::XmlRpcValue &value) :
00882 XmlRpc::XmlRpcValue(value)
00883 {
00884 }
00885
00886 XmlRpcValue::ValueStruct &getMap()
00887 {
00888 return *_value.asStruct;
00889 }
00890 };
00891
00892 void EthercatHardware::loadNonEthercatDevices()
00893 {
00894
00895
00896
00897
00898
00899
00900
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
00913
00914
00915 if (!node_.hasParam("non_ethercat_devices"))
00916 {
00917
00918 return;
00919 }
00920
00921 XmlRpc::XmlRpcValue devices;
00922 node_.getParam("non_ethercat_devices", devices);
00923 if (devices.getType() != XmlRpc::XmlRpcValue::TypeStruct)
00924 {
00925 ROS_ERROR("Rosparam 'non_ethercat_devices' is not a struct type");
00926 return;
00927 }
00928
00929 MyXmlRpcValue my_devices(devices);
00930 typedef XmlRpc::XmlRpcValue::ValueStruct::value_type map_item_t;
00931
00932 BOOST_FOREACH(map_item_t &item, my_devices.getMap())
00933 {
00934 const std::string & name(item.first);
00935 XmlRpc::XmlRpcValue & device_info(item.second);
00936
00937 if (device_info.getType() != XmlRpc::XmlRpcValue::TypeStruct)
00938 {
00939 ROS_ERROR("non_ethercat_devices/%s is not a struct type", name.c_str());
00940 continue;
00941 }
00942
00943 if (!device_info.hasMember("type"))
00944 {
00945 ROS_ERROR("non_ethercat_devices/%s 'type' element", name.c_str());
00946 continue;
00947 }
00948
00949 std::string type(static_cast<std::string> (device_info["type"]));
00950
00951 boost::shared_ptr<EthercatDevice> new_device =
00952 configNonEthercatDevice(name, type);
00953 if (new_device != NULL)
00954 {
00955 slaves_.push_back(new_device);
00956 }
00957 }
00958 }
00959
00960 void EthercatHardware::collectDiagnostics()
00961 {
00962 if (NULL == oob_com_)
00963 return;
00964
00965 {
00966 EC_Logic *logic = EC_Logic::instance();
00967 unsigned char p[1];
00968 uint16_t length = sizeof (p);
00969
00970
00971 APRD_Telegram status(logic->get_idx(),
00972 0,
00973 0,
00974 logic->get_wkc(),
00975 length,
00976 p);
00977
00978
00979 EC_Ethernet_Frame frame(&status);
00980 oob_com_->txandrx(&frame);
00981
00982
00983 diagnostics_.device_count_ = status.get_adp();
00984 }
00985
00986 for (unsigned i = 0; i < slaves_.size(); ++i)
00987 {
00988 boost::shared_ptr<EthercatDevice> d(slaves_[i]);
00989 d->collectDiagnostics(oob_com_);
00990 }
00991 }
00992
00993
00994
00995 void EthercatHardware::printCounters(std::ostream &os)
00996 {
00997 const struct netif_counters & c(ni_->counters);
00998 os << "netif counters :" << endl
00999 << " sent = " << c.sent << endl
01000 << " received = " << c.received << endl
01001 << " collected = " << c.collected << endl
01002 << " dropped = " << c.dropped << endl
01003 << " tx_error = " << c.tx_error << endl
01004 << " tx_net_down = " << c.tx_net_down << endl
01005 << " tx_would_block= " << c.tx_would_block << endl
01006 << " tx_no_bufs = " << c.tx_no_bufs << endl
01007 << " tx_full = " << c.tx_full << endl
01008 << " rx_runt_pkt = " << c.rx_runt_pkt << endl
01009 << " rx_not_ecat = " << c.rx_not_ecat << endl
01010 << " rx_other_eml = " << c.rx_other_eml << endl
01011 << " rx_bad_index = " << c.rx_bad_index << endl
01012 << " rx_bad_seqnum = " << c.rx_bad_seqnum << endl
01013 << " rx_dup_seqnum = " << c.rx_dup_seqnum << endl
01014 << " rx_dup_pkt = " << c.rx_dup_pkt << endl
01015 << " rx_bad_order = " << c.rx_bad_order << endl
01016 << " rx_late_pkt = " << c.rx_late_pkt << endl;
01017 }
01018
01019 bool EthercatHardware::txandrx_PD(unsigned buffer_size, unsigned char* buffer, unsigned tries)
01020 {
01021
01022 bool success = false;
01023 for (unsigned i = 0; i < tries && !success; ++i)
01024 {
01025
01026 success = em_->txandrx_PD(buffer_size_, this_buffer_);
01027 if (!success)
01028 {
01029 ++diagnostics_.txandrx_errors_;
01030 }
01031
01032 oob_com_->tx();
01033 }
01034 return success;
01035 }
01036
01037 bool EthercatHardware::publishTrace(int position, const string &reason, unsigned level,
01038 unsigned delay)
01039 {
01040 if (position >= (int) slaves_.size())
01041 {
01042 ROS_WARN("Invalid device position %d. Use 0-%d, or -1.", position, int(slaves_.size()) - 1);
01043 return false;
01044 }
01045
01046 if (level > 2)
01047 {
01048 ROS_WARN("Invalid level : %d. Using level=2 (ERROR).", level);
01049 level = 2;
01050 }
01051
01052 string new_reason("Manually triggered : " + reason);
01053
01054 bool retval = false;
01055 if (position < 0)
01056 {
01057 for (unsigned i = 0; i < slaves_.size(); ++i)
01058 {
01059 if (slaves_[i]->publishTrace(new_reason, level, delay))
01060 {
01061 retval = true;
01062 }
01063 }
01064 }
01065 else
01066 {
01067 retval = slaves_[position]->publishTrace(new_reason, level, delay);
01068 if (!retval)
01069 {
01070 ROS_WARN("Device %d does not support publishing trace", position);
01071 }
01072 }
01073 return retval;
01074 }