blob: 7e885d7840648741de629d290bc52f967a807686 [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
"""This file is used to send relay devices commands.
Usage:
See --help for more details.
relay_tool.py -c <acts_config> -tb <testbed_name> -rd <device_name> -cmd cmd
Runs the command cmd for the given device pulled from the ACTS config.
relay_tool.py -c <acts_config> -tb <testbed_name> -rd <device_name> -l
Lists all possible functions (as well as documentation) for the device.
Examples:
# For a Bluetooth Relay Device:
$ relay_tool.py -c ... -tb ... -rd ... -cmd get_mac_address
<MAC_ADDRESS>
relay_tool.py -c ... -tb ... -rd ... -cmd enter_pairing_mode
# No output. Waits for enter_pairing_mode to complete.
"""
import argparse
import json
import re
import sys
import inspect
from acts.controllers import relay_device_controller
def get_relay_device(config_path, testbed_name, device_name):
"""Returns the relay device specified by the arguments.
Args:
config_path: the path to the ACTS config.
testbed_name: the name of the testbed the device is a part of.
device_name: the name of the device within the testbed.
Returns:
The RelayDevice object.
"""
with open(config_path) as config_file:
config = json.load(config_file)
relay_config = config['testbed'][testbed_name]['RelayDevice']
relay_devices = relay_device_controller.create(relay_config)
try:
return next(device for device in relay_devices
if device.name == device_name)
except StopIteration:
# StopIteration is raised when no device is found.
all_device_names = [device.name for device in relay_devices]
print('Unable to find device with name "%s" in testbed "%s". Expected '
'any of %s.' % (device_name, testbed_name, all_device_names),
file=sys.stderr)
raise
def print_docstring(relay_device, func_name):
"""Prints the docstring of the specified function to stderr.
Note that the documentation will be printed as follows:
func_name:
Docstring information, indented with a minimum of 4 spaces.
Args:
relay_device: the RelayDevice to find a function on.
func_name: the function to pull the docstring from.
"""
func = getattr(relay_device, func_name)
signature = inspect.signature(func)
docstring = func.__doc__
if docstring is None:
docstring = ' No docstring available.'
else:
# Make the indentation uniform.
min_line_indentation = sys.maxsize
# Skip the first line, because docstrings begin with 'One liner.\n',
# instead of an indentation.
for line in docstring.split('\n')[1:]:
index = 0
for index, char in enumerate(line):
if char != ' ':
break
if index + 1 < min_line_indentation and index != 0:
min_line_indentation = index + 1
if min_line_indentation == sys.maxsize:
min_line_indentation = 0
min_indent = '\n' + ' ' * (min_line_indentation - 4)
docstring = ' ' * 4 + docstring.rstrip()
docstring = re.sub(min_indent, '\n', docstring,
flags=re.MULTILINE)
print('%s%s: \n%s\n' % (func_name, str(signature), docstring),
file=sys.stderr)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-c', '--config', type=str, required=True,
help='The path to the config file.')
parser.add_argument('-tb', '--testbed', type=str, required=True,
help='The testbed within the config file to use.')
parser.add_argument('-rd', '--relay_device', type=str, required=True,
help='The name of the relay device to use.')
group = parser.add_mutually_exclusive_group()
group.add_argument('-cmd', '--command', type=str, nargs='+',
help='The command to run on the relay device.')
group.add_argument('-l', '--list_commmands',
action='store_true',
help='lists all commands for the given device.')
args = parser.parse_args()
relay_device = get_relay_device(args.config, args.testbed,
args.relay_device)
func_names = [func_name
for func_name in dir(relay_device)
if (not func_name.startswith('_') and
not func_name.endswith('__') and
callable(getattr(relay_device, func_name)))]
if args.command:
if args.command[0] not in func_names:
print('Received command %s. Expected any of %s.' %
(repr(args.command[0]), repr(func_names)), file=sys.stderr)
else:
# getattr should not be able to fail here.
func = getattr(relay_device, args.command[0])
try:
ret = func(*args.command[1:])
if ret is not None:
print(ret)
except TypeError as e:
# The above call may raise TypeError if an incorrect number
# of args are passed.
if len(e.args) == 1 and func.__name__ in e.args[0]:
# If calling the function raised the TypeError, log this
# more informative message instead of the traceback.
print('Incorrect number of args passed to command "%s".' %
args.command[0], file=sys.stderr)
print_docstring(relay_device, args.command[0])
else:
raise
else: # args.list_commands is set
print('Note: These commands are specific to the device given. '
'Some of these commands may not work on other devices.\n',
file=sys.stderr)
for func_name in func_names:
print_docstring(relay_device, func_name)
exit(1)
if __name__ == '__main__':
main()