SDO.cpp
Go to the documentation of this file.
00001 // this is for emacs file handling -*- mode: c++; indent-tabs-mode: nil -*-
00002 
00003 // -- BEGIN LICENSE BLOCK ----------------------------------------------
00004 // This file is part of the SCHUNK Canopen Driver suite.
00005 //
00006 // This program is free software licensed under the LGPL
00007 // (GNU LESSER GENERAL PUBLIC LICENSE Version 3).
00008 // You can find a copy of this license in LICENSE folder in the top
00009 // directory of the source code.
00010 //
00011 // © Copyright 2016 SCHUNK GmbH, Lauffen/Neckar Germany
00012 // © Copyright 2016 FZI Forschungszentrum Informatik, Karlsruhe, Germany
00013 // -- END LICENSE BLOCK ------------------------------------------------
00014 
00015 //----------------------------------------------------------------------
00023 //----------------------------------------------------------------------
00024 
00025 #include "SDO.h"
00026 #include "ds301.h"
00027 
00028 #include "Logging.h"
00029 
00030 #include <boost/thread/mutex.hpp>
00031 
00032 namespace icl_hardware {
00033 namespace canopen_schunk {
00034 
00035 // static initializer of error_map
00036 std::map <uint32_t, std::string> SDO::m_error_map;
00037 
00038 SDO::SDO(const uint8_t& node_id,const CanDevPtr& can_device)
00039   : m_node_id(node_id),
00040     m_can_device(can_device),
00041     m_response_wait_time_ms(100),
00042     m_data_update_received(false)
00043 {
00044 }
00045 
00046 void SDO::update(const CanMsg& msg)
00047 {
00048   uint8_t node_id = msg.id - ds301::ID_TSDO_MIN + 1;
00049 
00050   if (node_id != m_node_id)
00051   {
00052     std::stringstream ss;
00053     ss << "SDO Update called with wrong canopen ID. Received ID: " << static_cast<int>(node_id) << " Node ID: " << static_cast<int>(m_node_id) << ". Update ignored.";
00054     LOGGING_ERROR_C(CanOpen,SDO, ss.str() << endl);
00055     return;
00056   }
00057 
00058   boost::mutex::scoped_lock(m_data_buffer_mutex);
00059 
00060   if (msg.dlc != 8)
00061   {
00062     std::stringstream ss;
00063     ss << "Unexpected length " << static_cast<int>(msg.dlc) << " of SDO response. Expected 8.";
00064     throw ResponseException(0,0,ss.str());
00065   }
00066 
00067   if (m_data_update_received)
00068   {
00069     std::stringstream ss;
00070     ss << "Data buffer contains unprocessed data which will be overwritten.";
00071     LOGGING_WARNING_C (CanOpen, SDO, ss.str() << endl);
00072   }
00073 
00074   m_data_buffer.clear();
00075   for (size_t i = 0; i < msg.dlc; ++i)
00076   {
00077     m_data_buffer.push_back(msg.data[i]);
00078   }
00079 
00080   m_data_update_received = true;
00081   m_data_buffer_updated_cond.notify_one();
00082 }
00083 
00084 bool SDO::download(const bool normal_transfer,
00085                    const uint16_t index,
00086                    const uint8_t subindex,
00087                    const std::vector< uint8_t >& usrdata)
00088 {
00089   //TODO: support normal transfer as well
00090   size_t num_bytes = usrdata.size();
00091   if (num_bytes > 4 || normal_transfer)
00092   {
00093     std::string mode = "expedited";
00094     if (normal_transfer)
00095     {
00096       mode = "segmented";
00097     }
00098     std::stringstream ss;
00099     ss << "So far only expedited transfers with maximum 4 data bytes are supported. "
00100        << "However, " << mode << " transfer of " << num_bytes
00101        << " bytes was requested. Aborting download";
00102     throw ProtocolException (index, subindex, ss.str());
00103   }
00104 
00105   if (num_bytes == 0)
00106   {
00107     throw ProtocolException (index, subindex, "Empty data message passed to download function.");
00108   }
00109 
00110   CanMsg msg;
00111 
00112   msg.id = ds301::ID_RSDO_MIN + m_node_id - 1;
00113   msg.dlc = 8;
00114   msg.rtr = 0;
00115   switch (num_bytes)
00116   {
00117   case 1:
00118       msg.data[0] = SDO_SEG_REQ_INIT_DOWNLOAD_1BYTE;
00119       break;
00120   case 2:
00121       msg.data[0] = SDO_SEG_REQ_INIT_DOWNLOAD_2BYTE;
00122       break;
00123   case 3:
00124       msg.data[0] = SDO_SEG_REQ_INIT_DOWNLOAD_3BYTE;
00125       break;
00126   case 4:
00127       msg.data[0] = SDO_SEG_REQ_INIT_DOWNLOAD_4BYTE;
00128       break;
00129   default:
00130       msg.data[0] = SDO_SEG_REQ_INIT_DOWNLOAD_xBYTE;
00131       break;
00132   }
00133 
00134   msg.data[1] = index & 0xff;
00135   msg.data[2] = index >> 8;
00136   msg.data[3] = subindex;
00137   for (size_t i = 0; i < 4; ++i)
00138   {
00139     if (i < num_bytes)
00140     {
00141       msg.data[4 + i] = usrdata[i];
00142     }
00143     else
00144     {
00145       msg.data[4 + i] = 0;
00146     }
00147   }
00148 
00149   // Send download request over CAN bus
00150   m_can_device->Send(msg);
00151 
00152   // Lock the data buffer access
00153   boost::mutex::scoped_lock buffer_guard(m_data_buffer_mutex);
00154 
00155   if (!m_data_update_received)
00156   {
00157     // Wait some time for the resonse...
00158     if (!m_data_buffer_updated_cond.timed_wait(buffer_guard,
00159                                                boost::posix_time::milliseconds(m_response_wait_time_ms)))
00160     {
00161       throw (TimeoutException(index, subindex, "No response to SDO download request received!"));
00162     }
00163   }
00164 
00165   /* New data will be processed next. As we might return from the function at many points,
00166    * we enable data receiving here again, as it will be locked by the mutex
00167    * until the end of this function, anyway.
00168    */
00169   m_data_update_received = false;
00170 
00171 
00172   // sanitizing again
00173   if ( m_data_buffer.size() != 8 )
00174   {
00175     std::stringstream ss;
00176     ss << "Unexpected length " << m_data_buffer.size() << " of SDO response. Expected 8.";
00177     throw (ProtocolException(index, subindex, ss.str()));
00178   }
00179   if ( m_data_buffer[0] == SDO_SEG_ABORT_TRANSFER )
00180   {
00181     uint32_t error_code = m_data_buffer[4] + (m_data_buffer[5]<<8) + (m_data_buffer[6]<<16) + (m_data_buffer[7]<<24);
00182     std::stringstream ss;
00183     ss << "SDO transfer aborted: " << lookupErrorString(error_code);
00184     throw ProtocolException(index, subindex, ss.str());
00185   }
00186   else if ( m_data_buffer[0] != SDO_SEG_RES_INIT_DOWNLOAD )
00187   {
00188     std::stringstream ss;
00189     ss << "Invalid SDO response, got " << hexToString(m_data_buffer[0])
00190         << " expected "  << hexToString(SDO_SEG_RES_INIT_DOWNLOAD);
00191     throw ResponseException(index, subindex, ss.str());
00192   }
00193 
00194   uint32_t rdindex = m_data_buffer[1] + (m_data_buffer[2]<<8);
00195   uint8_t rdsubindex = m_data_buffer[3];
00196   if (rdindex != index || rdsubindex != subindex)
00197   {
00198     std::stringstream ss;
00199     ss << "Invalid index/subindex, expected " << hexToString(index) << "/" << hexToString(subindex)
00200         << ", got " << hexToString(rdindex) << "/" << hexToString(rdsubindex);
00201     throw ResponseException(index, subindex, ss.str());
00202   }
00203 
00204   // If we reached this point, everything is fine :)
00205   return true;
00206 }
00207 
00208 
00209 bool SDO::upload(const bool normal_transfer,
00210                  const uint16_t index,
00211                  const uint8_t subindex,
00212                  std::vector<uint8_t>& uploaded_data)
00213 {
00214   //TODO: support normal transfer as well
00215   if (normal_transfer)
00216   {
00217     LOGGING_ERROR_C(CanOpen, SDO, "So far only expedited transfers with maximum 4 data bytes "
00218                                   << "are supported. "
00219                                   << "However, blocked transfer of was requested. Aborting upload" << endl);
00220     return false;
00221   }
00222 
00223   CanMsg msg;
00224 
00225   msg.id = ds301::ID_RSDO_MIN + m_node_id - 1;
00226   msg.dlc = 8;
00227   msg.rtr = 0;
00228   msg.data[0] = SDO_SEG_REQ_INIT_UPLOAD;
00229   msg.data[1] = index & 0xff;
00230   msg.data[2] = index >> 8;
00231   msg.data[3] = subindex;
00232 
00233   // Send upload request over CAN bus
00234   m_can_device->Send(msg);
00235 
00236   // Lock the data buffer access
00237   boost::mutex::scoped_lock buffer_guard(m_data_buffer_mutex);
00238 
00239   if (!m_data_update_received)
00240   {
00241     // Wait some time for the response...
00242     if (!m_data_buffer_updated_cond.timed_wait(buffer_guard,
00243                                                boost::posix_time::milliseconds(m_response_wait_time_ms)))
00244     {
00245       throw (TimeoutException(index, subindex, "No response to SDO upload request received!"));
00246     }
00247   }
00248 
00249   /* New data will be processed next. As we might return from the function at many points,
00250    * we enable data receiving here again, as it will be locked by the mutex
00251    * until the end of this function, anyway.
00252    */
00253   m_data_update_received = false;
00254 
00255   // sanitizing again
00256   if ( m_data_buffer.size() != 8 )
00257   {
00258     std::stringstream ss;
00259     ss << "Unexpected length " << m_data_buffer.size() << " of SDO response. Expected 8.";
00260     throw ProtocolException (index, subindex, ss.str());
00261   }
00262   if ( m_data_buffer[0] == SDO_SEG_ABORT_TRANSFER )
00263   {
00264     uint32_t error_code = m_data_buffer[4] + (m_data_buffer[5]<<8) + (m_data_buffer[6]<<16) + (m_data_buffer[7]<<24);
00265     std::stringstream ss;
00266     ss << "SDO transfer aborted: " << lookupErrorString(error_code);
00267     throw ProtocolException(index, subindex, ss.str());
00268   }
00269 
00270   uint32_t rdindex = m_data_buffer[1] + (m_data_buffer[2]<<8);
00271   uint8_t rdsubindex = m_data_buffer[3];
00272   if (rdindex != index || rdsubindex != subindex)
00273   {
00274     std::stringstream ss;
00275     ss << "Invalid index/subindex, expected " << hexToString(index) << "/" << hexToString(subindex)
00276         << ", got " << hexToString(rdindex) << "/" << hexToString(rdsubindex);
00277     throw ResponseException (index, subindex, ss.str());
00278   }
00279 
00280   uploaded_data.clear();
00281   uint8_t num_bytes = 0;
00282 
00283   switch (m_data_buffer[0])
00284   {
00285     case SDO_SEG_RES_INIT_UPLOAD_1BYTE:
00286     {
00287       num_bytes = 1;
00288       break;
00289     }
00290     case SDO_SEG_RES_INIT_UPLOAD_2BYTE:
00291     {
00292       num_bytes = 2;
00293       break;
00294     }
00295     case SDO_SEG_RES_INIT_UPLOAD_3BYTE:
00296     {
00297       num_bytes = 3;
00298       break;
00299     }
00300     case SDO_SEG_RES_INIT_UPLOAD_4BYTE:
00301     {
00302       num_bytes = 4;
00303       break;
00304     }
00305     default:
00306     {
00307       std::stringstream ss;
00308       ss << "Illegal SDO upload response received. Please note that so far only expedited "
00309          << " uploads with a data length of up to 4 bytes are supported.\n"
00310          << "Received response was " << hexArrayToString(&m_data_buffer[0], m_data_buffer.size());
00311       throw ResponseException (index, subindex, ss.str());
00312     }
00313   }
00314 
00315   for (size_t i = 0; i < num_bytes; ++i)
00316   {
00317     uploaded_data.push_back(m_data_buffer[4+i]);
00318   }
00319 
00320   return true;
00321 }
00322 
00323 std::string SDO::lookupErrorString(const uint32_t error_code)
00324 {
00325   std::map<uint32_t, std::string>::iterator map_it = m_error_map.find(error_code);
00326   if (map_it != m_error_map.end())
00327   {
00328     return map_it->second;
00329   }
00330   else
00331   {
00332     std::stringstream ss;
00333     ss << "Unknown error code: " << hexToString(error_code);
00334     return ss.str();
00335   }
00336 }
00337 
00338 
00339 void SDO::addErrorMapFromFile(const std::string& filename)
00340 {
00341   std::map<uint32_t, std::string> new_entries = getErrorMapFromConfigFile(filename);
00342   m_error_map.insert(new_entries.begin(), new_entries.end());
00343 
00344   LOGGING_DEBUG(CanOpen, "Added error codes from " << filename << endl);
00345 }
00346 
00347 
00348 }}// end of NS


schunk_canopen_driver
Author(s): Felix Mauch , Georg Heppner
autogenerated on Sun May 22 2016 03:30:56