make_library.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 #####################################################################
00004 # Software License Agreement (BSD License)
00005 #
00006 # Copyright (c) 2011, Willow Garage, Inc.
00007 # All rights reserved.
00008 #
00009 # Redistribution and use in source and binary forms, with or without
00010 # modification, are permitted provided that the following conditions
00011 # are met:
00012 #
00013 #  * Redistributions of source code must retain the above copyright
00014 #    notice, this list of conditions and the following disclaimer.
00015 #  * Redistributions in binary form must reproduce the above
00016 #    copyright notice, this list of conditions and the following
00017 #    disclaimer in the documentation and/or other materials provided
00018 #    with the distribution.
00019 #  * Neither the name of Willow Garage, Inc. nor the names of its
00020 #    contributors may be used to endorse or promote products derived
00021 #    from this software without specific prior written permission.
00022 #
00023 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00024 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00025 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00026 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00027 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00028 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00029 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00030 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00031 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00032 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00033 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00034 # POSSIBILITY OF SUCH DAMAGE.
00035 
00036 from __future__ import print_function
00037 
00038 __author__ = "mferguson@willowgarage.com (Michael Ferguson)"
00039 
00040 import roslib
00041 import roslib.srvs
00042 import roslib.message
00043 import rospkg
00044 import rospy
00045 
00046 import os, sys, subprocess, re
00047 
00048 # for copying files
00049 import shutil
00050 
00051 def type_to_var(ty):
00052     lookup = {
00053         1 : 'uint8_t',
00054         2 : 'uint16_t',
00055         4 : 'uint32_t',
00056         8 : 'uint64_t',
00057     }
00058     return lookup[ty]
00059 
00060 #####################################################################
00061 # Data Types
00062 
00063 class EnumerationType:
00064     """ For data values. """
00065 
00066     def __init__(self, name, ty, value):
00067         self.name = name
00068         self.type = ty
00069         self.value = value
00070 
00071     def make_declaration(self, f):
00072         f.write('      enum { %s = %s };\n' % (self.name, self.value))
00073 
00074 class PrimitiveDataType:
00075     """ Our datatype is a C/C++ primitive. """
00076 
00077     def __init__(self, name, ty, bytes):
00078         self.name = name
00079         self.type = ty
00080         self.bytes = bytes
00081 
00082     def make_initializer(self, f, trailer):
00083         f.write('      %s(0)%s\n' % (self.name, trailer))
00084 
00085     def make_declaration(self, f):
00086         f.write('      %s %s;\n' % (self.type, self.name) )
00087 
00088     def serialize(self, f):
00089         cn = self.name.replace("[","").replace("]","").split(".")[-1]
00090         if self.type != type_to_var(self.bytes):
00091             f.write('      union {\n')
00092             f.write('        %s real;\n' % self.type)
00093             f.write('        %s base;\n' % type_to_var(self.bytes))
00094             f.write('      } u_%s;\n' % cn)
00095             f.write('      u_%s.real = this->%s;\n' % (cn,self.name))
00096             for i in range(self.bytes):
00097                 f.write('      *(outbuffer + offset + %d) = (u_%s.base >> (8 * %d)) & 0xFF;\n' % (i, cn, i) )
00098         else:
00099             for i in range(self.bytes):
00100                 f.write('      *(outbuffer + offset + %d) = (this->%s >> (8 * %d)) & 0xFF;\n' % (i, self.name, i) )
00101         f.write('      offset += sizeof(this->%s);\n' % self.name)
00102 
00103     def deserialize(self, f):
00104         cn = self.name.replace("[","").replace("]","").split(".")[-1]
00105         if self.type != type_to_var(self.bytes):
00106             f.write('      union {\n')
00107             f.write('        %s real;\n' % self.type)
00108             f.write('        %s base;\n' % type_to_var(self.bytes))
00109             f.write('      } u_%s;\n' % cn)
00110             f.write('      u_%s.base = 0;\n' % cn)
00111             for i in range(self.bytes):
00112                 f.write('      u_%s.base |= ((%s) (*(inbuffer + offset + %d))) << (8 * %d);\n' % (cn,type_to_var(self.bytes),i,i) )
00113             f.write('      this->%s = u_%s.real;\n' % (self.name, cn) )
00114         else:
00115             f.write('      this->%s =  ((%s) (*(inbuffer + offset)));\n' % (self.name,self.type) )
00116             for i in range(self.bytes-1):
00117                 f.write('      this->%s |= ((%s) (*(inbuffer + offset + %d))) << (8 * %d);\n' % (self.name,self.type,i+1,i+1) )
00118         f.write('      offset += sizeof(this->%s);\n' % self.name)
00119 
00120 
00121 class MessageDataType(PrimitiveDataType):
00122     """ For when our data type is another message. """
00123 
00124     def make_initializer(self, f, trailer):
00125         f.write('      %s()%s\n' % (self.name, trailer))
00126 
00127     def serialize(self, f):
00128         f.write('      offset += this->%s.serialize(outbuffer + offset);\n' % self.name)
00129 
00130     def deserialize(self, f):
00131         f.write('      offset += this->%s.deserialize(inbuffer + offset);\n' % self.name)
00132 
00133 
00134 class AVR_Float64DataType(PrimitiveDataType):
00135     """ AVR C/C++ has no native 64-bit support, we automatically convert to 32-bit float. """
00136 
00137     def make_initializer(self, f, trailer):
00138         f.write('      %s(0)%s\n' % (self.name, trailer))
00139 
00140     def make_declaration(self, f):
00141         f.write('      float %s;\n' % self.name )
00142 
00143     def serialize(self, f):
00144         f.write('      offset += serializeAvrFloat64(outbuffer + offset, this->%s);\n' % self.name)
00145 
00146     def deserialize(self, f):
00147         f.write('      offset += deserializeAvrFloat64(inbuffer + offset, &(this->%s));\n' % self.name)
00148 
00149 
00150 class StringDataType(PrimitiveDataType):
00151     """ Need to convert to signed char *. """
00152 
00153     def make_initializer(self, f, trailer):
00154         f.write('      %s("")%s\n' % (self.name, trailer))
00155 
00156     def make_declaration(self, f):
00157         f.write('      const char* %s;\n' % self.name)
00158 
00159     def serialize(self, f):
00160         cn = self.name.replace("[","").replace("]","")
00161         f.write('      uint32_t length_%s = strlen(this->%s);\n' % (cn,self.name))
00162         f.write('      memcpy(outbuffer + offset, &length_%s, sizeof(uint32_t));\n' % cn)
00163         f.write('      offset += 4;\n')
00164         f.write('      memcpy(outbuffer + offset, this->%s, length_%s);\n' % (self.name,cn))
00165         f.write('      offset += length_%s;\n' % cn)
00166 
00167     def deserialize(self, f):
00168         cn = self.name.replace("[","").replace("]","")
00169         f.write('      uint32_t length_%s;\n' % cn)
00170         f.write('      memcpy(&length_%s, (inbuffer + offset), sizeof(uint32_t));\n' % cn)
00171         f.write('      offset += 4;\n')
00172         f.write('      for(unsigned int k= offset; k< offset+length_%s; ++k){\n'%cn) #shift for null character
00173         f.write('          inbuffer[k-1]=inbuffer[k];\n')
00174         f.write('      }\n')
00175         f.write('      inbuffer[offset+length_%s-1]=0;\n'%cn)
00176         f.write('      this->%s = (char *)(inbuffer + offset-1);\n' % self.name)
00177         f.write('      offset += length_%s;\n' % cn)
00178 
00179 
00180 class TimeDataType(PrimitiveDataType):
00181 
00182     def __init__(self, name, ty, bytes):
00183         self.name = name
00184         self.type = ty
00185         self.sec = PrimitiveDataType(name+'.sec','uint32_t',4)
00186         self.nsec = PrimitiveDataType(name+'.nsec','uint32_t',4)
00187 
00188     def make_initializer(self, f, trailer):
00189         f.write('      %s()%s\n' % (self.name, trailer))
00190 
00191     def make_declaration(self, f):
00192         f.write('      %s %s;\n' % (self.type, self.name))
00193 
00194     def serialize(self, f):
00195         self.sec.serialize(f)
00196         self.nsec.serialize(f)
00197 
00198     def deserialize(self, f):
00199         self.sec.deserialize(f)
00200         self.nsec.deserialize(f)
00201 
00202 
00203 class ArrayDataType(PrimitiveDataType):
00204 
00205     def __init__(self, name, ty, bytes, cls, array_size=None):
00206         self.name = name
00207         self.type = ty
00208         self.bytes = bytes
00209         self.size = array_size
00210         self.cls = cls
00211 
00212     def make_initializer(self, f, trailer):
00213         if self.size == None:
00214             f.write('      %s_length(0), %s(NULL)%s\n' % (self.name, self.name, trailer))
00215         else:
00216             f.write('      %s()%s\n' % (self.name, trailer))
00217 
00218     def make_declaration(self, f):
00219         c = self.cls("*"+self.name, self.type, self.bytes)
00220         if self.size == None:
00221             f.write('      uint8_t %s_length;\n' % self.name)
00222             f.write('      %s st_%s;\n' % (self.type, self.name)) # static instance for copy
00223             f.write('      %s * %s;\n' % (self.type, self.name))
00224         else:
00225             f.write('      %s %s[%d];\n' % (self.type, self.name, self.size))
00226 
00227     def serialize(self, f):
00228         c = self.cls(self.name+"[i]", self.type, self.bytes)
00229         if self.size == None:
00230             # serialize length
00231             f.write('      *(outbuffer + offset++) = %s_length;\n' % self.name)
00232             f.write('      *(outbuffer + offset++) = 0;\n')
00233             f.write('      *(outbuffer + offset++) = 0;\n')
00234             f.write('      *(outbuffer + offset++) = 0;\n')
00235             f.write('      for( uint8_t i = 0; i < %s_length; i++){\n' % self.name)
00236             c.serialize(f)
00237             f.write('      }\n')
00238         else:
00239             f.write('      for( uint8_t i = 0; i < %d; i++){\n' % (self.size) )
00240             c.serialize(f)
00241             f.write('      }\n')
00242 
00243     def deserialize(self, f):
00244         if self.size == None:
00245             c = self.cls("st_"+self.name, self.type, self.bytes)
00246             # deserialize length
00247             f.write('      uint8_t %s_lengthT = *(inbuffer + offset++);\n' % self.name)
00248             f.write('      if(%s_lengthT > %s_length)\n' % (self.name, self.name))
00249             f.write('        this->%s = (%s*)realloc(this->%s, %s_lengthT * sizeof(%s));\n' % (self.name, self.type, self.name, self.name, self.type))
00250             f.write('      offset += 3;\n')
00251             f.write('      %s_length = %s_lengthT;\n' % (self.name, self.name))
00252             # copy to array
00253             f.write('      for( uint8_t i = 0; i < %s_length; i++){\n' % (self.name) )
00254             c.deserialize(f)
00255             f.write('        memcpy( &(this->%s[i]), &(this->st_%s), sizeof(%s));\n' % (self.name, self.name, self.type))
00256             f.write('      }\n')
00257         else:
00258             c = self.cls(self.name+"[i]", self.type, self.bytes)
00259             f.write('      for( uint8_t i = 0; i < %d; i++){\n' % (self.size) )
00260             c.deserialize(f)
00261             f.write('      }\n')
00262 
00263 #####################################################################
00264 # Messages
00265 
00266 class Message:
00267     """ Parses message definitions into something we can export. """
00268     global ROS_TO_EMBEDDED_TYPES
00269 
00270     def __init__(self, name, package, definition, md5):
00271 
00272         self.name = name            # name of message/class
00273         self.package = package      # package we reside in
00274         self.md5 = md5              # checksum
00275         self.includes = list()      # other files we must include
00276 
00277         self.data = list()          # data types for code generation
00278         self.enums = list()
00279 
00280         # parse definition
00281         for line in definition:
00282             # prep work
00283             line = line.strip().rstrip()
00284             value = None
00285             if line.find("#") > -1:
00286                 line = line[0:line.find("#")]
00287             if line.find("=") > -1:
00288                 try:
00289                     value = line[line.find("=")+1:]
00290                 except:
00291                     value = '"' + line[line.find("=")+1:] + '"';
00292                 line = line[0:line.find("=")]
00293 
00294             # find package/class name
00295             line = line.replace("\t", " ")
00296             l = line.split(" ")
00297             while "" in l:
00298                 l.remove("")
00299             if len(l) < 2:
00300                 continue
00301             ty, name = l[0:2]
00302             if value != None:
00303                 self.enums.append( EnumerationType(name, ty, value))
00304                 continue
00305 
00306             try:
00307                 type_package, type_name = ty.split("/")
00308             except:
00309                 type_package = None
00310                 type_name = ty
00311             type_array = False
00312             if type_name.find('[') > 0:
00313                 type_array = True
00314                 try:
00315                     type_array_size = int(type_name[type_name.find('[')+1:type_name.find(']')])
00316                 except:
00317                     type_array_size = None
00318                 type_name = type_name[0:type_name.find('[')]
00319 
00320             # convert to C type if primitive, expand name otherwise
00321             try:
00322                 code_type = ROS_TO_EMBEDDED_TYPES[type_name][0]
00323                 size = ROS_TO_EMBEDDED_TYPES[type_name][1]
00324                 cls = ROS_TO_EMBEDDED_TYPES[type_name][2]
00325                 for include in ROS_TO_EMBEDDED_TYPES[type_name][3]:
00326                     if include not in self.includes:
00327                         self.includes.append(include)
00328             except:
00329                 if type_package == None:
00330                     type_package = self.package
00331                 if type_package+"/"+type_name not in self.includes:
00332                     self.includes.append(type_package+"/"+type_name)
00333                 cls = MessageDataType
00334                 code_type = type_package + "::" + type_name
00335                 size = 0
00336             if type_array:
00337                 self.data.append( ArrayDataType(name, code_type, size, cls, type_array_size ) )
00338             else:
00339                 self.data.append( cls(name, code_type, size) )
00340 
00341     def _write_serializer(self, f):
00342                 # serializer
00343         f.write('    virtual int serialize(unsigned char *outbuffer) const\n')
00344         f.write('    {\n')
00345         f.write('      int offset = 0;\n')
00346         for d in self.data:
00347             d.serialize(f)
00348         f.write('      return offset;\n');
00349         f.write('    }\n')
00350         f.write('\n')
00351 
00352     def _write_deserializer(self, f):
00353         # deserializer
00354         f.write('    virtual int deserialize(unsigned char *inbuffer)\n')
00355         f.write('    {\n')
00356         f.write('      int offset = 0;\n')
00357         for d in self.data:
00358             d.deserialize(f)
00359         f.write('     return offset;\n');
00360         f.write('    }\n')
00361         f.write('\n')
00362 
00363     def _write_std_includes(self, f):
00364         f.write('#include <stdint.h>\n')
00365         f.write('#include <string.h>\n')
00366         f.write('#include <stdlib.h>\n')
00367         f.write('#include "ros/msg.h"\n')
00368 
00369     def _write_msg_includes(self,f):
00370         for i in self.includes:
00371             f.write('#include "%s.h"\n' % i)
00372 
00373     def _write_constructor(self, f):
00374         f.write('    %s()%s\n' % (self.name, ':' if self.data else ''))
00375         if self.data:
00376             for d in self.data[:-1]:
00377                 d.make_initializer(f, ',')
00378             self.data[-1].make_initializer(f, '')
00379         f.write('    {\n    }\n\n')
00380 
00381     def _write_data(self, f):
00382         for d in self.data:
00383             d.make_declaration(f)
00384         for e in self.enums:
00385             e.make_declaration(f)
00386         f.write('\n')
00387 
00388     def _write_getType(self, f):
00389         f.write('    const char * getType(){ return "%s/%s"; };\n'%(self.package, self.name))
00390 
00391     def _write_getMD5(self, f):
00392         f.write('    const char * getMD5(){ return "%s"; };\n'%self.md5)
00393 
00394     def _write_impl(self, f):
00395         f.write('  class %s : public ros::Msg\n' % self.name)
00396         f.write('  {\n')
00397         f.write('    public:\n')
00398         self._write_data(f)
00399         self._write_constructor(f)
00400         self._write_serializer(f)
00401         self._write_deserializer(f)
00402         self._write_getType(f)
00403         self._write_getMD5(f)
00404         f.write('\n')
00405         f.write('  };\n')
00406 
00407     def make_header(self, f):
00408         f.write('#ifndef _ROS_%s_%s_h\n'%(self.package, self.name))
00409         f.write('#define _ROS_%s_%s_h\n'%(self.package, self.name))
00410         f.write('\n')
00411         self._write_std_includes(f)
00412         self._write_msg_includes(f)
00413 
00414         f.write('\n')
00415         f.write('namespace %s\n' % self.package)
00416         f.write('{\n')
00417         f.write('\n')
00418         self._write_impl(f)
00419         f.write('\n')
00420         f.write('}\n')
00421 
00422         f.write('#endif')
00423 
00424 class Service:
00425     def __init__(self, name, package, definition, md5req, md5res):
00426         """
00427         @param name -  name of service
00428         @param package - name of service package
00429         @param definition - list of lines of  definition
00430         """
00431 
00432         self.name = name
00433         self.package = package
00434 
00435         sep_line = len(definition)
00436         sep = re.compile('---*')
00437         for i in range(0, len(definition)):
00438             if (None!= re.match(sep, definition[i]) ):
00439                 sep_line = i
00440                 break
00441         self.req_def = definition[0:sep_line]
00442         self.resp_def = definition[sep_line+1:]
00443 
00444         self.req = Message(name+"Request", package, self.req_def, md5req)
00445         self.resp = Message(name+"Response", package, self.resp_def, md5res)
00446 
00447     def make_header(self, f):
00448         f.write('#ifndef _ROS_SERVICE_%s_h\n' % self.name)
00449         f.write('#define _ROS_SERVICE_%s_h\n' % self.name)
00450 
00451         self.req._write_std_includes(f)
00452         includes = self.req.includes
00453         includes.extend(self.resp.includes)
00454         includes = list(set(includes))
00455         for inc in includes:
00456             f.write('#include "%s.h"\n' % inc)
00457 
00458         f.write('\n')
00459         f.write('namespace %s\n' % self.package)
00460         f.write('{\n')
00461         f.write('\n')
00462         f.write('static const char %s[] = "%s/%s";\n'%(self.name.upper(), self.package, self.name))
00463 
00464         def write_type(out, name):
00465             out.write('    const char * getType(){ return %s; };\n'%(name))
00466         _write_getType = lambda out: write_type(out, self.name.upper())
00467         self.req._write_getType = _write_getType
00468         self.resp._write_getType = _write_getType
00469 
00470         f.write('\n')
00471         self.req._write_impl(f)
00472         f.write('\n')
00473         self.resp._write_impl(f)
00474         f.write('\n')
00475         f.write('  class %s {\n' % self.name )
00476         f.write('    public:\n')
00477         f.write('    typedef %s Request;\n' % self.req.name )
00478         f.write('    typedef %s Response;\n' % self.resp.name )
00479         f.write('  };\n')
00480         f.write('\n')
00481 
00482         f.write('}\n')
00483 
00484         f.write('#endif\n')
00485 
00486 
00487 #####################################################################
00488 # Make a Library
00489 
00490 def MakeLibrary(package, output_path, rospack):
00491     pkg_dir = rospack.get_path(package)
00492 
00493     # find the messages in this package
00494     messages = list()
00495     if os.path.exists(pkg_dir+"/msg"):
00496         print('Exporting %s\n'%package)
00497         sys.stdout.write('  Messages:')
00498         sys.stdout.write('\n    ')
00499         for f in os.listdir(pkg_dir+"/msg"):
00500             if f.endswith(".msg"):
00501                 file = pkg_dir + "/msg/" + f
00502                 # add to list of messages
00503                 print('%s,'%f[0:-4], end='')
00504                 definition = open(file).readlines()
00505                 md5sum = roslib.message.get_message_class(package+'/'+f[0:-4])._md5sum
00506                 messages.append( Message(f[0:-4], package, definition, md5sum) )
00507 
00508     # find the services in this package
00509     services = list()
00510     if (os.path.exists(pkg_dir+"/srv/")):
00511         if messages == list():
00512             print('Exporting %s\n'%package)
00513         else:
00514             print('\n')
00515         sys.stdout.write('  Services:')
00516         sys.stdout.write('\n    ')
00517         for f in os.listdir(pkg_dir+"/srv"):
00518             if f.endswith(".srv"):
00519                 file = pkg_dir + "/srv/" + f
00520                 # add to list of messages
00521                 print('%s,'%f[0:-4], end='')
00522                 definition, service = roslib.srvs.load_from_file(file)
00523                 definition = open(file).readlines()
00524                 md5req = roslib.message.get_service_class(package+'/'+f[0:-4])._request_class._md5sum
00525                 md5res = roslib.message.get_service_class(package+'/'+f[0:-4])._response_class._md5sum
00526                 messages.append( Service(f[0:-4], package, definition, md5req, md5res ) )
00527         print('\n')
00528     elif messages != list():
00529         print('\n')
00530 
00531     # generate for each message
00532     output_path = output_path + "/" + package
00533     for msg in messages:
00534         if not os.path.exists(output_path):
00535             os.makedirs(output_path)
00536         header = open(output_path + "/" + msg.name + ".h", "w")
00537         msg.make_header(header)
00538         header.close()
00539 
00540 def get_dependency_sorted_package_list(rospack):
00541     ''' Returns a list of package names, sorted by dependencies. '''
00542     pkgs = rospack.list()
00543     dependency_list = list()
00544     failed = list()
00545     for p in pkgs:
00546         try:
00547             depends = rospack.get_depends(p)
00548             dependent = False
00549             for i in range(len(dependency_list)):
00550                 if dependency_list[i] in depends:
00551                     dependency_list.insert(i, p)
00552                     dependent = True
00553                     break
00554             if not dependent:
00555                 dependency_list.append(p)
00556         except rospkg.common.ResourceNotFound as e:
00557             failed.append(p + " (missing dependency)")
00558             print('[%s]: Unable to find dependency: %s. Messages cannot be built.\n'% (p, str(e)))
00559     dependency_list.reverse()
00560     return [dependency_list, failed]
00561 
00562 def rosserial_generate(rospack, path, mapping):
00563     # horrible hack -- make this die
00564     global ROS_TO_EMBEDDED_TYPES
00565     ROS_TO_EMBEDDED_TYPES = mapping
00566 
00567     # find and sort all packages
00568     pkgs, failed = get_dependency_sorted_package_list(rospack)
00569 
00570     # gimme messages
00571     for p in pkgs:
00572         try:
00573             MakeLibrary(p, path, rospack)
00574         except Exception as e:
00575             failed.append(p)
00576             print('[%s]: Unable to build messages: %s\n' % (p, str(e)))
00577     print('\n')
00578     if len(failed) > 0:
00579         print('*** Warning, failed to generate libraries for the following packages: ***')
00580         for f in failed:
00581             print('    %s'%f)
00582     print('\n')
00583 
00584 def rosserial_client_copy_files(rospack, path):
00585     os.makedirs(path+"/ros")
00586     os.makedirs(path+"/tf")
00587     files = ['duration.cpp',
00588              'time.cpp',
00589              'ros/duration.h',
00590              'ros/msg.h',
00591              'ros/node_handle.h',
00592              'ros/publisher.h',
00593              'ros/service_client.h',
00594              'ros/service_server.h',
00595              'ros/subscriber.h',
00596              'ros/time.h',
00597              'tf/tf.h',
00598              'tf/transform_broadcaster.h']
00599     mydir = rospack.get_path("rosserial_client")
00600     for f in files:
00601         shutil.copy(mydir+"/src/ros_lib/"+f, path+f)
00602 


rosserial_client
Author(s): Michael Ferguson, Adam Stambler
autogenerated on Thu Jun 6 2019 19:56:25