Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame^] | 1 | # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | # |
| 5 | # This module manages the platform properties in mttools/platforms |
| 6 | import os |
| 7 | import re |
| 8 | import json |
| 9 | from xorg_conf import XorgInputClassParser |
| 10 | |
| 11 | # path to current script directory |
| 12 | script_dir = os.path.dirname(os.path.realpath(__file__)) |
| 13 | platforms_dir = os.path.realpath(os.path.join(script_dir, "..", "platforms")) |
| 14 | xorg_conf_path = os.path.realpath(os.path.join(script_dir, "..", |
| 15 | "..", "xorg-conf")) |
| 16 | |
| 17 | class PlatformProperties(object): |
| 18 | """ |
| 19 | A class containing hardware and xorg properties for a platform. |
| 20 | The class can be created from an activity log or by providing |
| 21 | the name of the platform. Information will then be read from the |
| 22 | 'platforms_dir' directory. |
| 23 | """ |
| 24 | def __init__(self, platform=None, activity_data=None): |
| 25 | if platform: |
| 26 | basename = os.path.join(platforms_dir, platform) |
| 27 | self.name = platform |
| 28 | self.hwprops_file = basename + ".hwprops" |
| 29 | self.props_file = basename + ".props" |
| 30 | self.xorg_parser = XorgInputClassParser() |
| 31 | self._ParseHWProperties(open(self.hwprops_file).read()) |
| 32 | self._ParseProperties(open(self.props_file).read()) |
| 33 | self._UpdateDimensions() |
| 34 | elif activity_data: |
| 35 | self.name = "" |
| 36 | self._ParseActivityLog(activity_data) |
| 37 | |
| 38 | def _ParseActivityLog(self, activity_data): |
| 39 | """ |
| 40 | Parse property information from an activity log |
| 41 | """ |
| 42 | activity = json.loads(activity_data) |
| 43 | self.properties = activity["properties"] |
| 44 | |
| 45 | hwprops = activity["hardwareProperties"] |
| 46 | self.x_min = int(hwprops["left"]) |
| 47 | self.x_max = int(hwprops["right"]) |
| 48 | self.x_res = int(hwprops["xResolution"]) |
| 49 | self.y_min = int(hwprops["top"]) |
| 50 | self.y_max = int(hwprops["bottom"]) |
| 51 | self.y_res = int(hwprops["yResolution"]) |
| 52 | |
| 53 | def _ParseHWProperties(self, data): |
| 54 | """ |
| 55 | parse x and y dimensions and resolution from hwprops file |
| 56 | """ |
| 57 | xregex = re.compile("A: 35 ([0-9\-]+) ([0-9\-]+) ([0-9\-]+) " + |
| 58 | "([0-9\-]+) ([0-9\-]+)") |
| 59 | xmatch = xregex.search(data); |
| 60 | self.x_min = int(xmatch.group(1)) |
| 61 | self.x_max = int(xmatch.group(2)) |
| 62 | self.x_res = int(xmatch.group(5)) |
| 63 | |
| 64 | yregex = re.compile("A: 36 ([0-9\-]+) ([0-9\-]+) ([0-9\-]+) " + |
| 65 | "([0-9\-]+) ([0-9\-]+)") |
| 66 | ymatch = yregex.search(data); |
| 67 | self.y_min = int(ymatch.group(1)) |
| 68 | self.y_max = int(ymatch.group(2)) |
| 69 | self.y_res = int(ymatch.group(5)) |
| 70 | |
| 71 | def _ParseProperties(self, data): |
| 72 | """ |
| 73 | parse properties from file and inject xorg properties. |
| 74 | """ |
| 75 | self.properties = {} |
| 76 | data = json.loads(data) |
| 77 | |
| 78 | if "gestures" in data: |
| 79 | self.properties.update(data["gestures"]) |
| 80 | |
| 81 | if "device_class" in data: |
| 82 | self.device_class = data["device_class"] |
| 83 | |
| 84 | if xorg_conf_path and "xorg" in data and "file" in data["xorg"]: |
| 85 | filename = os.path.join(xorg_conf_path, data["xorg"]["file"]) |
| 86 | input_classes = self.xorg_parser.Parse(file=filename) |
| 87 | if "identifier" in data["xorg"]: |
| 88 | properties = input_classes[data["xorg"]["identifier"]] |
| 89 | self.properties.update(properties) |
| 90 | if "identifiers" in data["xorg"]: |
| 91 | for identifier in data["xorg"]["identifiers"]: |
| 92 | properties = input_classes[identifier] |
| 93 | self.properties.update(properties) |
| 94 | |
| 95 | def _UpdateDimensions(self): |
| 96 | """ |
| 97 | CMT allows hardware properties to be overwritten by xorg properties. |
| 98 | Do the same in this class. |
| 99 | """ |
| 100 | if "Active Area Left" in self.properties: |
| 101 | self.x_min = int(self.properties["Active Area Left"]) |
| 102 | if "Active Area Right" in self.properties: |
| 103 | self.x_max = int(self.properties["Active Area Right"]) |
| 104 | if "Horizontal Resolution" in self.properties: |
| 105 | self.x_res = int(self.properties["Horizontal Resolution"]) |
| 106 | if "Active Area Top" in self.properties: |
| 107 | self.y_min = int(self.properties["Active Area Top"]) |
| 108 | if "Active Area Bottom" in self.properties: |
| 109 | self.y_max = int(self.properties["Active Area Bottom"]) |
| 110 | if "Vertical Resolution" in self.properties: |
| 111 | self.y_res = int(self.properties["Vertical Resolution"]) |
| 112 | |
| 113 | def Match(self, other): |
| 114 | """ |
| 115 | Compare these properties to another PlatformProperties instance. |
| 116 | The return value is a score between 1. 0 meaning there is a big mismatch |
| 117 | and 1 meaning the properties match completely. |
| 118 | Only a selected range of properties are compared in order to |
| 119 | prevent property adjustments to cause platforms to be mismatched. |
| 120 | """ |
| 121 | scores = [] |
| 122 | def compare(a, b, what): |
| 123 | value = abs(float(a) - float(b)) |
| 124 | if value > 0: |
| 125 | value = min(1, value / abs(float(a)), abs(float(b))) |
| 126 | scores.append(1-value) |
| 127 | def compare_attr(what): |
| 128 | compare(getattr(self, what), getattr(other, what), what) |
| 129 | def compare_prop(what): |
| 130 | if what not in self.properties or what not in other.properties: |
| 131 | return 0 |
| 132 | compare(self.properties[what], other.properties[what], what) |
| 133 | compare_attr("x_min") |
| 134 | compare_attr("x_max") |
| 135 | compare_attr("x_res") |
| 136 | compare_prop("Pressure Calibration Offset") |
| 137 | return reduce(lambda x,y: (x * y), scores) |
| 138 | |
| 139 | class PlatformDatabase(object): |
| 140 | """ |
| 141 | This class reads all available platforms from the platforms_dir and allows |
| 142 | to search for matching platforms to an activity log file. |
| 143 | """ |
| 144 | def __init__(self): |
| 145 | platform_files = [ f for f in os.listdir(platforms_dir) |
| 146 | if f.endswith(".hwprops") ] |
| 147 | self.platforms = {} |
| 148 | for file in platform_files: |
| 149 | name = file.replace(".hwprops", "") |
| 150 | self.platforms[name] = PlatformProperties(platform=name) |
| 151 | |
| 152 | def FindMatching(self, activity_data): |
| 153 | """ |
| 154 | Returns the PlatformProperties instance of the platform matching |
| 155 | the activity log data. |
| 156 | """ |
| 157 | result = None |
| 158 | properties = PlatformProperties(activity_data=activity_data) |
| 159 | for name, platform in self.platforms.items(): |
| 160 | score = platform.Match(properties) |
| 161 | if score > 0.9: |
| 162 | if result: |
| 163 | print ("multiple matching platforms:", result.name, |
| 164 | "and", platform.name) |
| 165 | sys.exit(-1) |
| 166 | result = platform |
| 167 | if not result: |
| 168 | print "cannot find matching platform" |
| 169 | return result |