blob: b9965cf69fdfc8d09502e15d02627a0fdb1751c4 [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright 2017 - 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.
import logging
import time
import xmlrpc.client
from subprocess import call
from acts import signals
MOBLY_CONTROLLER_CONFIG_NAME = "ChameleonDevice"
ACTS_CONTROLLER_REFERENCE_NAME = "chameleon_devices"
CHAMELEON_DEVICE_EMPTY_CONFIG_MSG = "Configuration is empty, abort!"
CHAMELEON_DEVICE_NOT_LIST_CONFIG_MSG = "Configuration should be a list, abort!"
audio_bus_endpoints = {
'CROS_HEADPHONE': 'Cros device headphone',
'CROS_EXTERNAL_MICROPHONE': 'Cros device external microphone',
'PERIPHERAL_MICROPHONE': 'Peripheral microphone',
'PERIPHERAL_SPEAKER': 'Peripheral speaker',
'FPGA_LINEOUT': 'Chameleon FPGA line-out',
'FPGA_LINEIN': 'Chameleon FPGA line-in',
'BLUETOOTH_OUTPUT': 'Bluetooth module output',
'BLUETOOTH_INPUT': 'Bluetooth module input'
}
class ChameleonDeviceError(signals.ControllerError):
pass
def create(configs):
if not configs:
raise ChameleonDeviceError(CHAMELEON_DEVICE_EMPTY_CONFIG_MSG)
elif not isinstance(configs, list):
raise ChameleonDeviceError(CHAMELEON_DEVICE_NOT_LIST_CONFIG_MSG)
elif isinstance(configs[0], str):
# Configs is a list of IP addresses
chameleons = get_instances(configs)
return chameleons
def destroy(chameleons):
for chameleon in chameleons:
del chameleon
def get_info(chameleons):
"""Get information on a list of ChameleonDevice objects.
Args:
ads: A list of ChameleonDevice objects.
Returns:
A list of dict, each representing info for ChameleonDevice objects.
"""
device_info = []
for chameleon in chameleons:
info = {"address": chameleon.address, "port": chameleon.port}
device_info.append(info)
return device_info
def get_instances(ips):
"""Create ChameleonDevice instances from a list of IPs.
Args:
ips: A list of Chameleon IPs.
Returns:
A list of ChameleonDevice objects.
"""
return [ChameleonDevice(ip) for ip in ips]
class ChameleonDevice:
"""Class representing a Chameleon device.
Each object of this class represents one Chameleon device in ACTS.
Attributes:
address: The full address to contact the Chameleon device at
client: The ServiceProxy of the XMLRPC client.
log: A logger object.
port: The TCP port number of the Chameleon device.
"""
def __init__(self, ip="", port=9992):
self.ip = ip
self.log = logging.getLogger()
self.port = port
self.address = "http://{}:{}".format(ip, self.port)
try:
self.client = xmlrpc.client.ServerProxy(
self.address, allow_none=True, verbose=False)
except ConnectionRefusedError as err:
self.log.exception(
"Failed to connect to Chameleon Device at: {}".format(
self.address))
self.client.Reset()
def pull_file(self, chameleon_location, destination):
"""Pulls a file from the Chameleon device. Usually the raw audio file.
Args:
chameleon_location: The path to the file on the Chameleon device
destination: The destination to where to pull it locally.
"""
# TODO: (tturney) implement
self.log.error("Definition not yet implemented")
def start_capturing_audio(self, port_id, has_file=True):
"""Starts capturing audio.
Args:
port_id: The ID of the audio input port.
has_file: True for saving audio data to file. False otherwise.
"""
self.client.StartCapturingAudio(port_id, has_file)
def stop_capturing_audio(self, port_id):
"""Stops capturing audio.
Args:
port_id: The ID of the audio input port.
Returns:
List contain the location of the recorded audio and a dictionary
of values relating to the raw audio including: file_type, channel,
sample_format, and rate.
"""
return self.client.StopCapturingAudio(port_id)
def audio_board_connect(self, bus_number, endpoint):
"""Connects an endpoint to an audio bus.
Args:
bus_number: 1 or 2 for audio bus 1 or bus 2.
endpoint: An endpoint defined in audio_bus_endpoints.
"""
self.client.AudioBoardConnect(bus_number, endpoint)
def audio_board_disconnect(self, bus_number, endpoint):
"""Connects an endpoint to an audio bus.
Args:
bus_number: 1 or 2 for audio bus 1 or bus 2.
endpoint: An endpoint defined in audio_bus_endpoints.
"""
self.client.AudioBoardDisconnect(bus_number, endpoint)
def audio_board_disable_bluetooth(self):
"""Disables Bluetooth module on audio board."""
self.client.AudioBoardDisableBluetooth()
def audio_board_clear_routes(self, bus_number):
"""Clears routes on an audio bus.
Args:
bus_number: 1 or 2 for audio bus 1 or bus 2.
"""
self.client.AudioBoardClearRoutes(bus_number)
def scp(self, source, destination):
"""Copies files from the Chameleon device to the host machine.
Args:
source: The file path on the Chameleon board.
dest: The file path on the host machine.
"""
cmd = "scp root@{}:/{} {}".format(self.ip, source, destination)
try:
call(cmd.split(" "))
except FileNotFoundError as err:
self.log.exception("File not found {}".format(source))