Moja Hsu | 7ef568c | 2016-12-27 15:58:58 +0800 | [diff] [blame] | 1 | #!/usr/bin/env python2 |
En-Shuo Hsu | a681451 | 2020-01-16 16:47:39 +0800 | [diff] [blame] | 2 | # -*- coding: utf-8 -*- |
Cheng-Yi Chiang | 3f9cb97 | 2015-11-23 17:43:59 +0800 | [diff] [blame] | 3 | # Copyright 2015 The Chromium OS Authors. All rights reserved. |
| 4 | # Use of this source code is governed by a BSD-style license that can be |
| 5 | # found in the LICENSE file. |
Cheng-Yi Chiang | 3f9cb97 | 2015-11-23 17:43:59 +0800 | [diff] [blame] | 6 | """A simple utility to connect to Chameleond in an interactive shell.""" |
| 7 | |
En-Shuo Hsu | a681451 | 2020-01-16 16:47:39 +0800 | [diff] [blame] | 8 | from __future__ import print_function |
| 9 | |
En-Shuo Hsu | 8fca32b | 2019-12-09 17:47:45 +0800 | [diff] [blame] | 10 | import atexit |
Cheng-Yi Chiang | 3f9cb97 | 2015-11-23 17:43:59 +0800 | [diff] [blame] | 11 | import argparse |
| 12 | import code |
En-Shuo Hsu | a681451 | 2020-01-16 16:47:39 +0800 | [diff] [blame] | 13 | import logging # pylint: disable=cros-logging-import |
Cheng-Yi Chiang | ab747d8 | 2016-11-22 15:39:12 +0800 | [diff] [blame] | 14 | import os |
Cheng-Yi Chiang | 3f9cb97 | 2015-11-23 17:43:59 +0800 | [diff] [blame] | 15 | import readline |
| 16 | import rlcompleter |
Cheng-Yi Chiang | ab747d8 | 2016-11-22 15:39:12 +0800 | [diff] [blame] | 17 | import subprocess |
Cheng-Yi Chiang | 852e13e | 2017-04-17 19:02:22 +0800 | [diff] [blame] | 18 | import time |
En-Shuo Hsu | a681451 | 2020-01-16 16:47:39 +0800 | [diff] [blame] | 19 | # TODO: import xmlrpclib form xmlrpc in Python 3 |
| 20 | # pylint: disable=W1648 |
Cheng-Yi Chiang | 3f9cb97 | 2015-11-23 17:43:59 +0800 | [diff] [blame] | 21 | import xmlrpclib |
| 22 | |
Moja Hsu | 7ef568c | 2016-12-27 15:58:58 +0800 | [diff] [blame] | 23 | from audio.audio_value_detector import AudioValueDetector |
| 24 | |
En-Shuo Hsu | a681451 | 2020-01-16 16:47:39 +0800 | [diff] [blame] | 25 | TUNNEL_NAME = 'to_chameleon_tunnel' |
En-Shuo Hsu | 8fca32b | 2019-12-09 17:47:45 +0800 | [diff] [blame] | 26 | |
Cheng-Yi Chiang | 3f9cb97 | 2015-11-23 17:43:59 +0800 | [diff] [blame] | 27 | |
| 28 | def ShowMessages(proxy): |
| 29 | """Shows the messages for usage. |
| 30 | |
| 31 | Args: |
| 32 | proxy: The xmlrpclib.ServerProxy to chameleond. |
| 33 | """ |
| 34 | logging.info('In interactive shell, p is the proxy to chameleond server') |
| 35 | supported_ports = proxy.GetSupportedPorts() |
| 36 | linein_port = None |
| 37 | hdmi_port = None |
| 38 | port_messages = [] |
| 39 | for port in supported_ports: |
| 40 | port_type = proxy.GetConnectorType(port) |
| 41 | if port_type == 'LineIn': |
| 42 | linein_port = port |
| 43 | if port_type == 'HDMI': |
| 44 | hdmi_port = port |
| 45 | port_messages.append('Port %d is %s.' % (port, port_type)) |
En-Shuo Hsu | a681451 | 2020-01-16 16:47:39 +0800 | [diff] [blame] | 46 | message = """ |
Cheng-Yi Chiang | 3f9cb97 | 2015-11-23 17:43:59 +0800 | [diff] [blame] | 47 | %s |
En-Shuo Hsu | a681451 | 2020-01-16 16:47:39 +0800 | [diff] [blame] | 48 | E.g.""" % '\n '.join(port_messages) |
Moja Hsu | 28e1aab | 2017-03-30 15:24:35 +0800 | [diff] [blame] | 49 | if linein_port: |
En-Shuo Hsu | a681451 | 2020-01-16 16:47:39 +0800 | [diff] [blame] | 50 | message += """ |
Cheng-Yi Chiang | 3f9cb97 | 2015-11-23 17:43:59 +0800 | [diff] [blame] | 51 | p.StartCapturingAudio(%d) to capture from LineIn. |
En-Shuo Hsu | a681451 | 2020-01-16 16:47:39 +0800 | [diff] [blame] | 52 | p.StopCapturingAudio(%d) to stop capturing from LineIn.""" % (linein_port, |
En-Shuo Hsu | 8fca32b | 2019-12-09 17:47:45 +0800 | [diff] [blame] | 53 | linein_port) |
Moja Hsu | 28e1aab | 2017-03-30 15:24:35 +0800 | [diff] [blame] | 54 | |
| 55 | if hdmi_port: |
En-Shuo Hsu | a681451 | 2020-01-16 16:47:39 +0800 | [diff] [blame] | 56 | message += """ |
Cheng-Yi Chiang | 3f9cb97 | 2015-11-23 17:43:59 +0800 | [diff] [blame] | 57 | p.Plug(%d) to plug HDMI. |
En-Shuo Hsu | a681451 | 2020-01-16 16:47:39 +0800 | [diff] [blame] | 58 | p.Unplug(%d) to unplug HDMI.""" % (hdmi_port, hdmi_port) |
Moja Hsu | 28e1aab | 2017-03-30 15:24:35 +0800 | [diff] [blame] | 59 | |
| 60 | logging.info(message) |
Cheng-Yi Chiang | 3f9cb97 | 2015-11-23 17:43:59 +0800 | [diff] [blame] | 61 | |
| 62 | |
En-Shuo Hsu | 8fca32b | 2019-12-09 17:47:45 +0800 | [diff] [blame] | 63 | def DetectAudioValue0(channels=None, |
| 64 | margin=0.01, |
| 65 | continuous_samples=5, |
| 66 | duration=3600, |
| 67 | dump_samples=48000): |
Moja Hsu | 7ef568c | 2016-12-27 15:58:58 +0800 | [diff] [blame] | 68 | """Detects if Chameleon captures continuous audio data close to 0. |
| 69 | |
| 70 | This function will get the audio streaming data from stream server and will |
| 71 | check if the audio data is close to 0 by the margin parameter. |
| 72 | -margin < value < margin will be considered to be close to 0. |
| 73 | If there are continuous audio samples close to 0 in the streamed data, |
| 74 | test_server will log it and save the audio data to a wav file. |
| 75 | |
| 76 | E.g. |
| 77 | >>> ConnectCrosToLineIn() |
| 78 | >>> p.StartCapturingAudio(6, False) |
| 79 | >>> DetectAudioValue0(duration=24*3600, margin=0.001) |
| 80 | |
| 81 | Args: |
| 82 | channels: Array of audio channels we want to check. |
| 83 | E.g. [0, 1] means we only care about channel 0 and channel 1. |
| 84 | margin: Used to decide if the value is closed to 0. Maximum value is 1. |
| 85 | continuous_samples: When continuous_samples samples are closed to 0, trigger |
| 86 | event. |
| 87 | duration: The duration of monitoring in seconds. |
| 88 | dump_samples: When event happens, how many audio samples we want to |
| 89 | save to file. |
| 90 | """ |
| 91 | if not channels: |
| 92 | channels = [0, 1] |
| 93 | detecter = AudioValueDetector(options.host) # pylint: disable=undefined-variable |
| 94 | detecter.Detect(channels, margin, continuous_samples, duration, dump_samples) |
| 95 | return True |
| 96 | |
| 97 | |
En-Shuo Hsu | 8fca32b | 2019-12-09 17:47:45 +0800 | [diff] [blame] | 98 | def StartInteractiveShell(p): # pylint: disable=unused-argument |
Cheng-Yi Chiang | 3f9cb97 | 2015-11-23 17:43:59 +0800 | [diff] [blame] | 99 | """Starts an interactive shell. |
| 100 | |
| 101 | Args: |
| 102 | p: The xmlrpclib.ServerProxy to chameleond. |
| 103 | """ |
Moja Hsu | 7ef568c | 2016-12-27 15:58:58 +0800 | [diff] [blame] | 104 | vars = globals() # pylint: disable=redefined-builtin |
Cheng-Yi Chiang | 3f9cb97 | 2015-11-23 17:43:59 +0800 | [diff] [blame] | 105 | vars.update(locals()) |
| 106 | readline.set_completer(rlcompleter.Completer(vars).complete) |
En-Shuo Hsu | a681451 | 2020-01-16 16:47:39 +0800 | [diff] [blame] | 107 | readline.parse_and_bind('tab: complete') |
Cheng-Yi Chiang | 3f9cb97 | 2015-11-23 17:43:59 +0800 | [diff] [blame] | 108 | shell = code.InteractiveConsole(vars) |
| 109 | shell.interact() |
| 110 | |
| 111 | |
| 112 | def ParseArgs(): |
| 113 | """Parses the arguments. |
| 114 | |
Moja Hsu | 7ef568c | 2016-12-27 15:58:58 +0800 | [diff] [blame] | 115 | Returns: |
| 116 | the namespace containing parsed arguments. |
Cheng-Yi Chiang | 3f9cb97 | 2015-11-23 17:43:59 +0800 | [diff] [blame] | 117 | """ |
| 118 | parser = argparse.ArgumentParser( |
| 119 | description='Connect to Chameleond and use interactive shell.', |
| 120 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) |
En-Shuo Hsu | 8fca32b | 2019-12-09 17:47:45 +0800 | [diff] [blame] | 121 | parser.add_argument( |
| 122 | '--chameleon_host', |
| 123 | type=str, |
| 124 | dest='host', |
| 125 | required=True, |
| 126 | help='host address of Chameleond') |
| 127 | parser.add_argument( |
| 128 | '--port', |
| 129 | type=int, |
| 130 | dest='port', |
| 131 | default=9992, |
| 132 | help='port number of Chameleond') |
| 133 | parser.add_argument( |
| 134 | '--remote', |
| 135 | action='store_true', |
| 136 | help='Connect remotely. ' |
| 137 | 'Adding the flag will establish ssh tunnel automatically for you.') |
| 138 | parser.add_argument( |
| 139 | '--local_port', |
| 140 | type=int, |
| 141 | default=12346, |
| 142 | help='port number of localhost. This will only be used ' |
| 143 | 'if you enable --remote.') |
Cheng-Yi Chiang | 3f9cb97 | 2015-11-23 17:43:59 +0800 | [diff] [blame] | 144 | return parser.parse_args() |
| 145 | |
| 146 | |
Cheng-Yi Chiang | ab747d8 | 2016-11-22 15:39:12 +0800 | [diff] [blame] | 147 | def GetAndConvertRecordedFile(remote_path): |
| 148 | """Gets recorded file and converts it into a wav file. |
| 149 | |
| 150 | A helper function to get recorded file from Chameleon host and do |
| 151 | file format conversion from 32 bit, 48000 rate, 8 channel raw file |
| 152 | to 2 channel wav file. |
| 153 | |
| 154 | E.g. |
| 155 | >>> p.StartCapturingAudio(6) |
| 156 | >>> s = p.StopCapturingAudio(6) |
| 157 | >>> GetAndConvertRecordedFile(s[0]) |
| 158 | |
| 159 | The recorded raw file and converted wav file will be in current |
| 160 | directory. |
| 161 | |
| 162 | Args: |
| 163 | remote_path: The file to copy from Chameleon host. |
Cheng-Yi Chiang | ab747d8 | 2016-11-22 15:39:12 +0800 | [diff] [blame] | 164 | """ |
| 165 | basename = os.path.basename(remote_path) |
| 166 | # options is already in the namespace. |
| 167 | subprocess.check_call( |
Moja Hsu | 7ef568c | 2016-12-27 15:58:58 +0800 | [diff] [blame] | 168 | ['scp', 'root@%s:%s' % (options.host, remote_path), basename]) # pylint: disable=undefined-variable |
En-Shuo Hsu | 8fca32b | 2019-12-09 17:47:45 +0800 | [diff] [blame] | 169 | subprocess.check_call([ |
| 170 | 'sox', '-b', '32', '-r', '48000', '-c', '8', '-e', 'signed', basename, |
| 171 | '-c', '2', basename + '.wav' |
| 172 | ]) |
Cheng-Yi Chiang | ab747d8 | 2016-11-22 15:39:12 +0800 | [diff] [blame] | 173 | |
| 174 | |
En-Shuo Hsu | a681451 | 2020-01-16 16:47:39 +0800 | [diff] [blame] | 175 | def ConnectCrosToLineIn(): |
| 176 | """Connects a audio bus path from Cros headphone to Chameleon LineIn.""" |
En-Shuo Hsu | 8fca32b | 2019-12-09 17:47:45 +0800 | [diff] [blame] | 177 | p.AudioBoardConnect(1, 'Cros device headphone') # pylint: disable=undefined-variable |
| 178 | p.AudioBoardConnect(1, 'Chameleon FPGA line-in') # pylint: disable=undefined-variable |
Cheng-Yi Chiang | ab747d8 | 2016-11-22 15:39:12 +0800 | [diff] [blame] | 179 | |
| 180 | |
Cheng-Yi Chiang | 852e13e | 2017-04-17 19:02:22 +0800 | [diff] [blame] | 181 | def TestMotors(): |
| 182 | """Test motors by touching and releasing each button once.""" |
| 183 | for func in ['Call', 'Hang Up', 'Mute', 'Vol Up', 'Vol Down']: |
| 184 | PressOneFunc(func) |
| 185 | |
| 186 | |
| 187 | def PressOneFunc(func, time_sec=0): |
| 188 | """Test motors by touching and releasing one button. |
| 189 | |
| 190 | Args: |
| 191 | func: The motor function. One of 'Call', 'Hang Up', 'Mute', 'Vol Up', |
| 192 | 'Vol Down'. |
| 193 | time_sec: Hold time in seconds after touch and before release. |
| 194 | """ |
En-Shuo Hsu | 8fca32b | 2019-12-09 17:47:45 +0800 | [diff] [blame] | 195 | logging.info('Testing %s button, press and hold for %f seconds', func, |
| 196 | time_sec) |
En-Shuo Hsu | a681451 | 2020-01-16 16:47:39 +0800 | [diff] [blame] | 197 | p.motor_board.Touch(func) # pylint: disable=undefined-variable |
Cheng-Yi Chiang | 852e13e | 2017-04-17 19:02:22 +0800 | [diff] [blame] | 198 | time.sleep(time_sec) |
En-Shuo Hsu | a681451 | 2020-01-16 16:47:39 +0800 | [diff] [blame] | 199 | p.motor_board.Release(func) # pylint: disable=undefined-variable |
Cheng-Yi Chiang | 852e13e | 2017-04-17 19:02:22 +0800 | [diff] [blame] | 200 | |
| 201 | |
En-Shuo Hsu | 8fca32b | 2019-12-09 17:47:45 +0800 | [diff] [blame] | 202 | def BuildTunnel(local_port, port, host): |
| 203 | |
| 204 | def cleanup(): |
| 205 | cleanup_cmd = 'ssh -S %s -O exit root@%s' % (TUNNEL_NAME, host) |
| 206 | subprocess.call(cleanup_cmd, shell=True) |
| 207 | |
| 208 | cmd = ('ssh -M -S %s -fnNT -o "StrictHostKeyChecking no" ' |
| 209 | '-L %d:localhost:%d root@%s') % (TUNNEL_NAME, local_port, port, host) |
| 210 | return None if subprocess.call(cmd, shell=True) else cleanup |
| 211 | |
| 212 | |
Cheng-Yi Chiang | 3f9cb97 | 2015-11-23 17:43:59 +0800 | [diff] [blame] | 213 | def Main(): |
| 214 | """The Main program.""" |
| 215 | logging.basicConfig( |
| 216 | format='%(asctime)s:%(levelname)s:%(message)s', level=logging.DEBUG) |
| 217 | |
| 218 | options = ParseArgs() |
| 219 | |
En-Shuo Hsu | 8fca32b | 2019-12-09 17:47:45 +0800 | [diff] [blame] | 220 | if options.remote: |
| 221 | cleanup = BuildTunnel(options.local_port, options.port, options.host) |
| 222 | if cleanup is None: |
En-Shuo Hsu | a681451 | 2020-01-16 16:47:39 +0800 | [diff] [blame] | 223 | logging.info('Failed to create tunnel') |
En-Shuo Hsu | 8fca32b | 2019-12-09 17:47:45 +0800 | [diff] [blame] | 224 | return |
| 225 | atexit.register(cleanup) |
| 226 | address = 'http://localhost:%d' % options.local_port |
| 227 | else: |
| 228 | address = 'http://%s:%s' % (options.host, options.port) |
| 229 | |
Cheng-Yi Chiang | 3f9cb97 | 2015-11-23 17:43:59 +0800 | [diff] [blame] | 230 | proxy = xmlrpclib.ServerProxy(address) |
En-Shuo Hsu | 8fca32b | 2019-12-09 17:47:45 +0800 | [diff] [blame] | 231 | logging.info('Connected to %s with MAC address %s', address, |
| 232 | proxy.GetMacAddress()) |
Cheng-Yi Chiang | 3f9cb97 | 2015-11-23 17:43:59 +0800 | [diff] [blame] | 233 | ShowMessages(proxy) |
En-Shuo Hsu | 8fca32b | 2019-12-09 17:47:45 +0800 | [diff] [blame] | 234 | StartInteractiveShell(proxy) |
Cheng-Yi Chiang | 3f9cb97 | 2015-11-23 17:43:59 +0800 | [diff] [blame] | 235 | |
| 236 | |
| 237 | if __name__ == '__main__': |
| 238 | Main() |