1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 """
34 Implements rosmsg/rossrv command-line tools.
35
36 The code API of the rosmsg module is unstable.
37 """
38
39 from __future__ import print_function
40
41 import collections
42 import inspect
43 import os
44 import sys
45 import yaml
46
47 import rospkg
48 import genmsg
49
50 import roslib.message
51 import rosbag
52
53 from optparse import OptionParser
54
55 MODE_MSG = '.msg'
56 MODE_SRV = '.srv'
57
61
62
63
64
65 MAX_DEFAULT_NON_FLOW_ITEMS = 4
66
67
69 if not isinstance(node, yaml.MappingNode):
70 raise yaml.constructor.ConstructorError(None, None,
71 "expected a mapping node, but found %s" % node.id,
72 node.start_mark)
73 mapping = collections.OrderedDict()
74 for key_node, value_node in node.value:
75 key = self.construct_object(key_node, deep=deep)
76 if not isinstance(key, collections.Hashable):
77 raise yaml.constructor.ConstructorError("while constructing a mapping", node.start_mark,
78 "found unhashable key", key_node.start_mark)
79 value = self.construct_object(value_node, deep=deep)
80 mapping[key] = value
81 return mapping
82
84 data = collections.OrderedDict()
85 yield data
86 value = self.construct_mapping(node)
87 data.update(value)
88
90 value = []
91 node = yaml.MappingNode(tag, value, flow_style=flow_style)
92 if self.alias_key is not None:
93 self.represented_objects[self.alias_key] = node
94 best_style = True
95 if hasattr(mapping, 'items'):
96 mapping = list(mapping.items())
97 for item_key, item_value in mapping:
98 node_key = self.represent_data(item_key)
99 node_value = self.represent_data(item_value)
100 if not (isinstance(node_key, yaml.ScalarNode) and not node_key.style):
101 best_style = False
102 if not (isinstance(node_value, yaml.ScalarNode) and not node_value.style):
103 best_style = False
104 value.append((node_key, node_value))
105 if flow_style is None:
106 if self.default_flow_style is not None:
107 node.flow_style = self.default_flow_style
108 else:
109 node.flow_style = best_style
110 return node
111
112
113
114
116 """
117 returns a single instance of field_type, where field_type can be a
118 message or ros primitive or an flexible size array.
119 """
120 field_type = field_type.strip().rstrip("[]")
121 if field_type == "empty":
122 return None
123 if not "/" in field_type:
124
125
126 if field_type in ['byte', 'int8', 'int16', 'int32', 'int64',\
127 'char', 'uint8', 'uint16', 'uint32', 'uint64']:
128 return 0
129 elif field_type in ['float32', 'float64']:
130 return 0
131 elif field_type in ['string']:
132 return ""
133 elif field_type == 'bool':
134 return False
135 elif field_type == 'time':
136 field_type = "std_msgs/Time"
137 elif field_type == 'duration':
138 field_type = "std_msgs/Duration"
139 elif field_type == 'Header':
140 field_type = "std_msgs/Header"
141 else:
142 if default_package is None:
143 return None
144 field_type = default_package + "/" + field_type
145 msg_class = roslib.message.get_message_class(field_type)
146 if (msg_class == None):
147
148 return None
149 instance = msg_class()
150 return instance
151
152 -def get_yaml_for_msg(msg, prefix='', time_offset=None, current_time=None, field_filter=None, flow_style_ = None, fill_arrays_ = False):
153 """
154 Builds a YAML string of message.
155 @param msg: A object, dict or array
156 @param flow_style_: if True, produces one line with brackets, if false uses multiple lines with indentation, if None uses both using heuristics
157 @param prefix: prefixes all lines with this string
158 @param fill_arrays_: if True, for all flexible size arrays an element will be generated
159 @param current_time: currently not used. Only provided for API compatibility. current_time passes in the current time with respect to the message.
160 @type current_time: Time
161 @param field_filter: filter the fields that are strified for Messages.
162 @type field_filter: fn(Message)->iter(str)
163 @type flow_style_: bool
164 @return: a string
165 """
166 def object_representer(dumper, obj):
167 ndict = collections.OrderedDict()
168 index = 0
169
170 if field_filter != None:
171 fields = list(field_filter(obj))
172 else:
173 fields = obj.__slots__
174 for key in fields:
175 if not key.startswith('_'):
176 val = getattr(obj, key)
177 if type(val) == list and len(val) > MAX_DEFAULT_NON_FLOW_ITEMS:
178 dumper.default_flow_style = flow_style_
179 if time_offset is not None and isinstance(val, Time):
180 ndict[key] = val-time_offset
181
182 elif fill_arrays_ == True and val == []:
183 message_type = obj._slot_types[index]
184 if (obj._type != None) and "/" in obj._type:
185 def_pack = obj._type.split("/")[0]
186 instance = get_array_type_instance(message_type, default_package = def_pack)
187 if instance == None:
188
189 ndict[key] = val
190 else:
191 ndict[key] = [instance]
192 elif not inspect.ismethod(val) and not inspect.isfunction(val):
193 ndict[key] = val
194 index += 1
195
196 if len(ndict) > MAX_DEFAULT_NON_FLOW_ITEMS:
197 dumper.default_flow_style = flow_style_
198 return dumper.represent_dict(ndict)
199 yaml.representer.SafeRepresenter.add_representer(None, object_representer)
200
201
202
203
204 initial_flow_style = False
205 if flow_style_ == True:
206 initial_flow_style = True
207
208
209
210
211 txt = yaml.safe_dump(msg,
212
213 default_flow_style = initial_flow_style,
214
215 default_style = '',
216
217
218
219
220
221
222
223
224
225
226
227
228
229 )
230 if prefix != None and prefix != '':
231 result = prefix + ("\n"+prefix).join(txt.splitlines())
232 else:
233 result = txt.rstrip('\n')
234 return result
235
236
238 """
239 returns a function to use as filter that returns all objects slots except those with names in list.
240 """
241 return lambda obj : filter(lambda slotname : not slotname in names, obj.__slots__)
242
243
245 if "OrderedDict" in collections.__dict__:
246 yaml.constructor.BaseConstructor.construct_mapping = construct_ordered_mapping
247 yaml.constructor.Constructor.add_constructor(
248 'tag:yaml.org,2002:map',
249 construct_yaml_map_with_ordered_dict)
250
251 yaml.representer.BaseRepresenter.represent_mapping = represent_ordered_mapping
252 yaml.representer.Representer.add_representer(collections.OrderedDict,
253 yaml.representer.SafeRepresenter.represent_dict)
254
256 init_rosmsg_proto()
257 parser = OptionParser(usage="usage: rosmsgproto msg/srv [options]",
258 description="Produces output or a msg or service request, intended for tab completion support.")
259 parser.add_option("-f","--flow_style",
260 dest="flow_style", type="int", default=None, action="store",
261 help="if 1 always use brackets, if 0 never use brackets. Default is a heuristic mix.")
262 parser.add_option("-e","--empty-arrays",
263 dest="empty_arrays", default=False, action="store_true",
264 help="if true flexible size arrays are not filled with default instance")
265 parser.add_option("-s","--silent",
266 dest="silent", default=False, action="store_true",
267 help="if true supresses all error messages")
268 parser.add_option("-p", "--prefix", metavar="prefix", default="",
269 help="prefix to print before each line, can be used for indent")
270 parser.add_option("-H","--no-hyphens",
271 dest="no_hyphens", default="", action="store_true",
272 help="if true output has no outer hyphens")
273 parser.add_option("-x", "--exclude-slots", metavar="exclude_slots", default="",
274 help="comma separated list of slot names not to print")
275
276 options, args = parser.parse_args(args)
277
278 try:
279 if len(args) < 2:
280 raise RosMsgProtoArgsException("Insufficient arguments")
281 mode = ".%s"%args[0]
282 message_type=args[1]
283 field_filter = None
284 if options.exclude_slots != None and options.exclude_slots.strip() != "":
285 field_filter = create_names_filter(options.exclude_slots.split(','))
286
287
288
289
290
291
292
293 if '::' in message_type:
294 if not options.silent:
295 parser.error("rosmsgproto does not understand C++-style namespaces (i.e. '::').\nPlease refer to msg/srv types as 'package_name/Type'.")
296 elif '.' in message_type:
297 if not options.silent:
298 parser.error("invalid message type '%s'.\nPlease refer to msg/srv types as 'package_name/Type'." % message_type)
299 if not '/' in message_type:
300
301 results = []
302 for found in rosmsg_search(rospkg.RosPack(), mode, message_type):
303 results.append(found)
304 if len(results) > 1:
305 raise RosMsgProtoException("Ambiguous message name %s"%message_type)
306 elif len(results) < 1:
307 raise RosMsgProtoException("Unknown message name %s"%message_type)
308 else:
309 message_type=results[0]
310
311 if mode == MODE_SRV:
312 msg_class = roslib.message.get_service_class(message_type)
313 if (msg_class == None):
314 raise RosMsgProtoException("Unknown service class: %s"%message_type)
315 instance = msg_class()._request_class()
316 elif mode == MODE_MSG:
317 msg_class = roslib.message.get_message_class(message_type)
318 if (msg_class == None):
319 raise RosMsgProtoException("Unknown message class: %s"%message_type)
320 instance = msg_class()
321 else:
322 raise RosMsgProtoException("Invalid mode: %s"%mode)
323 txt = get_yaml_for_msg(instance,
324 prefix = options.prefix,
325 flow_style_ = options.flow_style,
326 fill_arrays_ = not options.empty_arrays,
327 field_filter = field_filter)
328
329 if options.no_hyphens == True:
330 return txt
331 else:
332 return '"' + txt + '"'
333
334 except KeyError as e:
335 if not options.silent:
336 sys.stderr.write("Unknown message type: %s"%e, file=sys.stderr)
337 sys.exit(getattr(os, 'EX_USAGE', 1))
338
339
340
341
342 except ValueError, e:
343 if not options.silent:
344 sys.stderr.write("Invalid type: '%s'"%e)
345 sys.exit(getattr(os, 'EX_USAGE', 1))
346 except RosMsgProtoException as e:
347 if not options.silent:
348 sys.stderr.write(str(e))
349 sys.exit(1)
350 except RosMsgProtoArgsException as e:
351 if not options.silent:
352 sys.stderr.write("%s"%e)
353 sys.exit(getattr(os, 'EX_USAGE', 1))
354 except KeyboardInterrupt:
355 pass
356
357
358
359 try:
360 from cStringIO import StringIO
361 except ImportError:
362 from io import StringIO
363 -def spec_to_str(msg_context, spec, buff=None, indent=''):
364 """
365 Convert spec into a string representation. Helper routine for MsgSpec.
366 :param indent: internal use only, ``str``
367 :param buff: internal use only, ``StringIO``
368 :returns: string representation of spec, ``str``
369 """
370 if buff is None:
371 buff = StringIO()
372 for c in spec.constants:
373 buff.write("%s%s %s=%s\n"%(indent, c.type, c.name, c.val_text))
374 for type_, name in zip(spec.types, spec.names):
375 buff.write("%s%s %s\n"%(indent, type_, name))
376 base_type = genmsg.msgs.bare_msg_type(type_)
377 if not base_type in genmsg.msgs.BUILTIN_TYPES:
378 subspec = msg_context.get_registered(base_type)
379 spec_to_str(msg_context, subspec, buff, indent + ' ')
380 return buff.getvalue()
381
382 -def get_srv_text(type_, raw=False, rospack=None):
383 """
384 Get .srv file for type_ as text
385 :param type_: service type, ``str``
386 :param raw: if True, include comments and whitespace (default False), ``bool``
387 :returns: text of .srv file, ``str``
388 @raise ROSMsgException: if type_ is unknown
389 """
390 if rospack is None:
391 rospack = rospkg.RosPack()
392 srv_search_path = {}
393 msg_search_path = {}
394 for p in rospack.list():
395 path = rospack.get_path(p)
396 msg_search_path[p] = [os.path.join(path, 'msg')]
397 srv_search_path[p] = [os.path.join(path, 'srv')]
398
399
400 context = genmsg.MsgContext.create_default()
401 try:
402 spec = genmsg.load_srv_by_type(context, type_, srv_search_path)
403 genmsg.load_depends(context, spec, msg_search_path)
404 except Exception as e:
405 raise ROSMsgException("Unknown srv type [%s]: %s"%(type_, e))
406
407 if raw:
408 return spec.text
409 else:
410 return spec_to_str(context, spec.request)+'---\n'+spec_to_str(context, spec.response)
411
412 -def get_msg_text(type_, raw=False, rospack=None):
413 """
414 Get .msg file for type_ as text
415 :param type_: message type, ``str``
416 :param raw: if True, include comments and whitespace (default False), ``bool``
417 :returns: text of .msg file, ``str``
418 :raises :exc:`ROSMsgException` If type_ is unknown
419 """
420 if rospack is None:
421 rospack = rospkg.RosPack()
422 search_path = {}
423 for p in rospack.list():
424 search_path[p] = [os.path.join(rospack.get_path(p), 'msg')]
425
426 context = genmsg.MsgContext.create_default()
427 try:
428 spec = genmsg.load_msg_by_type(context, type_, search_path)
429 genmsg.load_depends(context, spec, search_path)
430 except Exception as e:
431 raise ROSMsgException("Unable to load msg [%s]: %s"%(type_, e))
432
433 if raw:
434 return spec.text
435 else:
436 return spec_to_str(context, spec)
437
439 """
440 Prints contents of msg/srv file
441 :param mode: MODE_MSG or MODE_SRV, ``str``
442 """
443 if mode == MODE_SRV:
444 print(get_srv_text(type_, raw=raw, rospack=rospack))
445 elif mode == MODE_MSG:
446 print(get_msg_text(type_, raw=raw, rospack=rospack))
447 else:
448 raise ROSMsgException("Invalid mode for debug: %s"%mode)
449
451 """
452 List srvs contained in package
453 :param package: package name, ``str``
454 :returns: list of srvs in package, ``[str]``
455 """
456 return list_types(package, mode=MODE_SRV)
457
459 """
460 List msgs contained in package
461 :param package: package name, ``str``
462 :returns: list of msgs in package, ``[str]``
463 """
464 return list_types(package)
465
467 """
468 Lists msg/srvs contained in package
469 :param package: package name, ``str``
470 :param mode: MODE_MSG or MODE_SRV. Defaults to msgs, ``str``
471 :returns: list of msgs/srv in package, ``[str]``
472 """
473 rospack = rospkg.RosPack()
474 if mode == MODE_MSG:
475 subdir = 'msg'
476 elif mode == MODE_SRV:
477 subdir = 'srv'
478 else:
479 raise ValueError('Unknown mode for list_types: %s'%mode)
480
481 path = os.path.join(rospack.get_path(package), subdir)
482
483 return [genmsg.resource_name(package, t) for t in _list_types(path, subdir, mode)]
484
486 def mfilter(f):
487 """
488 Predicate for filtering directory list. matches message files
489 :param f: filename, ``str``
490 """
491 return os.path.isfile(f) and f.endswith(ext)
492 return mfilter
493
495 """
496 List all messages in the specified package
497 :param package str: name of package to search
498 :param include_depends bool: if True, will also list messages in package dependencies
499 :returns [str]: message type names
500 """
501 types = _list_resources(path, _msg_filter(ext))
502 result = [x[:-len(ext)] for x in types]
503 result.sort()
504 return result
505
507 """
508 List resources in a package directory within a particular
509 subdirectory. This is useful for listing messages, services, etc...
510 :param rfilter: resource filter function that returns true if filename is the desired resource type, ``fn(filename)->bool``
511 """
512 resources = []
513 if os.path.isdir(path):
514 resources = [f for f in os.listdir(path) if rfilter(os.path.join(path, f))]
515 else:
516 resources = []
517 return resources
518
520 """
521 Iterator for packages that contain messages/services
522 :param mode: .msg or .srv, ``str``
523 """
524 if mode == MODE_MSG:
525 subdir = 'msg'
526 elif mode == MODE_SRV:
527 subdir = 'srv'
528 else:
529 raise ValueError('Unknown mode for iterate_packages: %s'%mode)
530
531 pkgs = rospack.list()
532 for p in pkgs:
533 d = os.path.join(rospack.get_path(p), subdir)
534 if os.path.isdir(d):
535 yield p, d
536
538 """
539 Iterator for all packages that contain a message matching base_type
540
541 :param base_type: message base type to match, e.g. 'String' would match std_msgs/String, ``str``
542 """
543 for p, path in iterate_packages(rospack, mode):
544 if os.path.isfile(os.path.join(path, "%s%s"%(base_type, mode))):
545 yield genmsg.resource_name(p, base_type)
546
548 options, args = parser.parse_args(sys.argv[2:])
549
550 if not args:
551 arg = None
552 while not arg:
553 arg = sys.stdin.readline().strip()
554 return options, arg
555 else:
556 if len(args) > 1:
557 parser.error("you may only specify one %s"%full)
558 return options, args[0]
559
561 cmd = "ros%s"%(mode[1:])
562 parser = OptionParser(usage="usage: %s show [options] <%s>"%(cmd, full))
563 parser.add_option("-r", "--raw",
564 dest="raw", default=False,action="store_true",
565 help="show raw message text, including comments")
566 parser.add_option("-b", "--bag",
567 dest="bag", default=None,
568 help="show message from .bag file", metavar="BAGFILE")
569 options, arg = _stdin_arg(parser, full)
570 if arg.endswith(mode):
571 arg = arg[:-(len(mode))]
572
573
574 if '::' in arg:
575 parser.error(cmd+" does not understand C++-style namespaces (i.e. '::').\nPlease refer to msg/srv types as 'package_name/Type'.")
576 elif '.' in arg:
577 parser.error("invalid message type '%s'.\nPlease refer to msg/srv types as 'package_name/Type'." % arg)
578 if options.bag:
579 bag_file = options.bag
580 if not os.path.exists(bag_file):
581 raise ROSMsgException("ERROR: bag file [%s] does not exist"%bag_file)
582 for topic, msg, t in rosbag.Bag(bag_file).read_messages(raw=True):
583 datatype, _, _, _, pytype = msg
584 if datatype == arg:
585 print(get_msg_text(datatype, options.raw, pytype._full_text))
586 break
587 else:
588 rospack = rospkg.RosPack()
589 if '/' in arg:
590 rosmsg_debug(rospack, mode, arg, options.raw)
591 else:
592 for found in rosmsg_search(rospack, mode, arg):
593 print("[%s]:"%found)
594 rosmsg_debug(rospack, mode, found, options.raw)
595
597 try:
598 if mode == MODE_MSG:
599 msg_class = roslib.message.get_message_class(type_)
600 else:
601 msg_class = roslib.message.get_service_class(type_)
602 except ImportError:
603 raise IOError("cannot load [%s]"%(type_))
604 if msg_class is not None:
605 return msg_class._md5sum
606 else:
607 raise IOError("cannot load [%s]"%(type_))
608
610 parser = OptionParser(usage="usage: ros%s md5 <%s>"%(mode[1:], full))
611 options, arg = _stdin_arg(parser, full)
612
613 if '/' in arg:
614 try:
615 md5 = rosmsg_md5(mode, arg)
616 print(md5)
617 except IOError:
618 print("Cannot locate [%s]"%arg, file=sys.stderr)
619 else:
620 rospack = rospkg.RosPack()
621 matches = [m for m in rosmsg_search(rospack, mode, arg)]
622 for found in matches:
623 try:
624 md5 = rosmsg_md5(mode, found)
625 print("[%s]: %s"%(found, md5))
626 except IOError:
627 print("Cannot locate [%s]"%found, file=sys.stderr)
628 if not matches:
629 print("No messages matching the name [%s]"%arg, file=sys.stderr)
630
632 parser = OptionParser(usage="usage: ros%s package <package>"%mode[1:])
633 parser.add_option("-s",
634 dest="single_line", default=False,action="store_true",
635 help="list all msgs on a single line")
636 options, arg = _stdin_arg(parser, full)
637 joinstring='\n'
638 if options.single_line:
639 joinstring=' '
640 print(joinstring.join(list_types(arg, mode=mode)))
641
643 if argv is None:
644 argv = sys.argv[1:]
645 parser = OptionParser(usage="usage: ros%s packages"%mode[1:])
646 parser.add_option("-s",
647 dest="single_line", default=False,action="store_true",
648 help="list all packages on a single line")
649 options, args = parser.parse_args(argv[1:])
650 rospack = rospkg.RosPack()
651 joinstring='\n'
652 if options.single_line:
653 joinstring=' '
654 p1 = [p for p, _ in iterate_packages(rospack, mode)]
655 p1.sort()
656 print(joinstring.join(p1))
657
659 if argv is None:
660 argv = sys.argv[1:]
661 parser = OptionParser(usage="usage: ros%s list"%mode[1:])
662 options, args = parser.parse_args(argv[1:])
663 if mode == MODE_MSG:
664 subdir = 'msg'
665 elif mode == MODE_SRV:
666 subdir = 'srv'
667 else:
668 raise ValueError('Unknown mode for iterate_packages: %s'%mode)
669 rospack = rospkg.RosPack()
670 packs = sorted([x for x in iterate_packages(rospack, mode)])
671 for (p, direc) in packs:
672 for file in _list_types(direc, subdir, mode):
673 print( "%s/%s"%(p, file))
674
675
677 """
678 :param cmd: command name, ``str``
679 :returns: usage text for cmd, ``str``
680 """
681 return """rosmsg is a command-line tool for displaying information about ROS Message types.
682
683 Commands:
684 \t%(cmd)s show\tShow message description
685 \t%(cmd)s list\tList all messages
686 \t%(cmd)s md5\tDisplay message md5sum
687 \t%(cmd)s package\tList messages in a package
688 \t%(cmd)s packages\tList packages that contain messages
689
690 Type %(cmd)s <command> -h for more detailed usage
691 """%locals()
692
694 """
695 Main entry point for command-line tools (rosmsg/rossrv).
696
697 rosmsg can interact with either ros messages or ros services. The mode
698 param indicates which
699 :param mode: MODE_MSG or MODE_SRV, ``str``
700 """
701 try:
702 if mode == MODE_MSG:
703 ext, full = mode, "message type"
704 elif mode == MODE_SRV:
705 ext, full = mode, "service type"
706 else:
707 raise ROSMsgException("Invalid mode: %s"%mode)
708 if len(sys.argv) == 1:
709 print(fullusage('ros'+mode[1:]))
710 sys.exit(0)
711
712 command = sys.argv[1]
713 if command == 'show':
714 rosmsg_cmd_show(ext, full)
715 elif command == 'package':
716 rosmsg_cmd_package(ext, full)
717 elif command == 'packages':
718 rosmsg_cmd_packages(ext, full)
719 elif command == 'list':
720 rosmsg_cmd_list(ext, full)
721 elif command == 'md5':
722 rosmsg_cmd_md5(ext, full)
723 elif command == '--help':
724 print(fullusage('ros'+mode[1:]))
725 sys.exit(0)
726 else:
727 print(fullusage('ros'+mode[1:]))
728 sys.exit(getattr(os, 'EX_USAGE', 1))
729 except KeyError as e:
730 print("Unknown message type: %s"%e, file=sys.stderr)
731 sys.exit(getattr(os, 'EX_USAGE', 1))
732 except rospkg.ResourceNotFound as e:
733 print("Invalid package: %s"%e, file=sys.stderr)
734 sys.exit(getattr(os, 'EX_USAGE', 1))
735 except ValueError as e:
736 print("Invalid type: '%s'"%e, file=sys.stderr)
737 sys.exit(getattr(os, 'EX_USAGE', 1))
738 except ROSMsgException as e:
739 print(str(e), file=sys.stderr)
740 sys.exit(1)
741 except KeyboardInterrupt:
742 pass
743