xref: /aosp_15_r20/external/autotest/client/common_lib/powerplay_util.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Lint as: python2, python3
2*9c5db199SXin Li# Copyright 2015 The Chromium OS Authors. All rights reserved.
3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
4*9c5db199SXin Li# found in the LICENSE file.
5*9c5db199SXin Li
6*9c5db199SXin Liimport error, logging, os, serial, shutil, threading, time
7*9c5db199SXin Li
8*9c5db199SXin Li_power_play_data_file = '/tmp/power_play_data'
9*9c5db199SXin Li
10*9c5db199SXin Liclass PowerPlay(object):
11*9c5db199SXin Li    """Class to record serial over USB data from Power Play (go/powerplay).
12*9c5db199SXin Li
13*9c5db199SXin Li    It detects if powerplay is connected to the DUT over USB and opens the
14*9c5db199SXin Li    serial port to start receiving powerplay data. It also opens a text file to
15*9c5db199SXin Li    save this data after some formatting.
16*9c5db199SXin Li    """
17*9c5db199SXin Li
18*9c5db199SXin Li    version = 1
19*9c5db199SXin Li
20*9c5db199SXin Li    def __init__(self, test_obj, record_interval=0):
21*9c5db199SXin Li        """Initialize PowerPlay.
22*9c5db199SXin Li
23*9c5db199SXin Li        @param test_obj: test object.
24*9c5db199SXin Li        @param record_interval: Power play data recording interval in seconds.
25*9c5db199SXin Li        """
26*9c5db199SXin Li        self.test = test_obj
27*9c5db199SXin Li        self.ser = None
28*9c5db199SXin Li        self.recording_interval = record_interval
29*9c5db199SXin Li        self.momentary_curr_list = list()
30*9c5db199SXin Li        self.record_thread = None
31*9c5db199SXin Li
32*9c5db199SXin Li    def extract_current(self, pp_data):
33*9c5db199SXin Li        """Extract momentary current value from each line of powerplay data.
34*9c5db199SXin Li
35*9c5db199SXin Li        @param pp_data: Single line of powerplay data with eight comma separated
36*9c5db199SXin Li                        values.
37*9c5db199SXin Li        @return list containing momentary current values.
38*9c5db199SXin Li        """
39*9c5db199SXin Li        if pp_data[0].isdigit():
40*9c5db199SXin Li            self.momentary_curr_list.append(float(pp_data[pp_data.index(',')+1:]
41*9c5db199SXin Li                    [:pp_data[pp_data.index(',')+1:].index(',')]))
42*9c5db199SXin Li        return self.momentary_curr_list
43*9c5db199SXin Li
44*9c5db199SXin Li    def start_recording_power_play_data(self):
45*9c5db199SXin Li        """Starts a new thread to record power play data."""
46*9c5db199SXin Li        self.record_thread = threading.Thread(target=self.start_record_thread)
47*9c5db199SXin Li        self.record_thread.daemon = True
48*9c5db199SXin Li        self.record_thread.start()
49*9c5db199SXin Li
50*9c5db199SXin Li    def start_record_thread(self):
51*9c5db199SXin Li        """Start recording power play data.
52*9c5db199SXin Li
53*9c5db199SXin Li        Get a list of connected USB devices and try to establish a serial
54*9c5db199SXin Li        connection. Once the connection is established, open a text file and
55*9c5db199SXin Li        start reading serial data and write it to the text file after some
56*9c5db199SXin Li        formatting.
57*9c5db199SXin Li        """
58*9c5db199SXin Li        devices = [x for x in os.listdir('/dev/') if x.startswith('ttyUSB')]
59*9c5db199SXin Li
60*9c5db199SXin Li        for device in devices:
61*9c5db199SXin Li            device_link = '/dev/' + device
62*9c5db199SXin Li            try:
63*9c5db199SXin Li                if self.ser == None:
64*9c5db199SXin Li                    logging.info('Trying ... %s', device_link)
65*9c5db199SXin Li                    self.ser = serial.Serial(device_link, 115200)
66*9c5db199SXin Li                    logging.info('Successfully connected to %s', device_link)
67*9c5db199SXin Li                    break
68*9c5db199SXin Li            except serial.SerialException as e:
69*9c5db199SXin Li                raise error.TestError('Failed to connect to %s becuase of %s' %
70*9c5db199SXin Li                                     (device_link, str(e)))
71*9c5db199SXin Li
72*9c5db199SXin Li        self.text_file = open(_power_play_data_file, 'w')
73*9c5db199SXin Li
74*9c5db199SXin Li        if self.ser != None:
75*9c5db199SXin Li            title_row = ('time,powerplay_timestamp,momentary_current (A),' +
76*9c5db199SXin Li                    'momentary_charge (AH),average_current (A),' +
77*9c5db199SXin Li                    'total_standby_time,total_wake_time,num_wakes,is_awake?\n')
78*9c5db199SXin Li            self.text_file.write(title_row)
79*9c5db199SXin Li            start_time = time.time()
80*9c5db199SXin Li            while self.ser.readline():
81*9c5db199SXin Li                current_timestamp = (('{:>10.3f}'.
82*9c5db199SXin Li                        format(time.time() - start_time)).replace(' ', ''))
83*9c5db199SXin Li                pp_data = (self.ser.readline().replace('\00', '').
84*9c5db199SXin Li                        replace(' ', ',').replace('\r', ''))
85*9c5db199SXin Li                if (not pp_data.startswith('#') and (len(pp_data) > 30) and
86*9c5db199SXin Li                        not self.text_file.closed):
87*9c5db199SXin Li                    self.text_file.write(current_timestamp + ',' + pp_data)
88*9c5db199SXin Li                    self.momentary_curr_list = self.extract_current(pp_data)
89*9c5db199SXin Li                time.sleep(self.recording_interval)
90*9c5db199SXin Li                self.ser.flushInput()
91*9c5db199SXin Li        else:
92*9c5db199SXin Li            self.text_file.write('No data from powerplay. Check connection.')
93*9c5db199SXin Li
94*9c5db199SXin Li    def stop_recording_power_play_data(self):
95*9c5db199SXin Li        """Stop recording power play data.
96*9c5db199SXin Li
97*9c5db199SXin Li        Close the text file and copy it to the test log results directory. Also
98*9c5db199SXin Li        report current data to the performance dashboard.
99*9c5db199SXin Li        """
100*9c5db199SXin Li        if not self.text_file.closed:
101*9c5db199SXin Li            self.text_file.close()
102*9c5db199SXin Li        shutil.copy(_power_play_data_file, self.test.resultsdir)
103*9c5db199SXin Li        self.test.output_perf_value(description='momentary_current_draw',
104*9c5db199SXin Li                               value=self.momentary_curr_list,
105*9c5db199SXin Li                               units='Amps', higher_is_better=False)
106