Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rosconsole echo #1324

Merged
merged 7 commits into from
Feb 26, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions clients/rospy/src/rospy/rosconsole.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,23 @@
from __future__ import print_function

import os
import re
import socket
import sys

from datetime import datetime
from dateutil.tz import tzlocal

from argparse import ArgumentParser
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems it would make sense to use argparse for the whole script. But I wouldn't couple that with the feature you are adding here. It is certainly good to use it for the new echo verb.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean I should make it local to the function where it's being used?

I honestly don't think it makes a big difference, but I can do that if that's what you're requesting here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, for not being clearer. I wasn't referring to the location of the import statement. Instead I was trying to point out that the first command line argument of rosconsole is inspected "manually" by looking at sys.argv and then argparse is only used for the new echo verb. It would be nice if argparse would be used for all arguments including the decision/selection based on the verb.

But that is clearly outside the scope of this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha! Yes, that makes a lot of sense, but definitely not a change for this PR.


import rosgraph
import rospy

from .logger_level_service_caller import LoggerLevelServiceCaller
from .logger_level_service_caller import ROSConsoleException

from rosgraph_msgs.msg import Log

NAME = 'rosconsole'


Expand Down Expand Up @@ -135,13 +143,105 @@ def _rosconsole_cmd_get(argv):
print(logger_level._current_levels[args[1]])


class RosConsoleEcho(object):
# See ANSI/VT100 terminal color codes here:
# https://misc.flogisoft.com/bash/tip_colors_and_formatting
LEVEL_COLOR = {
'DEBUG': 92, # Light green
'INFO' : 97, # White
'WARN' : 93, # Light yellow
'ERROR': 91, # Light red
'FATAL': 95, # Light magenta
}

LEVEL_MAX_LENGTH = max([len(level) for level in LEVEL_COLOR.keys()])

def __init__(self, options):
self._filter = re.compile(options.filter)
self._level = getattr(Log, options.level.upper())
self._nocolor = options.nocolor
self._verbose = options.verbose

self._level_string_map = {getattr(Log, level): self._stringify(level) for level in self.LEVEL_COLOR.keys()}

callback = self._once_callback if options.once else self._callback
rospy.Subscriber(options.topic, Log, callback)

def _stringify(self, level):
string = level.ljust(RosConsoleEcho.LEVEL_MAX_LENGTH)

return string if self._nocolor else \
'\033[{}m{}\033[0m'.format(self.LEVEL_COLOR[level], string)

@staticmethod
def get_levels():
"""Get levels sorted by increasing severity."""
return sorted(RosConsoleEcho.LEVEL_COLOR.keys(), key=lambda level: getattr(Log, level))

def _print(self, msg):
print('[ {} ] [\033[1m{}\033[21m]: {}'.format(
self._level_string_map[msg.level], msg.name, msg.msg))

if self._verbose:
stamp_sec = msg.header.stamp.to_sec()
stamp_tz = datetime.fromtimestamp(stamp_sec, tzlocal())

print(' [{} ({:.6f})] [{}]: {}:{}'.format(
stamp_tz, stamp_sec, msg.function, msg.file, msg.line))

def _callback(self, msg):
if self._filter.search(msg.name) and msg.level >= self._level:
self._print(msg)

def _once_callback(self, msg):
self._callback(msg)
rospy.signal_shutdown('Done')


def _get_cmd_echo_argparse(prog):
parser = ArgumentParser(prog=prog, description='Print logger messages')

parser.add_argument('filter', metavar='FILTER', type=str, nargs='?', default='.*',
help='regular expression to filter the logger name (default: %(default)s)')

parser.add_argument('-l', '--level', action='store', metavar='LEVEL',
type=str, default='warn', dest='level',
choices=[level.lower() for level in RosConsoleEcho.get_levels()],
help='minimum logger level to print (default: %(default)s)')

parser.add_argument('-1', '--once', action='store_true', dest='once',
help='prints one logger message and exits')

parser.add_argument('--topic', action='store', metavar='TOPIC',
type=str, default='/rosout', dest='topic',
help='topic to read the logger messages from (default: %(default)s)')

parser.add_argument('--nocolor', action='store_true', help='output without color')

parser.add_argument('-v', '--verbose', action='store_true', help='print full logger details')

return parser


def _rosconsole_cmd_echo(argv):
parser = _get_cmd_echo_argparse(' '.join([os.path.basename(argv[0]), argv[1]]))
args = parser.parse_args(argv[2:])

rospy.init_node('rosconsole', anonymous=True)

rosconsole = RosConsoleEcho(args)

rospy.spin()


def _fullusage():
print("""rosconsole is a command-line tool for configuring the logger level of ROS nodes.

Commands:
\trosconsole get\tdisplay level for a logger
\trosconsole list\tlist loggers for a node
\trosconsole set\tset level for a logger
\trosconsole echo\tprint logger messages

Type rosconsole <command> -h for more detailed usage, e.g. 'rosconsole list -h'
""")
Expand Down Expand Up @@ -169,6 +269,8 @@ def main(argv=None):
_rosconsole_cmd_list(argv)
elif command == 'set':
_rosconsole_cmd_set(argv)
elif command == 'echo':
_rosconsole_cmd_echo(argv)
else:
_fullusage()
except socket.error as e:
Expand Down