Package rospy :: Module rosconsole

Source Code for Module rospy.rosconsole

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2015, Chris Mansley, Open Source Robotics Foundation, 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  from __future__ import print_function 
 34   
 35  import os 
 36  import re 
 37  import socket 
 38  import sys 
 39   
 40  from datetime import datetime 
 41  from dateutil.tz import tzlocal 
 42   
 43  from argparse import ArgumentParser 
 44   
 45  import rosgraph 
 46  import rospy 
 47   
 48  from .logger_level_service_caller import LoggerLevelServiceCaller 
 49  from .logger_level_service_caller import ROSConsoleException 
 50   
 51  from rosgraph_msgs.msg import Log 
 52   
 53  NAME = 'rosconsole' 
54 55 56 -def error(status, msg):
57 print("%s: error: %s" % (NAME, msg), file=sys.stderr) 58 sys.exit(status)
59
60 61 -def _get_cmd_list_optparse():
62 from optparse import OptionParser 63 64 usage = "usage: %prog list <node>" 65 parser = OptionParser(usage=usage, prog=NAME) 66 67 return parser
68
69 70 -def _rosconsole_cmd_list(argv):
71 args = argv[2:] 72 parser = _get_cmd_list_optparse() 73 (options, args) = parser.parse_args(args) 74 75 if not args: 76 parser.error("you must specify a node to list loggers") 77 elif len(args) > 1: 78 parser.error("you may only specify one node to list") 79 80 logger_level = LoggerLevelServiceCaller() 81 82 loggers = logger_level.get_loggers(args[0]) 83 84 output = '\n'.join(loggers) 85 print(output)
86
87 88 -def _get_cmd_set_optparse():
89 from optparse import OptionParser 90 91 usage = "usage: %prog set <node> <logger> <level>" 92 levels = ', '.join(LoggerLevelServiceCaller().get_levels()) 93 usage += "\n\n <level> must be one of [" + levels + "]" 94 parser = OptionParser(usage=usage, prog=NAME) 95 96 return parser
97
98 99 -def _rosconsole_cmd_set(argv):
100 args = argv[2:] 101 parser = _get_cmd_set_optparse() 102 (options, args) = parser.parse_args(args) 103 104 if len(args) < 3: 105 parser.error("you must specify a node, a logger and a level") 106 107 logger_level = LoggerLevelServiceCaller() 108 logger_level.get_loggers(args[0]) 109 110 if args[1] not in logger_level._current_levels: 111 error(2, "node " + args[0] + " does not contain logger " + args[1]) 112 113 level = args[2].lower() 114 if level not in logger_level.get_levels(): 115 parser.error("invalid level") 116 117 logger_level.send_logger_change_message(args[0], args[1], args[2])
118
119 120 -def _get_cmd_get_optparse():
121 from optparse import OptionParser 122 123 usage = "usage: %prog get <node> <logger>" 124 parser = OptionParser(usage=usage, prog=NAME) 125 126 return parser
127
128 129 -def _rosconsole_cmd_get(argv):
130 args = argv[2:] 131 parser = _get_cmd_get_optparse() 132 (options, args) = parser.parse_args(args) 133 134 if len(args) < 2: 135 parser.error("you must specify a node and a logger") 136 137 logger_level = LoggerLevelServiceCaller() 138 logger_level.get_loggers(args[0]) 139 140 if args[1] not in logger_level._current_levels: 141 error(2, "node " + args[0] + " does not contain logger " + args[1]) 142 143 print(logger_level._current_levels[args[1]])
144
145 146 -class RosConsoleEcho(object):
147 # See ANSI/VT100 terminal color codes here: 148 # https://misc.flogisoft.com/bash/tip_colors_and_formatting 149 LEVEL_COLOR = { 150 'DEBUG': 92, # Light green 151 'INFO' : 97, # White 152 'WARN' : 93, # Light yellow 153 'ERROR': 91, # Light red 154 'FATAL': 95, # Light magenta 155 } 156 157 LEVEL_MAX_LENGTH = max([len(level) for level in LEVEL_COLOR.keys()]) 158
159 - def __init__(self, options):
160 self._filter = re.compile(options.filter) 161 self._level = getattr(Log, options.level.upper()) 162 self._nocolor = options.nocolor 163 self._verbose = options.verbose 164 165 self._level_string_map = {getattr(Log, level): self._stringify(level) for level in self.LEVEL_COLOR.keys()} 166 167 callback = self._once_callback if options.once else self._callback 168 rospy.Subscriber(options.topic, Log, callback)
169
170 - def _stringify(self, level):
171 string = level.ljust(RosConsoleEcho.LEVEL_MAX_LENGTH) 172 173 return string if self._nocolor else \ 174 '\033[{}m{}\033[0m'.format(self.LEVEL_COLOR[level], string)
175 176 @staticmethod
177 - def get_levels():
178 """Get levels sorted by increasing severity.""" 179 return sorted(RosConsoleEcho.LEVEL_COLOR.keys(), key=lambda level: getattr(Log, level))
180
181 - def _print(self, msg):
182 print('[ {} ] [\033[1m{}\033[21m]: {}'.format( 183 self._level_string_map[msg.level], msg.name, msg.msg)) 184 185 if self._verbose: 186 stamp_sec = msg.header.stamp.to_sec() 187 stamp_tz = datetime.fromtimestamp(stamp_sec, tzlocal()) 188 189 print(' [{} ({:.6f})] [{}]: {}:{}'.format( 190 stamp_tz, stamp_sec, msg.function, msg.file, msg.line))
191
192 - def _callback(self, msg):
193 if self._filter.search(msg.name) and msg.level >= self._level: 194 self._print(msg)
195
196 - def _once_callback(self, msg):
197 self._callback(msg) 198 rospy.signal_shutdown('Done')
199
200 201 -def _get_cmd_echo_argparse(prog):
202 parser = ArgumentParser(prog=prog, description='Print logger messages') 203 204 parser.add_argument('filter', metavar='FILTER', type=str, nargs='?', default='.*', 205 help='regular expression to filter the logger name (default: %(default)s)') 206 207 parser.add_argument('-l', '--level', action='store', metavar='LEVEL', 208 type=str, default='warn', dest='level', 209 choices=[level.lower() for level in RosConsoleEcho.get_levels()], 210 help='minimum logger level to print (default: %(default)s)') 211 212 parser.add_argument('-1', '--once', action='store_true', dest='once', 213 help='prints one logger message and exits') 214 215 parser.add_argument('--topic', action='store', metavar='TOPIC', 216 type=str, default='/rosout', dest='topic', 217 help='topic to read the logger messages from (default: %(default)s)') 218 219 parser.add_argument('--nocolor', action='store_true', help='output without color') 220 221 parser.add_argument('-v', '--verbose', action='store_true', help='print full logger details') 222 223 return parser
224
225 226 -def _rosconsole_cmd_echo(argv):
227 parser = _get_cmd_echo_argparse(' '.join([os.path.basename(argv[0]), argv[1]])) 228 args = parser.parse_args(argv[2:]) 229 230 rospy.init_node('rosconsole', anonymous=True) 231 232 rosconsole = RosConsoleEcho(args) 233 234 rospy.spin()
235
236 237 -def _fullusage():
238 print("""rosconsole is a command-line tool for configuring the logger level of ROS nodes. 239 240 Commands: 241 \trosconsole get\tdisplay level for a logger 242 \trosconsole list\tlist loggers for a node 243 \trosconsole set\tset level for a logger 244 \trosconsole echo\tprint logger messages 245 246 Type rosconsole <command> -h for more detailed usage, e.g. 'rosconsole list -h' 247 """) 248 sys.exit(getattr(os, 'EX_USAGE', 1))
249
250 251 -def main(argv=None):
252 if argv is None: 253 argv = sys.argv 254 255 # Initialize ourselves as a node, to ensure handling of namespace and 256 # remapping arguments 257 rospy.init_node('rosconsole', anonymous=True) 258 argv = rospy.myargv(argv) 259 260 # process argv 261 if len(argv) == 1: 262 _fullusage() 263 264 try: 265 command = argv[1] 266 if command == 'get': 267 _rosconsole_cmd_get(argv) 268 elif command == 'list': 269 _rosconsole_cmd_list(argv) 270 elif command == 'set': 271 _rosconsole_cmd_set(argv) 272 elif command == 'echo': 273 _rosconsole_cmd_echo(argv) 274 else: 275 _fullusage() 276 except socket.error as e: 277 error(1, 278 "Network communication failed; most likely failed to communicate with master: %s" % e) 279 except rosgraph.MasterException as e: 280 # mainly for invalid master URI/rosgraph.masterapi 281 error(1, str(e)) 282 except ROSConsoleException as e: 283 error(1, str(e)) 284 except KeyboardInterrupt: 285 pass
286