上次我们提到,run_shell.py文件里首先导入了一些模块,然后通过Python的argparse
模块来创建一个命令行解析器对象,以及为解析器对象添加一些通用参数,接下来,让我们看一下一些默认命令。
如果args.command没有设置的话,默认命令就是shell。args.no_python就被设置为false。logger就根据日志记录器里args.verbose的值进行相应的设置。
if args.command is None:
args.command = 'shell' #shell 命令行环境
args.no_ipython = False
logger = Logger(verbose=args.verbose)
app_shutdown_token = Event()
那日志记录器又是个啥呢?那就让我们来查查。选中Logger,然后右键,go to defination(转到定义)。
这时,我们会发现跳转到了fiber文件夹下的utils.py文件下,这里便有着Logger的定义,下面是它的功能
- 这是一段日志记录器类的代码,可以将消息打印到终端或者控制台窗口。有以下功能:
- 可以设置日志记录的详细程度(通过verbose参数)
- 可以设置日志记录的前缀(通过indent的方法)
- 可以将消息打印在倒数第二行(通过print_on_second_last_line)
- 可以以不同的颜色打印消息(通过print_colored)
- 提供了几个不同级别的日志记录方法('debug','success','info','notofy','warn','error')
- 根据平台的不同,它使用了不同的方法来控制颜色和光标位置。在Windows系统上,它使用了Win32 API来控制颜色和光标位置。在其他系统上,它使用了VT100转义序列来控制颜色和光标位
class Logger():
"""
Logs messages to stdout
"""
COLOR_DEFAULT = 0
COLOR_GREEN = 1
COLOR_CYAN = 2
COLOR_YELLOW = 3
COLOR_RED = 4
_VT100Colors = {
COLOR_GREEN: '\x1b[92;1m',
COLOR_CYAN: '\x1b[96;1m',
COLOR_YELLOW: '\x1b[93;1m',
COLOR_RED: '\x1b[91;1m',
COLOR_DEFAULT: '\x1b[0m'
}
_Win32Colors = {
COLOR_GREEN: 0x0A,
COLOR_CYAN: 0x0B,
COLOR_YELLOW: 0x0E,
COLOR_RED: 0x0C,
COLOR_DEFAULT: 0x07
}
def __init__(self, verbose=True):
self._prefix = ''
self._skip_bottom_line = False # If true, messages are printed one line above the cursor
self._verbose = verbose
self._print_lock = threading.Lock()
if platform.system() == 'Windows':
self._stdout_buf = win32console.GetStdHandle(win32console.STD_OUTPUT_HANDLE)
def indent(self, prefix=' '):
indented_logger = Logger()
indented_logger._prefix = self._prefix + prefix
return indented_logger
def print_on_second_last_line(self, text, color):
"""
Prints a text on the second last line.
This can be used to print a message above the command
prompt. If the command prompt spans multiple lines
there will be glitches.
If the printed text spans multiple lines there will also
be glitches (though this could be fixed).
"""
if platform.system() == 'Windows':
# Windows <10 doesn't understand VT100 escape codes and the colorama
# also doesn't support the specific escape codes we need so we use the
# native Win32 API.
info = self._stdout_buf.GetConsoleScreenBufferInfo()
cursor_pos = info['CursorPosition']
scroll_rect=win32console.PySMALL_RECTType(
Left=0, Top=1,
Right=info['Window'].Right,
Bottom=cursor_pos.Y-1)
scroll_dest = win32console.PyCOORDType(scroll_rect.Left, scroll_rect.Top-1)
self._stdout_buf.ScrollConsoleScreenBuffer(
scroll_rect, scroll_rect, scroll_dest, # clipping rect is same as scroll rect
u' ', Logger._Win32Colors[color]) # fill with empty cells with the desired color attributes
line_start = win32console.PyCOORDType(0, cursor_pos.Y-1)
self._stdout_buf.WriteConsoleOutputCharacter(text, line_start)
else:
# Assume we're in a terminal that interprets VT100 escape codes.
# TODO: test on macOS
# Escape character sequence:
# ESC 7: store cursor position
# ESC 1A: move cursor up by one
# ESC 1S: scroll entire viewport by one
# ESC 1L: insert 1 line at cursor position
# (print text)
# ESC 8: restore old cursor position
self._print_lock.acquire()
sys.stdout.write('\x1b7\x1b[1A\x1b[1S\x1b[1L')
sys.stdout.write(Logger._VT100Colors[color] + text + Logger._VT100Colors[Logger.COLOR_DEFAULT])
sys.stdout.write('\x1b8')
sys.stdout.flush()
self._print_lock.release()
OK,现在关于Logger已经基本了解了,该返回我们原来的代码了,那我们如何快速回到我们刚刚所在的位置呢?很简单,只需要按住Alt+左键,就可以回到我们刚刚的页面啦。
接下来,如果args.command==shell的话,我们就导入ref_tool_shell模块,然后调用'launch_shell'函数来启动命令行界面。其中,'launch_shell'函数接受三个参数,'args'是用户传入的参数,logger是用于记录日志的,'app_shutdown_token'用来通知应用程序停止运行。
if args.command == 'shell': #shell 用于与Dummy-Robot进行交互的命令行界面
# if ".dev" in ref_tool.__version__:
# print("")
# logger.warn("Developer Preview")
# print("")
import ref_tool.shell
ref_tool.shell.launch_shell(args, logger, app_shutdown_token)
看到这里,有点人可能会疑问,ref_tool_shell模块是个啥?'launch_shell'又是用来干嘛的呢?
想要知道答案,其实很简单,还记得我们刚刚的操作吗?选中、右键,go to defination
这样我们就来到它的定义啦,原来是ref_tool文件夹下shell.py文件里的一个函数,这个函数的作用呢就是启动一个交互式的Python命令行界面。
def launch_shell(args, logger, app_shutdown_token):
"""
Launches an interactive python or IPython command line
interface.
As ODrives are connected they are made available as
"odrv0", "odrv1", ...
"""
interactive_variables = {
'start_liveplotter': start_liveplotter,
}
fibre.launch_shell(args,
interactive_variables,
print_banner, print_help,
logger, app_shutdown_token,
branding_short="dummy", branding_long="Dummy-Robot")
然后Alt+右键,回到我们刚才的界面
后面不同的命令行也是同样的解析方式啦,在这里我就不过多赘述,直接上它们的具体功能给大家。
- liveplotter:启动实时绘图工具,绘制Dummy-Robot的位置估计。
- drv-status:打印电机驱动器的状态信息
- rate-test:进行性能测试,测试Dummy-Robot的速率
- udev-setup:设置udev规则,用于在Linux上自动识别和配置Dummy-Robot
- generate-code:根据模板生成代码,用于控制Dummy-Robot
- backup-config:备份Dummy-Robot的配置信息
- restore-config:恢复Dummy-Robot的配置信息
除此之外的命令行则不会被执行,最后呢,就是停止运行应用程序。
elif args.command == 'liveplotter':
from ref_tool.utils import start_liveplotter #引入实时绘图工具
print("Waiting for ODrive...")
ref_unit = ref_tool.find_any(path=args.path, serial_number=args.serial_number, #绘制dummy参数
search_cancellation_token=app_shutdown_token,
channel_termination_token=app_shutdown_token)
# If you want to plot different values, change them here.
# You can plot any number of values concurrently.
cancellation_token = start_liveplotter(lambda: [
ref_unit.axis0.encoder.pos_estimate,
ref_unit.axis1.encoder.pos_estimate,
])
print("Showing plot. Press Ctrl+C to exit.")
while not cancellation_token.is_set():
time.sleep(1)
elif args.command == 'drv-status':#进行性能测试,测试Dummy-Robot的速率
from ref_tool.utils import print_drv_regs
print("Waiting for ODrive...")
ref_unit = ref_tool.find_any(path=args.path, serial_number=args.serial_number,
search_cancellation_token=app_shutdown_token,
channel_termination_token=app_shutdown_token)
print_drv_regs("Motor 0", ref_unit.axis0.motor)
print_drv_regs("Motor 1", ref_unit.axis1.motor)
elif args.command == 'rate-test':#打印电机驱动器状态
from ref_tool.utils import rate_test
print("Waiting for ODrive...")
ref_unit = ref_tool.find_any(path=args.path, serial_number=args.serial_number,
search_cancellation_token=app_shutdown_token,
channel_termination_token=app_shutdown_token)
rate_test(ref_unit)
elif args.command == 'udev-setup':#设置udev规则,用于在Linux上自动识别和配置Dummy-Robot
from ref_tool.version import setup_udev_rules
setup_udev_rules(logger)
elif args.command == 'generate-code':#根据模板生成代码,用于控制Dummy-Robot
from ref_tool.code_generator import generate_code
ref_unit = ref_tool.find_any(path=args.path, serial_number=args.serial_number,
channel_termination_token=app_shutdown_token)
generate_code(ref_unit, args.template, args.output)
elif args.command == 'backup-config':#备份Dummy-Robot的配置信息
from ref_tool.configuration import backup_config
print("Waiting for ODrive...")
ref_unit = ref_tool.find_any(path=args.path, serial_number=args.serial_number,
search_cancellation_token=app_shutdown_token,
channel_termination_token=app_shutdown_token)
backup_config(ref_unit, args.file, logger)
elif args.command == 'restore-config':#恢复Dummy-Robot的配置信息
from ref_tool.configuration import restore_config
print("Waiting for ODrive...")
ref_unit = ref_tool.find_any(path=args.path, serial_number=args.serial_number,
search_cancellation_token=app_shutdown_token,
channel_termination_token=app_shutdown_token)
restore_config(ref_unit, args.file, logger)
else:
raise Exception("unknown command: " + args.command)
except OperationAbortedException:
logger.info("Operation aborted.")
finally:
app_shutdown_token.set()#停止运行应用程序