ethercat_manager.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2015, Jonathan Meyer
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions are met:
00006  *   * Redistributions of source code must retain the above copyright notice,
00007  *     this list of conditions and the following disclaimer.
00008  *   * Redistributions in binary form must reproduce the above copyright
00009  *     notice, this list of conditions and the following disclaimer in the
00010  *     documentation and/or other materials provided with the distribution.
00011  *   * Neither the names of Tokyo Opensource Robotics Kyokai Association. nor the names of its
00012  *     contributors may be used to endorse or promote products derived from
00013  *     this software without specific prior written permission.
00014  *
00015  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00016  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00017  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00018  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00019  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00020  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00021  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00022  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00023  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00024  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00025  * POSSIBILITY OF SUCH DAMAGE.
00026  */
00027 
00028 // copied from https://github.com/ros-industrial/robotiq/blob/jade-devel/robotiq_ethercat/src/ethercat_manager.cpp
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; // 1 ms
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   /* one ore more slaves are not responding */
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         /* re-check state */
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   // 1ms in nanoseconds
00128   double period = THREAD_SLEEP_TIME * 1000;
00129   // get curren ttime
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     // check overrun
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     //usleep(THREAD_SLEEP_TIME);
00157     clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &tick, NULL);
00158     timespecInc(tick, period);
00159     //printf("%24.12f %14.10f [msec] %02x %02x %02x %02x\n", tick.tv_sec+double(tick.tv_nsec)/NSEC_PER_SECOND, overrun_time*1000, ec_slave[1].outputs[24], ec_slave[1].outputs[23], ec_slave[1].outputs[22], ec_slave[1].outputs[21]);
00160   }
00161 }
00162 
00163 } // end of anonymous namespace
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    // construction failed
00182    throw EtherCatError("Could not initialize SOEM");
00183  }
00184 }
00185 
00186 EtherCatManager::~EtherCatManager()
00187 {
00188   stop_flag_ = true;
00189 
00190   // Request init operational state for all slaves
00191   ec_slave[0].state = EC_STATE_INIT;
00192 
00193   /* request init state for all slaves */
00194   ec_writestate(0);
00195 
00196   //stop SOEM, close socket
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   // Copy string contents because SOEM library doesn't 
00204   // practice const correctness
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   /* find and auto-config slaves */
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     // MINAS-A5B Serial Man = 066Fh, ID = [5/D]****[0/4/8][0-F]*
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     SET PDO maping 4    SX-DSV02470 p.52
00244                         Index     Size(bit)     Name
00245     RxPDO (1603h)       6040h 00h 16 Controlword
00246                         6060h 00h  8 Modes of operation
00247                         6071h 00h 16 Target Torque
00248                         6072h 00h 16 Max torque
00249                         607Ah 00h 32 Target Position
00250                         6080h 00h 32 Max motor speed
00251                         60B8h 00h 16 Touch probe function
00252                         60FFh 00h 32 Target Velocity
00253     TxPDO (1A03h)
00254                         603Fh 00h 16 Error code
00255                         6041h 00h 16 Statusword
00256                         6061h 00h  8 Modes of operation display
00257                         6064h 00h 32 Position actual value
00258                         606Ch 00h 32 Velocity actual value
00259                         6077h 00h 16 Torque actual value
00260                         60B9h 00h 16 Touch probe status
00261                         60BAh 00h 32 Touch probe pos1 pos val
00262                         60FDh 00h 32 Digital inputs
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   // extend PDO mapping 4 see p. 53 of SX-DSV02470
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       // add position offset (60B0 / 00h / I32)
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   // use PDO mapping 4
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       // set 0 change PDO mapping index
00299       num_pdo = 0;
00300       ret += ec_SDOwrite(cnt, 0x1c12, 0x00, FALSE, sizeof(num_pdo), &num_pdo, EC_TIMEOUTRXM);
00301       // set to default PDO mapping 4
00302       uint16_t idx_rxpdo = 0x1603;
00303       ret += ec_SDOwrite(cnt, 0x1c12, 0x01, FALSE, sizeof(idx_rxpdo), &idx_rxpdo, EC_TIMEOUTRXM);
00304       // set number of assigned PDOs
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       // set 0 change PDO mapping index
00310       num_pdo = 0;
00311       ret += ec_SDOwrite(cnt, 0x1c13, 0x00, FALSE, sizeof(num_pdo), &num_pdo, EC_TIMEOUTRXM);
00312       // set to default PDO mapping 4
00313       uint16_t idx_txpdo = 0x1a03;
00314       ret += ec_SDOwrite(cnt, 0x1c13, 0x01, FALSE, sizeof(idx_txpdo), &idx_txpdo, EC_TIMEOUTRXM);
00315       // set number of assigned PDOs
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   // configure IOMap
00322   int iomap_size = ec_config_map(iomap_);
00323   printf("SOEM IOMap size: %d\n", iomap_size);
00324 
00325   // locates dc slaves - ???
00326   ec_configdc();
00327 
00328   // '0' here addresses all slaves
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       This section attempts to bring all slaves to operational status. It does so
00337       by attempting to set the status of all slaves (ec_slave[0]) to operational,
00338       then proceeding through 40 send/recieve cycles each waiting up to 50 ms for a
00339       response about the status. 
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); // 50 ms wait for state check
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 ) { // ret = Workcounter from last slave response
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 


ethercat_manager
Author(s): Jonathan Meyer
autogenerated on Sat Jun 8 2019 19:23:26