blob: 2f896718d83719bf52f6122567f36ddccf2eff8a [file] [log] [blame]
Moja Hsu7ef568c2016-12-27 15:58:58 +08001#!/usr/bin/env python2
En-Shuo Hsua6814512020-01-16 16:47:39 +08002# -*- coding: utf-8 -*-
Cheng-Yi Chiang3f9cb972015-11-23 17:43:59 +08003# 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 Chiang3f9cb972015-11-23 17:43:59 +08006"""A simple utility to connect to Chameleond in an interactive shell."""
7
En-Shuo Hsua6814512020-01-16 16:47:39 +08008from __future__ import print_function
9
En-Shuo Hsu8fca32b2019-12-09 17:47:45 +080010import atexit
Cheng-Yi Chiang3f9cb972015-11-23 17:43:59 +080011import argparse
12import code
En-Shuo Hsua6814512020-01-16 16:47:39 +080013import logging # pylint: disable=cros-logging-import
Cheng-Yi Chiangab747d82016-11-22 15:39:12 +080014import os
Cheng-Yi Chiang3f9cb972015-11-23 17:43:59 +080015import readline
16import rlcompleter
Cheng-Yi Chiangab747d82016-11-22 15:39:12 +080017import subprocess
Cheng-Yi Chiang852e13e2017-04-17 19:02:22 +080018import time
En-Shuo Hsua6814512020-01-16 16:47:39 +080019# TODO: import xmlrpclib form xmlrpc in Python 3
20# pylint: disable=W1648
Cheng-Yi Chiang3f9cb972015-11-23 17:43:59 +080021import xmlrpclib
22
Moja Hsu7ef568c2016-12-27 15:58:58 +080023from audio.audio_value_detector import AudioValueDetector
24
En-Shuo Hsua6814512020-01-16 16:47:39 +080025TUNNEL_NAME = 'to_chameleon_tunnel'
En-Shuo Hsu8fca32b2019-12-09 17:47:45 +080026
Cheng-Yi Chiang3f9cb972015-11-23 17:43:59 +080027
28def 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 Hsua6814512020-01-16 16:47:39 +080046 message = """
Cheng-Yi Chiang3f9cb972015-11-23 17:43:59 +080047 %s
En-Shuo Hsua6814512020-01-16 16:47:39 +080048 E.g.""" % '\n '.join(port_messages)
Moja Hsu28e1aab2017-03-30 15:24:35 +080049 if linein_port:
En-Shuo Hsua6814512020-01-16 16:47:39 +080050 message += """
Cheng-Yi Chiang3f9cb972015-11-23 17:43:59 +080051 p.StartCapturingAudio(%d) to capture from LineIn.
En-Shuo Hsua6814512020-01-16 16:47:39 +080052 p.StopCapturingAudio(%d) to stop capturing from LineIn.""" % (linein_port,
En-Shuo Hsu8fca32b2019-12-09 17:47:45 +080053 linein_port)
Moja Hsu28e1aab2017-03-30 15:24:35 +080054
55 if hdmi_port:
En-Shuo Hsua6814512020-01-16 16:47:39 +080056 message += """
Cheng-Yi Chiang3f9cb972015-11-23 17:43:59 +080057 p.Plug(%d) to plug HDMI.
En-Shuo Hsua6814512020-01-16 16:47:39 +080058 p.Unplug(%d) to unplug HDMI.""" % (hdmi_port, hdmi_port)
Moja Hsu28e1aab2017-03-30 15:24:35 +080059
60 logging.info(message)
Cheng-Yi Chiang3f9cb972015-11-23 17:43:59 +080061
62
En-Shuo Hsu8fca32b2019-12-09 17:47:45 +080063def DetectAudioValue0(channels=None,
64 margin=0.01,
65 continuous_samples=5,
66 duration=3600,
67 dump_samples=48000):
Moja Hsu7ef568c2016-12-27 15:58:58 +080068 """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 Hsu8fca32b2019-12-09 17:47:45 +080098def StartInteractiveShell(p): # pylint: disable=unused-argument
Cheng-Yi Chiang3f9cb972015-11-23 17:43:59 +080099 """Starts an interactive shell.
100
101 Args:
102 p: The xmlrpclib.ServerProxy to chameleond.
103 """
Moja Hsu7ef568c2016-12-27 15:58:58 +0800104 vars = globals() # pylint: disable=redefined-builtin
Cheng-Yi Chiang3f9cb972015-11-23 17:43:59 +0800105 vars.update(locals())
106 readline.set_completer(rlcompleter.Completer(vars).complete)
En-Shuo Hsua6814512020-01-16 16:47:39 +0800107 readline.parse_and_bind('tab: complete')
Cheng-Yi Chiang3f9cb972015-11-23 17:43:59 +0800108 shell = code.InteractiveConsole(vars)
109 shell.interact()
110
111
112def ParseArgs():
113 """Parses the arguments.
114
Moja Hsu7ef568c2016-12-27 15:58:58 +0800115 Returns:
116 the namespace containing parsed arguments.
Cheng-Yi Chiang3f9cb972015-11-23 17:43:59 +0800117 """
118 parser = argparse.ArgumentParser(
119 description='Connect to Chameleond and use interactive shell.',
120 formatter_class=argparse.ArgumentDefaultsHelpFormatter)
En-Shuo Hsu8fca32b2019-12-09 17:47:45 +0800121 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 Chiang3f9cb972015-11-23 17:43:59 +0800144 return parser.parse_args()
145
146
Cheng-Yi Chiangab747d82016-11-22 15:39:12 +0800147def 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 Chiangab747d82016-11-22 15:39:12 +0800164 """
165 basename = os.path.basename(remote_path)
166 # options is already in the namespace.
167 subprocess.check_call(
Moja Hsu7ef568c2016-12-27 15:58:58 +0800168 ['scp', 'root@%s:%s' % (options.host, remote_path), basename]) # pylint: disable=undefined-variable
En-Shuo Hsu8fca32b2019-12-09 17:47:45 +0800169 subprocess.check_call([
170 'sox', '-b', '32', '-r', '48000', '-c', '8', '-e', 'signed', basename,
171 '-c', '2', basename + '.wav'
172 ])
Cheng-Yi Chiangab747d82016-11-22 15:39:12 +0800173
174
En-Shuo Hsua6814512020-01-16 16:47:39 +0800175def ConnectCrosToLineIn():
176 """Connects a audio bus path from Cros headphone to Chameleon LineIn."""
En-Shuo Hsu8fca32b2019-12-09 17:47:45 +0800177 p.AudioBoardConnect(1, 'Cros device headphone') # pylint: disable=undefined-variable
178 p.AudioBoardConnect(1, 'Chameleon FPGA line-in') # pylint: disable=undefined-variable
Cheng-Yi Chiangab747d82016-11-22 15:39:12 +0800179
180
Cheng-Yi Chiang852e13e2017-04-17 19:02:22 +0800181def 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
187def 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 Hsu8fca32b2019-12-09 17:47:45 +0800195 logging.info('Testing %s button, press and hold for %f seconds', func,
196 time_sec)
En-Shuo Hsua6814512020-01-16 16:47:39 +0800197 p.motor_board.Touch(func) # pylint: disable=undefined-variable
Cheng-Yi Chiang852e13e2017-04-17 19:02:22 +0800198 time.sleep(time_sec)
En-Shuo Hsua6814512020-01-16 16:47:39 +0800199 p.motor_board.Release(func) # pylint: disable=undefined-variable
Cheng-Yi Chiang852e13e2017-04-17 19:02:22 +0800200
201
En-Shuo Hsu8fca32b2019-12-09 17:47:45 +0800202def 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 Chiang3f9cb972015-11-23 17:43:59 +0800213def 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 Hsu8fca32b2019-12-09 17:47:45 +0800220 if options.remote:
221 cleanup = BuildTunnel(options.local_port, options.port, options.host)
222 if cleanup is None:
En-Shuo Hsua6814512020-01-16 16:47:39 +0800223 logging.info('Failed to create tunnel')
En-Shuo Hsu8fca32b2019-12-09 17:47:45 +0800224 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 Chiang3f9cb972015-11-23 17:43:59 +0800230 proxy = xmlrpclib.ServerProxy(address)
En-Shuo Hsu8fca32b2019-12-09 17:47:45 +0800231 logging.info('Connected to %s with MAC address %s', address,
232 proxy.GetMacAddress())
Cheng-Yi Chiang3f9cb972015-11-23 17:43:59 +0800233 ShowMessages(proxy)
En-Shuo Hsu8fca32b2019-12-09 17:47:45 +0800234 StartInteractiveShell(proxy)
Cheng-Yi Chiang3f9cb972015-11-23 17:43:59 +0800235
236
237if __name__ == '__main__':
238 Main()