1#!/usr/bin/env python3.4
2#
3#   Copyright 2018 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the 'License');
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an 'AS IS' BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17import os
18import statistics
19import time
20import acts.controllers.iperf_server as ipf
21
22
23class IperfHelper(object):
24    """ Helps with iperf config and processing the results
25
26    This class can be used to process the results of multiple iperf servers
27    (for example, dual traffic scenarios). It also helps in setting the
28    correct arguments for when using the phone as an iperf server
29    """
30    IPERF_CLIENT_RESULT_FILE_LOC_PHONE = '/sdcard/Download/'
31
32    def __init__(self, config):
33        self.traffic_type = config['traffic_type']
34        self.traffic_direction = config['traffic_direction']
35        self.duration = config['duration']
36        self.port = config['port']
37        self.server_idx = config['server_idx']
38        self.use_client_output = False
39        if 'bandwidth' in config:
40            self.bandwidth = config['bandwidth']
41        else:
42            self.bandwidth = None
43        if 'start_meas_time' in config:
44            self.start_meas_time = config['start_meas_time']
45        else:
46            self.start_meas_time = 0
47
48        iperf_args = '-i 1 -t {} -p {} -J'.format(self.duration, self.port)
49
50        if self.traffic_type == "UDP":
51            iperf_args = iperf_args + ' -u'
52        if self.traffic_direction == "DL":
53            iperf_args = iperf_args + ' -R'
54            self.use_client_output = True
55        # Set bandwidth in Mbit/s
56        if self.bandwidth is not None:
57            iperf_args = iperf_args + ' -b {}M'.format(self.bandwidth)
58
59        # Set the TCP window size
60        self.window = config.get("window", None)
61        if self.window:
62            iperf_args += ' -w {}M'.format(self.window)
63
64        # Parse the client side data to a file saved on the phone
65        self.results_filename_phone = self.IPERF_CLIENT_RESULT_FILE_LOC_PHONE \
66                                      + 'iperf_client_port_{}_{}.log'.format( \
67                                      self.port, self.traffic_direction)
68        iperf_args = iperf_args + ' > %s' % self.results_filename_phone
69
70        self.iperf_args = iperf_args
71
72    def process_iperf_results(self, dut, log, iperf_servers, test_name):
73        """Gets the iperf results from the phone and computes the average rate
74
75        Returns:
76             throughput: the average throughput (Mbit/s).
77        """
78        # Stopping the server (as safety to get the result file)
79        iperf_servers[self.server_idx].stop()
80        time.sleep(1)
81
82        # Get IPERF results and add this to the plot title
83        RESULTS_DESTINATION = os.path.join(
84            iperf_servers[self.server_idx].log_path,
85            'iperf_client_output_{}.log'.format(test_name))
86
87        PULL_FILE = '{} {}'.format(self.results_filename_phone,
88                                   RESULTS_DESTINATION)
89        dut.adb.pull(PULL_FILE)
90
91        # Calculate the average throughput
92        if self.use_client_output:
93            iperf_file = RESULTS_DESTINATION
94        else:
95            iperf_file = iperf_servers[self.server_idx].log_files[-1]
96        try:
97            iperf_result = ipf.IPerfResult(iperf_file)
98
99            # Get instantaneous rates after measuring starts
100            samples = iperf_result.instantaneous_rates[self.start_meas_time:-1]
101
102            # Convert values to Mbit/s
103            samples = [rate * 8 * (1.024**2) for rate in samples]
104
105            # compute mean, var and max_dev
106            mean = statistics.mean(samples)
107            var = statistics.variance(samples)
108            max_dev = 0
109            for rate in samples:
110                if abs(rate - mean) > max_dev:
111                    max_dev = abs(rate - mean)
112
113            log.info('The average throughput is {}. Variance is {} and max '
114                     'deviation is {}.'.format(
115                         round(mean, 2), round(var, 2), round(max_dev, 2)))
116
117        except:
118            log.warning('Cannot get iperf result.')
119            mean = 0
120
121        return mean
122