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