Home | History | Annotate | Download | only in cfm
      1 from autotest_lib.client.common_lib.cros.cfm import cras_input_node
      2 from autotest_lib.client.common_lib.cros.cfm import cras_output_node
      3 
      4 class CrasNodeCollector(object):
      5     """Utility class for obtaining node data from cras_test_client."""
      6 
      7     DEVICE_COLUMN_COUNT = 2
      8     NODE_COLUMN_COUNT = 8
      9     DEVICE_HEADERS = ['ID', 'Name']
     10     OUTPUT_NODE_HEADERS = ['Stable Id', 'ID', 'Vol', 'Plugged', 'L/R swapped',
     11                            'Time Hotword', 'Type', 'Name']
     12     INPUT_NODE_HEADERS = ['Stable Id', 'ID', 'Gain', 'Plugged', 'L/Rswapped',
     13                           'Time Hotword', 'Type', 'Name']
     14 
     15     def __init__(self, host):
     16         """
     17         Constructor
     18         @param host the device under test (CrOS).
     19         """
     20         self._host = host
     21 
     22     def _replace_multiple_whitespace_with_one(self, string):
     23         """
     24         Replace multiple sequential whitespaces with a single whitespace.
     25         @returns a string
     26         """
     27         return ' '.join(string.split())
     28 
     29     def _construct_columns(self, columns_str, columns_count):
     30         """
     31         Constructs a list of strings from a single string.
     32 
     33         @param columns_str A whitespace separated list of values.
     34         @param columns_count number of columns to create
     35         @returns a list with strings.
     36         """
     37         # 1) Replace multiple whitespaces with one
     38         # 2) Split on whitespace, create nof_columns columns
     39         columns_str = self._replace_multiple_whitespace_with_one(columns_str)
     40         return columns_str.split(None, columns_count-1)
     41 
     42     def _collect_input_device_data(self):
     43         cmd = ("cras_test_client --dump_server_info "
     44               " | awk '/Input Devices:/,/Input Nodes:/'")
     45         lines = self._host.run_output(cmd).split('\n')
     46         # Ignore the first two lines ("Input Devices" and headers) and the
     47         # last line ("Input Nodes")
     48         lines = lines[2:-1]
     49         rows = [self._construct_columns(line, self.DEVICE_COLUMN_COUNT)
     50                 for line in lines]
     51         return [dict(zip(self.DEVICE_HEADERS, row)) for row in rows]
     52 
     53     def _collect_output_device_data(self):
     54         cmd = ("cras_test_client --dump_server_info "
     55               " | awk '/Output Devices:/,/Output Nodes:/'")
     56         lines = self._host.run_output(cmd).split('\n')
     57         # Ignore the first two lines ("Output Devices" and headers) and the
     58         # last line ("Output Nodes")
     59         lines = lines[2:-1]
     60         rows = [self._construct_columns(line, self.DEVICE_COLUMN_COUNT)
     61                 for line in lines]
     62         return [dict(zip(self.DEVICE_HEADERS, row)) for row in rows]
     63 
     64     def _collect_output_node_cras_data(self):
     65         """
     66         Collects output nodes data using cras_test_client.
     67 
     68         @returns a list of dictionaries where keys are in OUTPUT_NODE_HEADERS
     69         """
     70         # It's a bit hacky to use awk; we should probably do the parsing
     71         # in Python instead using textfsm or some other lib.
     72         cmd = ("cras_test_client --dump_server_info"
     73                "| awk '/Output Nodes:/,/Input Devices:/'")
     74         lines = self._host.run_output(cmd).split('\n')
     75         # Ignore the first two lines ("Output Nodes:" and headers) and the
     76         # last line ("Input Devices:")
     77         lines = lines[2:-1]
     78         rows = [self._construct_columns(line, self.NODE_COLUMN_COUNT)
     79                 for line in lines]
     80         return [dict(zip(self.OUTPUT_NODE_HEADERS, row)) for row in rows]
     81 
     82     def _collect_input_node_cras_data(self):
     83         """
     84         Collects input nodes data using cras_test_client.
     85 
     86         @returns a list of dictionaries where keys are in INPUT_NODE_HEADERS
     87         """
     88         cmd = ("cras_test_client --dump_server_info "
     89               " | awk '/Input Nodes:/,/Attached clients:/'")
     90         lines = self._host.run_output(cmd).split('\n')
     91         # Ignore the first two lines ("Input Nodes:" and headers) and the
     92         # last line ("Attached clients:")
     93         lines = lines[2:-1]
     94         rows = [self._construct_columns(line, self.NODE_COLUMN_COUNT)
     95                 for line in lines]
     96         return [dict(zip(self.INPUT_NODE_HEADERS, row)) for row in rows]
     97 
     98     def _get_device_name(self, device_data, device_id):
     99         for device in device_data:
    100             if device['ID'] == device_id:
    101                 return device['Name']
    102         return None
    103 
    104     def _create_input_node(self, node_data, device_data):
    105         """
    106         Create a CrasInputNode.
    107         @param node_data Input Node data
    108         @param device_data Input Device data
    109         @return CrasInputNode
    110         """
    111         device_id = node_data['ID'].split(':')[0]
    112         device_name = self._get_device_name(device_data, device_id)
    113         return cras_input_node.CrasInputNode(
    114             node_id=node_data['ID'],
    115             node_name=node_data['Name'],
    116             gain=node_data['Gain'],
    117             node_type=node_data['Type'],
    118             device_id=device_id,
    119             device_name=device_name)
    120 
    121     def _create_output_node(self, node_data, device_data):
    122         """
    123         Create a CrasOutputNode.
    124         @param node_data Output Node data
    125         @param device_data Output Device data
    126         @return CrasOutputNode
    127         """
    128         device_id = node_data['ID'].split(':')[0]
    129         device_name = self._get_device_name(device_data, device_id)
    130         return cras_output_node.CrasOutputNode(
    131             node_id=node_data['ID'],
    132             node_type=node_data['Type'],
    133             node_name=node_data['Name'],
    134             volume=node_data['Vol'],
    135             device_id=device_id,
    136             device_name=device_name)
    137 
    138     def get_input_nodes(self):
    139         device_data = self._collect_input_device_data()
    140         node_data = self._collect_input_node_cras_data()
    141         return [self._create_input_node(d, device_data) for d in node_data]
    142 
    143     def get_output_nodes(self):
    144         device_data = self._collect_output_device_data()
    145         node_data = self._collect_output_node_cras_data()
    146         return [self._create_output_node(d, device_data) for d in node_data]
    147