1c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici#    Copyright 2013-2015 ARM Limited
2c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici#
3c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici# Licensed under the Apache License, Version 2.0 (the "License");
4c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici# you may not use this file except in compliance with the License.
5c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici# You may obtain a copy of the License at
6c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici#
7c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici#     http://www.apache.org/licenses/LICENSE-2.0
8c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici#
9c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici# Unless required by applicable law or agreed to in writing, software
10c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici# distributed under the License is distributed on an "AS IS" BASIS,
11c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici# See the License for the specific language governing permissions and
13c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici# limitations under the License.
14c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici#
15c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnicifrom __future__ import division
16c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnicifrom collections import defaultdict
17c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici
18dd26b43ac5237d967dca702deb5274a566a8e885Sergei Trofimovfrom devlib import DerivedMeasurements, DerivedMetric
19dd26b43ac5237d967dca702deb5274a566a8e885Sergei Trofimovfrom devlib.instrument import  MEASUREMENT_TYPES, InstrumentChannel
20c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici
21c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici
22c093d567542460e398662b76e0577fbb49ae89f7Marc Bonniciclass DerivedEnergyMeasurements(DerivedMeasurements):
23c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici
24c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici    @staticmethod
25c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici    def process(measurements_csv):
26c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici
27c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici        should_calculate_energy = []
28c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici        use_timestamp = False
29c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici
30c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici        # Determine sites to calculate energy for
31c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici        channel_map = defaultdict(list)
32c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici        for channel in measurements_csv.channels:
33c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici            channel_map[channel].append(channel.kind)
34c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici        for channel, kinds in channel_map.iteritems():
35c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici            if 'power' in kinds and not 'energy' in kinds:
36c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                should_calculate_energy.append(channel.site)
37c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici            if channel.site == 'timestamp':
38c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                use_timestamp = True
39c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                time_measurment = channel.measurement_type
40c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici
41c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici        if measurements_csv.sample_rate_hz is None and not use_timestamp:
42c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici            msg = 'Timestamp data is unavailable, please provide a sample rate'
43c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici            raise ValueError(msg)
44c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici
45c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici        if use_timestamp:
46c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici            # Find index of timestamp column
47c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici            ts_index = [i for i, chan in enumerate(measurements_csv.channels)
48c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                        if chan.site == 'timestamp']
49c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici            if len(ts_index) > 1:
50c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                raise ValueError('Multiple timestamps detected')
51c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici            ts_index = ts_index[0]
52c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici
53c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici        row_ts = 0
54c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici        last_ts = 0
55c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici        energy_results = defaultdict(dict)
56c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici        power_results = defaultdict(float)
57c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici
58c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici        # Process data
59dfd0b8ebd9a5f57aec6480ca6bd1d4d25763aedcSergei Trofimov        for count, row in enumerate(measurements_csv.iter_measurements()):
60c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici            if use_timestamp:
61c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                last_ts = row_ts
62c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                row_ts = time_measurment.convert(float(row[ts_index].value), 'time')
63c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici            for entry in row:
64c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                channel = entry.channel
65c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                site = channel.site
66c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                if channel.kind == 'energy':
67c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                    if count == 0:
68c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                        energy_results[site]['start'] = entry.value
69c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                    else:
70c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                        energy_results[site]['end'] = entry.value
71c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici
72c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                if channel.kind == 'power':
73c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                    power_results[site] += entry.value
74c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici
75c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                    if site in should_calculate_energy:
76c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                        if count == 0:
77c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                            energy_results[site]['start'] = 0
78c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                            energy_results[site]['end'] = 0
79c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                        elif use_timestamp:
80c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                            energy_results[site]['end'] += entry.value * (row_ts - last_ts)
81c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                        else:
82c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                            energy_results[site]['end'] += entry.value * (1 /
83c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici                                                           measurements_csv.sample_rate_hz)
84c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici
85c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici        # Calculate final measurements
86c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici        derived_measurements = []
87c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici        for site in energy_results:
88c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici            total_energy = energy_results[site]['end'] - energy_results[site]['start']
89dd26b43ac5237d967dca702deb5274a566a8e885Sergei Trofimov            name = '{}_total_energy'.format(site)
90dd26b43ac5237d967dca702deb5274a566a8e885Sergei Trofimov            derived_measurements.append(DerivedMetric(name, total_energy, MEASUREMENT_TYPES['energy']))
91c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici
92c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici        for site in power_results:
93c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici            power = power_results[site] / (count + 1)  #pylint: disable=undefined-loop-variable
94dd26b43ac5237d967dca702deb5274a566a8e885Sergei Trofimov            name = '{}_average_power'.format(site)
95dd26b43ac5237d967dca702deb5274a566a8e885Sergei Trofimov            derived_measurements.append(DerivedMetric(name, power, MEASUREMENT_TYPES['power']))
96c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici
97c093d567542460e398662b76e0577fbb49ae89f7Marc Bonnici        return derived_measurements
98