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 #include "ethercat_manager/ethercat_manager.h"
00032
00033 #include <unistd.h>
00034 #include <stdio.h>
00035 #include <time.h>
00036
00037 #include <boost/ref.hpp>
00038 #include <boost/interprocess/sync/scoped_lock.hpp>
00039
00040 #include <soem/ethercattype.h>
00041 #include <soem/nicdrv.h>
00042 #include <soem/ethercatbase.h>
00043 #include <soem/ethercatmain.h>
00044 #include <soem/ethercatdc.h>
00045 #include <soem/ethercatcoe.h>
00046 #include <soem/ethercatfoe.h>
00047 #include <soem/ethercatconfig.h>
00048 #include <soem/ethercatprint.h>
00049
00050 namespace
00051 {
00052 static const unsigned THREAD_SLEEP_TIME = 1000;
00053 static const unsigned EC_TIMEOUTMON = 500;
00054 static const int NSEC_PER_SECOND = 1e+9;
00055 void timespecInc(struct timespec &tick, int nsec)
00056 {
00057 tick.tv_nsec += nsec;
00058 while (tick.tv_nsec >= NSEC_PER_SECOND)
00059 {
00060 tick.tv_nsec -= NSEC_PER_SECOND;
00061 tick.tv_sec++;
00062 }
00063 }
00064
00065 void handleErrors()
00066 {
00067
00068 ec_group[0].docheckstate = FALSE;
00069 ec_readstate();
00070 for (int slave = 1; slave <= ec_slavecount; slave++)
00071 {
00072 if ((ec_slave[slave].group == 0) && (ec_slave[slave].state != EC_STATE_OPERATIONAL))
00073 {
00074 ec_group[0].docheckstate = TRUE;
00075 if (ec_slave[slave].state == (EC_STATE_SAFE_OP + EC_STATE_ERROR))
00076 {
00077 fprintf(stderr, "ERROR : slave %d is in SAFE_OP + ERROR, attempting ack.\n", slave);
00078 ec_slave[slave].state = (EC_STATE_SAFE_OP + EC_STATE_ACK);
00079 ec_writestate(slave);
00080 }
00081 else if(ec_slave[slave].state == EC_STATE_SAFE_OP)
00082 {
00083 fprintf(stderr, "WARNING : slave %d is in SAFE_OP, change to OPERATIONAL.\n", slave);
00084 ec_slave[slave].state = EC_STATE_OPERATIONAL;
00085 ec_writestate(slave);
00086 }
00087 else if(ec_slave[slave].state > 0)
00088 {
00089 if (ec_reconfig_slave(slave, EC_TIMEOUTMON))
00090 {
00091 ec_slave[slave].islost = FALSE;
00092 printf("MESSAGE : slave %d reconfigured\n",slave);
00093 }
00094 }
00095 else if(!ec_slave[slave].islost)
00096 {
00097
00098 ec_statecheck(slave, EC_STATE_OPERATIONAL, EC_TIMEOUTRET);
00099 if (!ec_slave[slave].state)
00100 {
00101 ec_slave[slave].islost = TRUE;
00102 fprintf(stderr, "ERROR : slave %d lost\n",slave);
00103 }
00104 }
00105 }
00106 if (ec_slave[slave].islost)
00107 {
00108 if(!ec_slave[slave].state)
00109 {
00110 if (ec_recover_slave(slave, EC_TIMEOUTMON))
00111 {
00112 ec_slave[slave].islost = FALSE;
00113 printf("MESSAGE : slave %d recovered\n",slave);
00114 }
00115 }
00116 else
00117 {
00118 ec_slave[slave].islost = FALSE;
00119 printf("MESSAGE : slave %d found\n",slave);
00120 }
00121 }
00122 }
00123 }
00124
00125 void cycleWorker(boost::mutex& mutex, bool& stop_flag)
00126 {
00127
00128 double period = THREAD_SLEEP_TIME * 1000;
00129
00130 struct timespec tick;
00131 clock_gettime(CLOCK_REALTIME, &tick);
00132 timespecInc(tick, period);
00133 while (!stop_flag)
00134 {
00135 int expected_wkc = (ec_group[0].outputsWKC * 2) + ec_group[0].inputsWKC;
00136 int sent, wkc;
00137 {
00138 boost::mutex::scoped_lock lock(mutex);
00139 sent = ec_send_processdata();
00140 wkc = ec_receive_processdata(EC_TIMEOUTRET);
00141 }
00142
00143 if (wkc < expected_wkc)
00144 {
00145 handleErrors();
00146 }
00147
00148
00149 struct timespec before;
00150 clock_gettime(CLOCK_REALTIME, &before);
00151 double overrun_time = (before.tv_sec + double(before.tv_nsec)/NSEC_PER_SECOND) - (tick.tv_sec + double(tick.tv_nsec)/NSEC_PER_SECOND);
00152 if (overrun_time > 0.0)
00153 {
00154 fprintf(stderr, " overrun: %f\n", overrun_time);
00155 }
00156
00157 clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &tick, NULL);
00158 timespecInc(tick, period);
00159
00160 }
00161 }
00162
00163 }
00164
00165
00166 namespace ethercat {
00167
00168 EtherCatManager::EtherCatManager(const std::string& ifname)
00169 : ifname_(ifname),
00170 num_clients_(0),
00171 stop_flag_(false)
00172 {
00173 if (initSoem(ifname))
00174 {
00175 cycle_thread_ = boost::thread(cycleWorker,
00176 boost::ref(iomap_mutex_),
00177 boost::ref(stop_flag_));
00178 }
00179 else
00180 {
00181
00182 throw EtherCatError("Could not initialize SOEM");
00183 }
00184 }
00185
00186 EtherCatManager::~EtherCatManager()
00187 {
00188 stop_flag_ = true;
00189
00190
00191 ec_slave[0].state = EC_STATE_INIT;
00192
00193
00194 ec_writestate(0);
00195
00196
00197 ec_close();
00198 cycle_thread_.join();
00199 }
00200
00201 #define IF_MINAS(_ec_slave) (((int)_ec_slave.eep_man == 0x066f) && ((((0xf0000000&(int)ec_slave[cnt].eep_id)>>28) == 0x5) || (((0xf0000000&(int)ec_slave[cnt].eep_id)>>28) == 0xD)))
00202 bool EtherCatManager::initSoem(const std::string& ifname) {
00203
00204
00205 const static unsigned MAX_BUFF_SIZE = 1024;
00206 char buffer[MAX_BUFF_SIZE];
00207 size_t name_size = ifname_.size();
00208 if (name_size > sizeof(buffer) - 1)
00209 {
00210 fprintf(stderr, "Ifname %s exceeds maximum size of %u bytes\n", ifname_.c_str(), MAX_BUFF_SIZE);
00211 return false;
00212 }
00213 std::strncpy(buffer, ifname_.c_str(), MAX_BUFF_SIZE);
00214
00215 printf("Initializing etherCAT master\n");
00216
00217 if (!ec_init(buffer))
00218 {
00219 fprintf(stderr, "Could not initialize ethercat driver\n");
00220 return false;
00221 }
00222
00223
00224 if (ec_config_init(FALSE) <= 0)
00225 {
00226 fprintf(stderr, "No slaves found on %s\n", ifname_.c_str());
00227 return false;
00228 }
00229
00230 printf("SOEM found and configured %d slaves\n", ec_slavecount);
00231 for( int cnt = 1 ; cnt <= ec_slavecount ; cnt++)
00232 {
00233
00234 printf(" Man: %8.8x ID: %8.8x Rev: %8.8x %s\n", (int)ec_slave[cnt].eep_man, (int)ec_slave[cnt].eep_id, (int)ec_slave[cnt].eep_rev, IF_MINAS(ec_slave[cnt])?" MINAS Drivers":"");
00235 if(IF_MINAS(ec_slave[cnt])) {
00236 num_clients_++;
00237 }
00238 }
00239 printf("Found %d MINAS Drivers\n", num_clients_);
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264 if (ec_statecheck(0, EC_STATE_PRE_OP, EC_TIMEOUTSTATE*4) != EC_STATE_PRE_OP)
00265 {
00266 fprintf(stderr, "Could not set EC_STATE_PRE_OP\n");
00267 return false;
00268 }
00269
00270
00271 for( int cnt = 1 ; cnt <= ec_slavecount ; cnt++)
00272 {
00273 if (! IF_MINAS(ec_slave[cnt])) continue;
00274 int ret = 0, l;
00275 uint8_t num_entries;
00276 l = sizeof(num_entries);
00277 ret += ec_SDOread(cnt, 0x1603, 0x00, FALSE, &l, &num_entries, EC_TIMEOUTRXM);
00278 printf("len = %d\n", num_entries);
00279 num_entries = 0;
00280 ret += ec_SDOwrite(cnt, 0x1603, 0x00, FALSE, sizeof(num_entries), &num_entries, EC_TIMEOUTRXM);
00281
00282 uint32_t mapping;
00283 mapping = 0x60B00020;
00284 ret += ec_SDOwrite(cnt, 0x1603, 0x09, FALSE, sizeof(mapping), &mapping, EC_TIMEOUTRXM);
00285
00286 num_entries = 9;
00287 ret += ec_SDOwrite(cnt, 0x1603, 0x00, FALSE, sizeof(num_entries), &num_entries, EC_TIMEOUTRXM);
00288 ret += ec_SDOread(cnt, 0x1603, 0x00, FALSE, &l, &num_entries, EC_TIMEOUTRXM);
00289 printf("len = %d\n", num_entries);
00290 }
00291
00292
00293 for( int cnt = 1 ; cnt <= ec_slavecount ; cnt++)
00294 {
00295 if (! IF_MINAS(ec_slave[cnt])) continue;
00296 int ret = 0, l;
00297 uint8_t num_pdo ;
00298
00299 num_pdo = 0;
00300 ret += ec_SDOwrite(cnt, 0x1c12, 0x00, FALSE, sizeof(num_pdo), &num_pdo, EC_TIMEOUTRXM);
00301
00302 uint16_t idx_rxpdo = 0x1603;
00303 ret += ec_SDOwrite(cnt, 0x1c12, 0x01, FALSE, sizeof(idx_rxpdo), &idx_rxpdo, EC_TIMEOUTRXM);
00304
00305 num_pdo = 1;
00306 ret += ec_SDOwrite(cnt, 0x1c12, 0x00, FALSE, sizeof(num_pdo), &num_pdo, EC_TIMEOUTRXM);
00307 printf("RxPDO mapping object index %d = %04x ret=%d\n", cnt, idx_rxpdo, ret);
00308
00309
00310 num_pdo = 0;
00311 ret += ec_SDOwrite(cnt, 0x1c13, 0x00, FALSE, sizeof(num_pdo), &num_pdo, EC_TIMEOUTRXM);
00312
00313 uint16_t idx_txpdo = 0x1a03;
00314 ret += ec_SDOwrite(cnt, 0x1c13, 0x01, FALSE, sizeof(idx_txpdo), &idx_txpdo, EC_TIMEOUTRXM);
00315
00316 num_pdo = 1;
00317 ret += ec_SDOwrite(cnt, 0x1c13, 0x00, FALSE, sizeof(num_pdo), &num_pdo, EC_TIMEOUTRXM);
00318 printf("TxPDO mapping object index %d = %04x ret=%d\n", cnt, idx_txpdo, ret);
00319 }
00320
00321
00322 int iomap_size = ec_config_map(iomap_);
00323 printf("SOEM IOMap size: %d\n", iomap_size);
00324
00325
00326 ec_configdc();
00327
00328
00329 if (ec_statecheck(0, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE*4) != EC_STATE_SAFE_OP)
00330 {
00331 fprintf(stderr, "Could not set EC_STATE_SAFE_OP\n");
00332 return false;
00333 }
00334
00335
00336
00337
00338
00339
00340
00341 ec_slave[0].state = EC_STATE_OPERATIONAL;
00342 ec_send_processdata();
00343 ec_receive_processdata(EC_TIMEOUTRET);
00344
00345 ec_writestate(0);
00346 int chk = 40;
00347 do {
00348 ec_send_processdata();
00349 ec_receive_processdata(EC_TIMEOUTRET);
00350 ec_statecheck(0, EC_STATE_OPERATIONAL, 50000);
00351 } while (chk-- && (ec_slave[0].state != EC_STATE_OPERATIONAL));
00352
00353 if(ec_statecheck(0,EC_STATE_OPERATIONAL, EC_TIMEOUTSTATE) != EC_STATE_OPERATIONAL)
00354 {
00355 fprintf(stderr, "OPERATIONAL state not set, exiting\n");
00356 return false;
00357 }
00358
00359 ec_readstate();
00360 for( int cnt = 1 ; cnt <= ec_slavecount ; cnt++)
00361 {
00362 if (! IF_MINAS(ec_slave[cnt])) continue;
00363 printf("\nSlave:%d\n Name:%s\n Output size: %dbits\n Input size: %dbits\n State: %d\n Delay: %d[ns]\n Has DC: %d\n",
00364 cnt, ec_slave[cnt].name, ec_slave[cnt].Obits, ec_slave[cnt].Ibits,
00365 ec_slave[cnt].state, ec_slave[cnt].pdelay, ec_slave[cnt].hasdc);
00366 if (ec_slave[cnt].hasdc) printf(" DCParentport:%d\n", ec_slave[cnt].parentport);
00367 printf(" Activeports:%d.%d.%d.%d\n", (ec_slave[cnt].activeports & 0x01) > 0 ,
00368 (ec_slave[cnt].activeports & 0x02) > 0 ,
00369 (ec_slave[cnt].activeports & 0x04) > 0 ,
00370 (ec_slave[cnt].activeports & 0x08) > 0 );
00371 printf(" Configured address: %4.4x\n", ec_slave[cnt].configadr);
00372 }
00373
00374 for( int cnt = 1 ; cnt <= ec_slavecount ; cnt++)
00375 {
00376 if (! IF_MINAS(ec_slave[cnt])) continue;
00377 int ret = 0, l;
00378 uint16_t sync_mode;
00379 uint32_t cycle_time;
00380 uint32_t minimum_cycle_time;
00381 uint32_t sync0_cycle_time;
00382 l = sizeof(sync_mode);
00383 ret += ec_SDOread(cnt, 0x1c32, 0x01, FALSE, &l, &sync_mode, EC_TIMEOUTRXM);
00384 l = sizeof(cycle_time);
00385 ret += ec_SDOread(cnt, 0x1c32, 0x01, FALSE, &l, &cycle_time, EC_TIMEOUTRXM);
00386 l = sizeof(minimum_cycle_time);
00387 ret += ec_SDOread(cnt, 0x1c32, 0x05, FALSE, &l, &minimum_cycle_time, EC_TIMEOUTRXM);
00388 l = sizeof(sync0_cycle_time);
00389 ret += ec_SDOread(cnt, 0x1c32, 0x0a, FALSE, &l, &sync0_cycle_time, EC_TIMEOUTRXM);
00390 printf("PDO syncmode %02x, cycle time %d ns (min %d), sync0 cycle time %d ns, ret = %d\n", sync_mode, cycle_time, minimum_cycle_time, sync0_cycle_time, ret);
00391 }
00392
00393 printf("\nFinished configuration successfully\n");
00394 return true;
00395 }
00396
00397 int EtherCatManager::getNumClinets() const
00398 {
00399 return num_clients_;
00400 }
00401
00402 void EtherCatManager::getStatus(int slave_no, std::string &name, int &eep_man, int &eep_id, int &eep_rev, int &obits, int &ibits, int &state, int &pdelay, int &hasdc, int &activeports, int &configadr) const
00403 {
00404 if (slave_no > ec_slavecount) {
00405 fprintf(stderr, "ERROR : slave_no(%d) is larger than ec_slavecount(%d)\n", slave_no, ec_slavecount);
00406 exit(1);
00407 }
00408 name = std::string(ec_slave[slave_no].name);
00409 eep_man = (int)ec_slave[slave_no].eep_man;
00410 eep_id = (int)ec_slave[slave_no].eep_id;
00411 eep_rev = (int)ec_slave[slave_no].eep_rev;
00412 obits = ec_slave[slave_no].Obits;
00413 ibits = ec_slave[slave_no].Ibits;
00414 state = ec_slave[slave_no].state;
00415 pdelay = ec_slave[slave_no].pdelay;
00416 hasdc = ec_slave[slave_no].hasdc;
00417 activeports = ec_slave[slave_no].activeports;
00418 configadr = ec_slave[slave_no].configadr;
00419 }
00420
00421 void EtherCatManager::write(int slave_no, uint8_t channel, uint8_t value)
00422 {
00423 boost::mutex::scoped_lock lock(iomap_mutex_);
00424 ec_slave[slave_no].outputs[channel] = value;
00425 }
00426
00427 uint8_t EtherCatManager::readInput(int slave_no, uint8_t channel) const
00428 {
00429 boost::mutex::scoped_lock lock(iomap_mutex_);
00430 if (slave_no > ec_slavecount) {
00431 fprintf(stderr, "ERROR : slave_no(%d) is larger than ec_slavecount(%d)\n", slave_no, ec_slavecount);
00432 exit(1);
00433 }
00434 if (channel*8 >= ec_slave[slave_no].Ibits) {
00435 fprintf(stderr, "ERROR : channel(%d) is larget thatn Input bits (%d)\n", channel*8, ec_slave[slave_no].Ibits);
00436 exit(1);
00437 }
00438 return ec_slave[slave_no].inputs[channel];
00439 }
00440
00441 uint8_t EtherCatManager::readOutput(int slave_no, uint8_t channel) const
00442 {
00443 boost::mutex::scoped_lock lock(iomap_mutex_);
00444 if (slave_no > ec_slavecount) {
00445 fprintf(stderr, "ERROR : slave_no(%d) is larger than ec_slavecount(%d)\n", slave_no, ec_slavecount);
00446 exit(1);
00447 }
00448 if (channel*8 >= ec_slave[slave_no].Obits) {
00449 fprintf(stderr, "ERROR : channel(%d) is larget thatn Output bits (%d)\n", channel*8, ec_slave[slave_no].Obits);
00450 exit(1);
00451 }
00452 return ec_slave[slave_no].outputs[channel];
00453 }
00454
00455 template <typename T>
00456 uint8_t EtherCatManager::writeSDO(int slave_no, uint16_t index, uint8_t subidx, T value) const
00457 {
00458 int ret;
00459 ret = ec_SDOwrite(slave_no, index, subidx, FALSE, sizeof(value), &value, EC_TIMEOUTSAFE);
00460 return ret;
00461 }
00462
00463 template <typename T>
00464 T EtherCatManager::readSDO(int slave_no, uint16_t index, uint8_t subidx) const
00465 {
00466 int ret, l;
00467 T val;
00468 l = sizeof(val);
00469 ret = ec_SDOread(slave_no, index, subidx, FALSE, &l, &val, EC_TIMEOUTRXM);
00470 if ( ret <= 0 ) {
00471 fprintf(stderr, "Failed to read from ret:%d, slave_no:%d, index:0x%04x, subidx:0x%02x\n", ret, slave_no, index, subidx);
00472 }
00473 return val;
00474 }
00475
00476 template uint8_t EtherCatManager::writeSDO<char> (int slave_no, uint16_t index, uint8_t subidx, char value) const;
00477 template uint8_t EtherCatManager::writeSDO<int> (int slave_no, uint16_t index, uint8_t subidx, int value) const;
00478 template uint8_t EtherCatManager::writeSDO<short> (int slave_no, uint16_t index, uint8_t subidx, short value) const;
00479 template uint8_t EtherCatManager::writeSDO<long> (int slave_no, uint16_t index, uint8_t subidx, long value) const;
00480 template uint8_t EtherCatManager::writeSDO<unsigned char> (int slave_no, uint16_t index, uint8_t subidx, unsigned char value) const;
00481 template uint8_t EtherCatManager::writeSDO<unsigned int> (int slave_no, uint16_t index, uint8_t subidx, unsigned int value) const;
00482 template uint8_t EtherCatManager::writeSDO<unsigned short> (int slave_no, uint16_t index, uint8_t subidx, unsigned short value) const;
00483 template uint8_t EtherCatManager::writeSDO<unsigned long> (int slave_no, uint16_t index, uint8_t subidx, unsigned long value) const;
00484
00485 template char EtherCatManager::readSDO<char> (int slave_no, uint16_t index, uint8_t subidx) const;
00486 template int EtherCatManager::readSDO<int> (int slave_no, uint16_t index, uint8_t subidx) const;
00487 template short EtherCatManager::readSDO<short> (int slave_no, uint16_t index, uint8_t subidx) const;
00488 template long EtherCatManager::readSDO<long> (int slave_no, uint16_t index, uint8_t subidx) const;
00489 template unsigned char EtherCatManager::readSDO<unsigned char> (int slave_no, uint16_t index, uint8_t subidx) const;
00490 template unsigned int EtherCatManager::readSDO<unsigned int> (int slave_no, uint16_t index, uint8_t subidx) const;
00491 template unsigned short EtherCatManager::readSDO<unsigned short> (int slave_no, uint16_t index, uint8_t subidx) const;
00492 template unsigned long EtherCatManager::readSDO<unsigned long> (int slave_no, uint16_t index, uint8_t subidx) const;
00493
00494 }
00495