Module rosmsg
[frames] | no frames]

Source Code for Module rosmsg

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2009, Willow Garage, Inc. 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  # 
 10  #  * Redistributions of source code must retain the above copyright 
 11  #    notice, this list of conditions and the following disclaimer. 
 12  #  * Redistributions in binary form must reproduce the above 
 13  #    copyright notice, this list of conditions and the following 
 14  #    disclaimer in the documentation and/or other materials provided 
 15  #    with the distribution. 
 16  #  * Neither the name of Willow Garage, Inc. nor the names of its 
 17  #    contributors may be used to endorse or promote products derived 
 18  #    from this software without specific prior written permission. 
 19  # 
 20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 31  # POSSIBILITY OF SUCH DAMAGE. 
 32   
 33  """ 
 34  Implements rosmsg/rossrv command-line tools. 
 35   
 36  The code API of the rosmsg module is unstable. Much of the 
 37  functionality of rosmsg/rossrv is implemented using the roslib.msgs 
 38  and roslib.srvs libraries and can be found there instead. 
 39  """ 
 40   
 41  import os 
 42  import platform 
 43  import sys 
 44  import socket 
 45  import threading 
 46  import time 
 47  import subprocess 
 48   
 49  import roslib 
 50  import roslib.genpy 
 51  import roslib.gentools 
 52  import roslib.message 
 53  import roslib.msgs 
 54  import roslib.names 
 55  import roslib.packages 
 56  import roslib.srvs 
 57  import rosbag 
 58   
 59  from optparse import OptionParser 
60 61 -class ROSMsgException(Exception): pass
62 63 import warnings
64 -def deprecated(func):
65 """This is a decorator which can be used to mark functions 66 as deprecated. It will result in a warning being emmitted 67 when the function is used.""" 68 def newFunc(*args, **kwargs): 69 warnings.warn("Call to deprecated function %s." % func.__name__, 70 category=DeprecationWarning, stacklevel=2) 71 return func(*args, **kwargs)
72 newFunc.__name__ = func.__name__ 73 newFunc.__doc__ = func.__doc__ 74 newFunc.__dict__.update(func.__dict__) 75 return newFunc 76
77 -def succeed(args):
78 code, msg, val = args 79 if code != 1: 80 raise ROSMsgException("remote call failed: %s"%msg) 81 return val
82
83 -def make_find_command(path):
84 if os.uname()[0] in ['Darwin', 'FreeBSD']: 85 return ["find", "-E", path] 86 else: 87 return ["find", path, "-regextype", "posix-egrep"]
88
89 90 -def rosmsg_users_package_search(mode, type_, package):
91 result = set() #using a set for deduplication 92 mode_str = "srv" 93 if mode == roslib.msgs.EXT: 94 mode_str = "msg" 95 96 msg_pkg, msg_name = type_.split('/') 97 98 # Get the full path to the using package 99 p = roslib.packages.get_pkg_dir(package) 100 101 102 # Find the msg/srv files 103 # Leave the heavy lifting to find and grep. 104 if mode == roslib.msgs.EXT: 105 command = [] 106 if msg_pkg == package: 107 command = make_find_command(p) 108 command += ["-regex", ".*\.(msg|srv)", "!", "-regex", ".*build.*", "!", "-regex", ".*\.svn.*", "!", "-regex", ".*~", "-exec", "grep", "-lE", "(" + type_ + "|" + msg_name + ")", "{}", ";"] 109 else: 110 command = make_find_command(p) 111 command += ["-regex", ".*\.(msg|srv)", "!", "-regex", ".*build.*", "!", "-regex", ".*\.svn.*", "!", "-regex", ".*~", "-exec", "grep", "-lE", type_, "{}", ";"] 112 113 114 #print ' '.join(command) 115 msgfiles = subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0].strip().split() 116 #print cppfiles 117 # Dump out what we found 118 for f in msgfiles: 119 if len(f.strip()) != 0: 120 result.add( f ) 121 #print f[len(p)-len(d):] 122 123 # Find the C/C++ files in this package that #include the msg/srv 124 # header. Leave the heavy lifting to find and grep. 125 command = make_find_command(p) 126 command += ["-regex", ".*\.(cpp|h|hh|cc|hpp|c)", "!", "-regex", ".*build.*", "!", "-regex", ".*svn.*", "!", "-regex", ".*~", "-exec", "grep", "-lE", " *#include *(\"|<)" + type_ + ".h", "{}", ";"] 127 #print ' '.join(command) 128 cppfiles = subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0].strip().split() 129 #print cppfiles 130 # Dump out what we found 131 for f in cppfiles: 132 if len(f.strip()) != 0: 133 result.add( f ) 134 #print f[len(p)-len(d):] 135 136 #print type_list[0], " and ", type_list[1] 137 # Capture all of the form import package_name.msg.message_name 138 command = make_find_command(p) 139 # The following regex doesn't work on OS X, and also doesn't appear to 140 # do anything. 141 #command += ["-regex", ".*(\.py|)"] 142 command += ["!", "-regex", ".*build.*", "!", "-regex", ".*\.svn.*", "-exec", "grep", "-IlE", "import " +msg_pkg+"\."+mode_str+"\."+ msg_name, "{}", ";"] 143 #print ' '.join(command) 144 pyfiles = subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0].strip().split('\n') 145 for f in pyfiles: 146 if len(f.strip()) != 0: 147 result.add(f) 148 149 # Capture all of the form "from package_name.msg import *|message_name with message name used 150 command = make_find_command(p) 151 # The following regex doesn't work on OS X, and also doesn't appear to 152 # do anything. 153 #command += ["-regex", ".*(\.py|)"] 154 command += ["!", "-regex", ".*build.*", "!", "-regex", ".*\.svn.*", "!", "-regex", ".*~", "-exec", "grep", "-IlE", " *from.*" + msg_pkg + "\."+mode_str+".*import (\*|" + msg_name +")", "{}",";"] 155 #print ' '.join(command) 156 pyfiles = subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0].strip().split('\n') 157 158 159 for f in pyfiles: 160 if len(f.strip()) != 0: 161 # Make sure that this message is used for the above test could return without using the message if importing * 162 #print "looking for", msg, "in", f 163 command = ["grep", "-l", msg_name, f] 164 #print ' '.join(command) 165 present = subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0].strip().split('\n') 166 if len(present[0]) > 0: 167 #print present, len(present[0]) 168 result.add(f) 169 170 return list(result) #return as list
171
172 173 -def rosmsg_users(mode, type_):
174 msg_pkg, msg_name = roslib.names.package_resource_name(type_) 175 # Find the direct users of the package; they're the only ones who 176 # should be able to use this message 177 if not msg_pkg: 178 print >> sys.stderr, "Please specify the package and type for [%s]"%type_ 179 sys.exit(os.EX_USAGE) 180 181 print 'Files using %s:' % (type_) 182 183 deps = subprocess.Popen(["rospack", "depends-on1", msg_pkg], stdout=subprocess.PIPE).communicate()[0].strip().split() 184 # Check the messages own package too 185 deps.append(msg_pkg) 186 187 correct_dependencies = [] 188 for d in deps: 189 files = rosmsg_users_package_search(mode, type_, d) 190 correct_dependencies.extend(files) 191 192 print "Usages directly depended upon:" + type_ 193 for f in correct_dependencies: 194 print f 195 196 deps = subprocess.Popen(["rospack", "depends-on", msg_pkg], stdout=subprocess.PIPE).communicate()[0].strip().split() 197 incorrect_dependencies = [] 198 for d in deps: 199 files = rosmsg_users_package_search(mode, type_, d) 200 incorrect_dependencies.extend(files) 201 print "Usages indirectly depended upon:" 202 for f in incorrect_dependencies: 203 if f not in correct_dependencies: 204 print f
205
206 -def get_srv_text(type_, raw=False):
207 """ 208 Get .srv file for type_ as text 209 @param type_: service type 210 @type type_: str 211 @param raw: if True, include comments and whitespace (default False) 212 @type raw: bool 213 @return: text of .srv file 214 @rtype: str 215 @raise ROSMsgException: if type_ is unknown 216 """ 217 package, base_type = roslib.names.package_resource_name(type_) 218 roslib.msgs.load_package_dependencies(package, load_recursive=True) 219 roslib.msgs.load_package(package) 220 f = roslib.srvs.srv_file(package, base_type) 221 if not os.path.isfile(f): 222 raise ROSMsgException("Unknown srv type: %s"%type_) 223 name, spec = roslib.srvs.load_from_file(f, package) 224 if raw: 225 return spec.text 226 else: 227 return str(spec.request)+'---\n'+str(spec.response)
228
229 -def get_msg_text(type_, raw=False, full_text=None):
230 """ 231 Get .msg file for type_ as text 232 @param type_: message type 233 @type type_: str 234 @param raw: if True, include comments and whitespace (default False) 235 @type raw: bool 236 @param full_text: if not None, contains full text of message definition 237 @type full_text: str 238 @return: text of .msg file 239 @rtype: str 240 @raise ROSMsgException: if type_ is unknown 241 """ 242 package, base_type = roslib.names.package_resource_name(type_) 243 244 if not full_text: 245 roslib.msgs.load_package_dependencies(package, load_recursive=True) 246 roslib.msgs.load_package(package) 247 try: 248 spec = roslib.msgs.get_registered(type_) 249 except KeyError: 250 raise ROSMsgException("Unknown msg type: %s"%type_) 251 252 if raw: 253 text = spec.text 254 else: 255 text = str(spec) 256 else: 257 splits = full_text.split('\n'+'='*80+'\n') 258 core_msg = splits[0] 259 deps_msgs = splits[1:] 260 261 specs = { type_: roslib.msgs.load_from_string(core_msg, package) } 262 for dep_msg in deps_msgs: 263 dep_type, dep_spec = roslib.genpy._generate_dynamic_specs(specs, dep_msg) 264 specs[dep_type] = dep_spec 265 266 for t, spec in specs.iteritems(): 267 roslib.msgs.register(t, spec) 268 spec = specs[type_] 269 if raw: 270 text = spec.text 271 else: 272 text = str(spec) 273 274 return text
275
276 -def rosmsg_debug(mode, type_, raw=False):
277 """ 278 Prints contents of msg/srv file 279 @param mode: roslib.srvs.EXT or roslib.msgs.EXT 280 @type mode: str 281 """ 282 if mode == roslib.srvs.EXT: 283 print get_srv_text(type_, raw=raw) 284 elif mode == roslib.msgs.EXT: 285 print get_msg_text(type_, raw=raw) 286 else: 287 raise ROSMsgException("invalid mode: %s"%mode)
288
289 -def list_srvs(package):
290 """ 291 List srvs contained in package 292 @param package: package name 293 @type package: str 294 @return: list of srvs in package 295 @rtype: [str] 296 """ 297 return list_types(package, mode=roslib.srvs.EXT)
298
299 -def list_msgs(package):
300 """ 301 List msgs contained in package 302 @param package: package name 303 @type package: str 304 @return: list of msgs in package 305 @rtype: [str] 306 """ 307 return list_types(package)
308
309 # DEPRECATED 310 @deprecated 311 -def rosmsg_list_package(mode, package):
312 return list_types(package, mode=mode)
313
314 -def list_types(package, mode=roslib.msgs.EXT):
315 """ 316 Lists msg/srvs contained in package 317 @param package: package name 318 @type package: str 319 @param mode: roslib.srvs.EXT or roslib.msgs.EXT. Defaults to msgs. 320 @type mode: str 321 @return: list of msgs/srv in package 322 @rtype: [str] 323 """ 324 if mode == roslib.msgs.EXT: 325 return [roslib.names.resource_name(package, t) for t in roslib.msgs.list_msg_types(package, False)] 326 elif mode == roslib.srvs.EXT: 327 return [roslib.names.resource_name(package, t) for t in roslib.srvs.list_srv_types(package, False)] 328 else: 329 raise ValueError('mode')
330
331 -def iterate_packages(mode):
332 """ 333 Iterator for packages that contain messages/services 334 @param mode: roslib.msgs.EXT or roslib.srvs.EXT 335 @type mode: str 336 """ 337 if mode == roslib.msgs.EXT: 338 subdir = roslib.packages.MSG_DIR 339 elif mode == roslib.srvs.EXT: 340 subdir = roslib.packages.SRV_DIR 341 else: 342 raise ValueError('mode') 343 344 pkgs = roslib.packages.list_pkgs() 345 for p in pkgs: 346 dir = roslib.packages.get_pkg_subdir(p, subdir, False) 347 if dir and os.path.isdir(dir): 348 yield p
349
350 @deprecated 351 -def rosmsg_list_packages(mode):
352 """ 353 Use list_packages 354 """ 355 return list_packages(mode=mode)
356
357 -def list_packages(mode=roslib.msgs.EXT):
358 """ 359 List all packages that contain messages/services. This is a convenience 360 function of iterate_packages 361 @param mode: roslib.msgs.EXT or roslib.srvs.EXT. Defaults to msgs 362 @type mode: str 363 @return: list of packages that contain messages/services (depending on mode) 364 @rtype: [str] 365 """ 366 return [p for p in iterate_packages(mode)]
367
368 ## iterator for all packages that contain a message matching base_type 369 ## @param base_type str: message base type to match, e.g. 'String' would match std_msgs/String 370 -def rosmsg_search(mode, base_type):
371 if mode == roslib.msgs.EXT: 372 res_file = roslib.msgs.msg_file 373 else: 374 res_file = roslib.srvs.srv_file 375 for p in iterate_packages(mode): 376 if os.path.isfile(res_file(p, base_type)): 377 yield roslib.names.resource_name(p, base_type)
378
379 -def _stdin_arg(parser, full):
380 options, args = parser.parse_args(sys.argv[2:]) 381 # read in args from stdin pipe if not present 382 if not args: 383 arg = None 384 while not arg: 385 arg = sys.stdin.readline().strip() 386 return options, arg 387 else: 388 if len(args) > 1: 389 parser.error("you may only specify one %s"%full) 390 return options, args[0]
391
392 -def rosmsg_cmd_show(mode, full):
393 cmd = "ros%s"%(mode[1:]) 394 parser = OptionParser(usage="usage: %s show [options] <%s>"%(cmd, full)) 395 parser.add_option("-r", "--raw", 396 dest="raw", default=False,action="store_true", 397 help="show raw message text, including comments") 398 parser.add_option("-b", "--bag", 399 dest="bag", default=None, 400 help="show message from .bag file", metavar="BAGFILE") 401 options, arg = _stdin_arg(parser, full) 402 if arg.endswith(mode): 403 arg = arg[:-(len(mode))] 404 405 # try to catch the user specifying code-style types and error 406 if '::' in arg: 407 parser.error(cmd+" does not understand C++-style namespaces (i.e. '::').\nPlease refer to msg/srv types as 'package_name/Type'.") 408 elif '.' in arg: 409 parser.error("invalid message type '%s'.\nPlease refer to msg/srv types as 'package_name/Type'." % arg) 410 if options.bag: 411 bag_file = options.bag 412 if not os.path.exists(bag_file): 413 raise ROSMsgException("ERROR: bag file [%s] does not exist"%bag_file) 414 for topic, msg, t in rosbag.Bag(bag_file).read_messages(raw=True): 415 datatype, _, _, _, pytype = msg 416 if datatype == arg: 417 print get_msg_text(datatype, options.raw, pytype._full_text) 418 break 419 else: 420 if '/' in arg: #package specified 421 rosmsg_debug(mode, arg, options.raw) 422 else: 423 for found in rosmsg_search(mode, arg): 424 print "[%s]:"%found 425 rosmsg_debug(mode, found, options.raw)
426
427 -def rosmsg_md5(mode, type_):
428 package, base_type = roslib.names.package_resource_name(type_) 429 roslib.msgs.load_package_dependencies(package, load_recursive=True) 430 roslib.msgs.load_package(package) 431 if mode == roslib.msgs.EXT: 432 f = roslib.msgs.msg_file(package, base_type) 433 name, spec = roslib.msgs.load_from_file(f, package) 434 else: 435 f = roslib.srvs.srv_file(package, base_type) 436 name, spec = roslib.srvs.load_from_file(f, package) 437 gendeps_dict = roslib.gentools.get_dependencies(spec, package, compute_files=False) 438 return roslib.gentools.compute_md5(gendeps_dict)
439
440 ## soft-fail routine to check against generated md5sum to provide a sanity check. Only checks the Python md5 441 -def _rosmsg_md5_check(mode, type_, gendeps_md5):
442 try: 443 msg_class = roslib.message.get_message_class(type_) 444 if msg_class is not None and msg_class._md5sum != gendeps_md5: 445 print >> sys.stderr, "WARN: md5sum for compiled [%s] appears to differ from %s:\n\t%s vs %s"%(type_, mode, msg_class._md5sum, gendeps_md5) 446 except ImportError: pass
447
448 -def rosmsg_cmd_md5(mode, full):
449 parser = OptionParser(usage="usage: ros%s md5 <%s>"%(mode[1:], full)) 450 options, arg = _stdin_arg(parser, full) 451 452 if '/' in arg: #package specified 453 try: 454 md5 = rosmsg_md5(mode, arg) 455 print md5 456 _rosmsg_md5_check(mode, arg, md5) 457 except IOError: 458 print >> sys.stderr, "Cannot locate [%s]"%arg 459 else: 460 matches = [m for m in rosmsg_search(mode, arg)] 461 for found in matches: 462 try: 463 md5 = rosmsg_md5(mode, found) 464 print "[%s]: %s"%(found, md5) 465 _rosmsg_md5_check(mode, found, md5) 466 except IOError: 467 print >> sys.stderr, "Cannot locate [%s]"%found 468 if not matches: 469 print >> sys.stderr, "No messages matching the name [%s]"%arg
470
471 -def rosmsg_cmd_users(mode, full):
472 parser = OptionParser(usage="usage: ros%s users <%s>"%(mode[1:], full)) 473 options, arg = _stdin_arg(parser, full) 474 rosmsg_users(mode, arg)
475
476 -def rosmsg_cmd_package(mode, full):
477 parser = OptionParser(usage="usage: ros%s package <package>"%mode[1:]) 478 parser.add_option("-s", 479 dest="single_line", default=False,action="store_true", 480 help="list all msgs on a single line") 481 options, arg = _stdin_arg(parser, full) 482 if options.single_line: 483 print ' '.join(list_types(arg,mode=mode)) 484 else: 485 print '\n'.join(list_types(arg, mode=mode))
486
487 -def rosmsg_cmd_packages(mode, full):
488 parser = OptionParser(usage="usage: ros%s packages"%mode[1:]) 489 parser.add_option("-s", 490 dest="single_line", default=False,action="store_true", 491 help="list all packages on a single line") 492 options, args = parser.parse_args(sys.argv[2:]) 493 if options.single_line: 494 print ' '.join([p for p in iterate_packages(mode)]) 495 else: 496 print '\n'.join([p for p in iterate_packages(mode)])
497
498 -def fullusage(cmd):
499 """ 500 @param cmd: command name 501 @type cmd: str 502 @return: usage text for cmd 503 @rtype: str 504 """ 505 return """Commands: 506 \t%(cmd)s show\tShow message description 507 \t%(cmd)s users\tFind files that use message 508 \t%(cmd)s md5\tDisplay message md5sum 509 \t%(cmd)s package\tList messages in a package 510 \t%(cmd)s packages\tList packages that contain messages 511 512 Type %(cmd)s <command> -h for more detailed usage 513 """%locals()
514
515 -def rosmsgmain(mode=roslib.msgs.EXT):
516 """ 517 Main entry point for command-line tools (rosmsg/rossrv). 518 519 rosmsg can interact with either ros messages or ros services. The mode 520 param indicates which 521 @param mode: roslib.msgs.EXT or roslib.srvs.EXT 522 @type mode: str 523 """ 524 if len(sys.argv) == 1: 525 print fullusage('ros'+mode[1:]) 526 sys.exit(0) 527 if mode == roslib.msgs.EXT: 528 ext, full = mode, "message type" 529 else: 530 ext, full = mode, "service type" 531 532 try: 533 command = sys.argv[1] 534 if command == 'users': 535 rosmsg_cmd_users(ext, full) 536 elif command == 'show': 537 rosmsg_cmd_show(ext, full) 538 elif command == 'package': 539 rosmsg_cmd_package(ext, full) 540 elif command == 'packages': 541 rosmsg_cmd_packages(ext, full) 542 elif command == 'md5': 543 rosmsg_cmd_md5(ext, full) 544 else: 545 print fullusage('ros'+mode[1:]) 546 sys.exit(0) 547 except KeyError, e: 548 print >> sys.stderr, "Unknown message type: %s"%e 549 sys.exit(os.EX_USAGE) 550 except roslib.packages.InvalidROSPkgException, e: 551 print >> sys.stderr, "Invalid package: '%s'"%e 552 sys.exit(os.EX_USAGE) 553 except ValueError, e: 554 print >> sys.stderr, "Invalid type: '%s'"%e 555 sys.exit(os.EX_USAGE) 556 except ROSMsgException, e: 557 print >> sys.stderr, str(e) 558 sys.exit(1) 559 except KeyboardInterrupt: 560 pass
561