Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 1 | #!/usr/bin/env python3.4 |
| 2 | # |
Enrico Granata | 4c43add | 2017-03-15 18:01:34 -0700 | [diff] [blame] | 3 | # Copyright (C) 2017 The Android Open Source Project |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 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 | # |
| 17 | |
Enrico Granata | 4c43add | 2017-03-15 18:01:34 -0700 | [diff] [blame] | 18 | # This script generates useful Java representations for the OBD2 sensors |
| 19 | # defined in Vehicle HAL. It is meant to be as an easy way to update the |
| 20 | # list of diagnostic sensors and get all downstream users of that information |
| 21 | # updated in a consistent fashion. |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 22 | |
Enrico Granata | 4c43add | 2017-03-15 18:01:34 -0700 | [diff] [blame] | 23 | import sys |
| 24 | sys.dont_write_bytecode = True |
| 25 | |
| 26 | import hidl_parser.parser |
| 27 | |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 28 | class SensorList(object): |
| 29 | """A list of sensors ordered by a unique identifier.""" |
| 30 | def __init__(self, descriptor): |
| 31 | self.sensors = [] |
| 32 | self.id = -1 |
| 33 | self.descriptor = descriptor |
| 34 | |
| 35 | def addSensor(self, sensor): |
| 36 | """Add a new sensor to the list.""" |
| 37 | if not hasattr(sensor, 'id'): |
| 38 | self.id += 1 |
| 39 | sensor.id = self.id |
| 40 | self.sensors.append(sensor) |
| 41 | |
| 42 | def finalizeList(self): |
| 43 | """Complete the list, adding well-known sensor information.""" |
| 44 | self.id -= 1 |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 45 | vendorStartSensor = self.sensorClass("VENDOR_START_INDEX", |
| 46 | id="LAST_SYSTEM_INDEX + 1") |
| 47 | # make calling finalizeList idempotent |
| 48 | self.finalizeList = lambda: self |
| 49 | return self |
| 50 | |
| 51 | def __getitem__(self, key): |
| 52 | return self.sensors.__getitem__(key) |
| 53 | |
| 54 | class SensorPolicy(object): |
| 55 | """A formatter object that does processing on sensor data.""" |
| 56 | @classmethod |
| 57 | def indentLines(cls, string, numSpaces): |
| 58 | indent = ' ' * numSpaces |
| 59 | parts = string.split('\n') |
| 60 | parts = [indent + part for part in parts] |
| 61 | return '\n'.join(parts) + "\n" |
| 62 | |
| 63 | def sensor(self, theSensor, theSensors): |
| 64 | """Produce output for a sensor.""" |
| 65 | pass |
| 66 | |
| 67 | def prefix(self, theSensors): |
| 68 | """Prefix string before any sensor data is generated.""" |
| 69 | return "" |
| 70 | |
Enrico Granata | d20ed36 | 2017-03-30 11:12:26 -0700 | [diff] [blame] | 71 | def suffix(self, theSensors): |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 72 | """Suffix string after all sensor data is generated.""" |
| 73 | return "" |
| 74 | |
| 75 | def indent(self): |
| 76 | """Indentation level for individual sensor data.""" |
| 77 | return 0 |
| 78 | |
| 79 | def separator(self): |
| 80 | """Separator between individual sensor data entries.""" |
| 81 | return "" |
| 82 | |
| 83 | def description(self): |
| 84 | """A description of this policy.""" |
| 85 | return "A sensor policy." |
| 86 | |
| 87 | def sensors(self, theSensors): |
| 88 | """Produce output for all sensors.""" |
| 89 | theSensors = theSensors.finalizeList() |
| 90 | s = self.prefix(theSensors) + "\n" |
| 91 | first = True |
| 92 | for theSensor in theSensors: |
| 93 | if first: |
| 94 | first = False |
| 95 | else: |
| 96 | s += self.separator() |
| 97 | sensorLine = SensorPolicy.indentLines(self.sensor(theSensor, |
| 98 | theSensors), self.indent()) |
| 99 | s += sensorLine |
| 100 | s += self.suffix(theSensors) + "\n" |
| 101 | return s |
| 102 | |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 103 | class JavaSensorPolicy(SensorPolicy): |
| 104 | """The sensor policy that emits Java sensor descriptions.""" |
| 105 | def sensor(self, theSensor, theSensors): |
| 106 | sensorName = theSensor.name.replace("_INDEX", "") |
| 107 | sensorId = str(theSensor.id).replace("_INDEX", "") |
| 108 | return "public static final int " + sensorName + " = " + \ |
| 109 | str(sensorId) + ";" |
| 110 | |
| 111 | def prefix(self, theSensors): |
Enrico Granata | 3b0f5b7 | 2017-07-31 14:04:50 -0700 | [diff] [blame] | 112 | s = \ |
| 113 | "/*\n" + \ |
| 114 | " * Copyright (C) 2017 The Android Open Source Project\n" + \ |
| 115 | " *\n" + \ |
| 116 | " * Licensed under the Apache License, Version 2.0 (the \"License\");\n" + \ |
| 117 | " * you may not use this file except in compliance with the License.\n" + \ |
| 118 | " * You may obtain a copy of the License at\n" + \ |
| 119 | " *\n" + \ |
| 120 | " * http://www.apache.org/licenses/LICENSE-2.0\n" + \ |
| 121 | " *\n" + \ |
| 122 | " * Unless required by applicable law or agreed to in writing, software\n" + \ |
| 123 | " * distributed under the License is distributed on an \"AS IS\" BASIS,\n" + \ |
| 124 | " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + \ |
| 125 | " * See the License for the specific language governing permissions and\n" + \ |
| 126 | " * limitations under the License.\n" + \ |
| 127 | "*/\n" + \ |
| 128 | "\n" + \ |
Enrico Granata | 25e8946 | 2017-08-02 11:41:31 -0700 | [diff] [blame] | 129 | "package android.car.diagnostic;\n" + \ |
Enrico Granata | 3b0f5b7 | 2017-07-31 14:04:50 -0700 | [diff] [blame] | 130 | "\n" + \ |
| 131 | "import android.annotation.IntDef;\n" + \ |
| 132 | "import android.annotation.SystemApi;\n" + \ |
| 133 | "import java.lang.annotation.Retention;\n" + \ |
| 134 | "import java.lang.annotation.RetentionPolicy;\n" + \ |
| 135 | "\n" + \ |
| 136 | "/**\n" + \ |
| 137 | " * This class is a container for the indices of diagnostic sensors. The values are extracted by\n" + \ |
| 138 | " * running packages/services/Car/tools/update-obd2-sensors.py against types.hal.\n" + \ |
| 139 | " *\n" + \ |
| 140 | " * DO NOT EDIT MANUALLY\n" + \ |
| 141 | " *\n" + \ |
| 142 | " * @hide\n" + \ |
| 143 | " */\n" + \ |
| 144 | "@SystemApi\n" + \ |
| 145 | "public final class %sSensorIndex {\n" % theSensors.descriptor + \ |
| 146 | " private %sSensorIndex() {}\n" % theSensors.descriptor |
| 147 | |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 148 | return s |
| 149 | |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 150 | def indent(self): |
Enrico Granata | 3b0f5b7 | 2017-07-31 14:04:50 -0700 | [diff] [blame] | 151 | return 4 |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 152 | |
Enrico Granata | d20ed36 | 2017-03-30 11:12:26 -0700 | [diff] [blame] | 153 | class PythonSensorPolicy(SensorPolicy): |
| 154 | """The sensor policy that emits Python sensor descriptions.""" |
| 155 | def sensor(self, theSensor, theSensors): |
Enrico Granata | 976cee4 | 2017-07-25 17:23:45 -0700 | [diff] [blame] | 156 | return "DIAGNOSTIC_SENSOR_%s_%s = %s" % ( |
Enrico Granata | d20ed36 | 2017-03-30 11:12:26 -0700 | [diff] [blame] | 157 | theSensors.descriptor.upper(), |
| 158 | theSensor.name.upper(), |
| 159 | self.adjustSensorId(theSensors.descriptor.upper(), str(theSensor.id)) |
| 160 | ) |
| 161 | |
| 162 | def adjustSensorId(self, descriptor, sensorId): |
| 163 | if sensorId.isdigit(): return sensorId |
Enrico Granata | 976cee4 | 2017-07-25 17:23:45 -0700 | [diff] [blame] | 164 | return "DIAGNOSTIC_SENSOR_%s_%s" % (descriptor, sensorId.upper()) |
Enrico Granata | d20ed36 | 2017-03-30 11:12:26 -0700 | [diff] [blame] | 165 | |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 166 | class IntDefSensorPolicy(SensorPolicy): |
| 167 | """The sensor policy that emits @IntDef sensor descriptions.""" |
| 168 | def sensor(self, theSensor, theSensors): |
| 169 | sensorName = theSensor.name.replace("_INDEX", "") |
Enrico Granata | 976cee4 | 2017-07-25 17:23:45 -0700 | [diff] [blame] | 170 | return "%sSensorIndex.%s," % (theSensors.descriptor,sensorName) |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 171 | |
| 172 | def prefix(self, theSensors): |
Enrico Granata | 3b0f5b7 | 2017-07-31 14:04:50 -0700 | [diff] [blame] | 173 | return " /** @hide */\n @Retention(RetentionPolicy.SOURCE)\n @IntDef({" |
Enrico Granata | 4c43add | 2017-03-15 18:01:34 -0700 | [diff] [blame] | 174 | |
| 175 | def indent(self): |
| 176 | return 8 |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 177 | |
| 178 | def suffix(self, theSensors): |
Enrico Granata | 3b0f5b7 | 2017-07-31 14:04:50 -0700 | [diff] [blame] | 179 | return " })\n public @interface SensorIndex {}" |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 180 | |
| 181 | class SensorMeta(type): |
| 182 | """Metaclass for sensor classes.""" |
| 183 | def __new__(cls, name, parents, dct): |
| 184 | sensorList = dct['sensorList'] |
| 185 | class SensorBase(object): |
| 186 | def __init__(self, name, comment=None, id=None): |
| 187 | self.name = name |
| 188 | self.comment = comment if comment else "" |
| 189 | if id: self.id = id |
| 190 | sensorList.addSensor(self) |
| 191 | def __repr__(self): |
| 192 | s = "" |
| 193 | if self.comment: |
| 194 | s = s + self.comment + "\n" |
| 195 | s = s + self.name + " = " + str(self.id) |
| 196 | return s |
| 197 | |
| 198 | newClass = super().__new__(cls, name, (SensorBase,), dct) |
| 199 | sensorList.sensorClass = newClass |
| 200 | return newClass |
| 201 | |
| 202 | intSensors = SensorList(descriptor="Integer") |
| 203 | floatSensors = SensorList(descriptor="Float") |
| 204 | |
| 205 | class intSensor(metaclass=SensorMeta): |
| 206 | sensorList = intSensors |
| 207 | |
| 208 | class floatSensor(metaclass=SensorMeta): |
| 209 | sensorList = floatSensors |
| 210 | |
Enrico Granata | 4c43add | 2017-03-15 18:01:34 -0700 | [diff] [blame] | 211 | def applyPolicy(policy, destfile): |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 212 | """Given a sensor policy, apply it to all known sensor types""" |
Enrico Granata | 3b0f5b7 | 2017-07-31 14:04:50 -0700 | [diff] [blame] | 213 | applyIntPolicy(policy, destfile) |
| 214 | applyFloatPolicy(policy, destfile) |
| 215 | |
| 216 | def applyIntPolicy(policy, destfile): |
| 217 | "Given a sensor policy, apply it to integer sensors" |
Enrico Granata | 4c43add | 2017-03-15 18:01:34 -0700 | [diff] [blame] | 218 | print(policy.sensors(intSensors), file=destfile) |
Enrico Granata | 3b0f5b7 | 2017-07-31 14:04:50 -0700 | [diff] [blame] | 219 | |
| 220 | def applyFloatPolicy(policy, destfile): |
| 221 | "Given a sensor policy, apply it to float sensors" |
Enrico Granata | 4c43add | 2017-03-15 18:01:34 -0700 | [diff] [blame] | 222 | print(policy.sensors(floatSensors), file=destfile) |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 223 | |
Enrico Granata | 4c43add | 2017-03-15 18:01:34 -0700 | [diff] [blame] | 224 | def java(destfile): |
| 225 | applyPolicy(JavaSensorPolicy(), destfile) |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 226 | |
Enrico Granata | 4c43add | 2017-03-15 18:01:34 -0700 | [diff] [blame] | 227 | def intdef(destfile): |
| 228 | applyPolicy(IntDefSensorPolicy(), destfile) |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 229 | |
Enrico Granata | d20ed36 | 2017-03-30 11:12:26 -0700 | [diff] [blame] | 230 | def python(destfile): |
| 231 | applyPolicy(PythonSensorPolicy(), destfile) |
| 232 | |
| 233 | def generateJava(filepath): |
| 234 | """Generate Java code for all sensors.""" |
Enrico Granata | 3b0f5b7 | 2017-07-31 14:04:50 -0700 | [diff] [blame] | 235 | intfile = open(os.path.join(filepath, "IntegerSensorIndex.java"), "w") |
| 236 | floatfile = open(os.path.join(filepath, "FloatSensorIndex.java"), "w") |
| 237 | javaPolicy = JavaSensorPolicy() |
| 238 | intdefPolicy = IntDefSensorPolicy() |
| 239 | applyIntPolicy(javaPolicy, intfile) |
| 240 | applyIntPolicy(intdefPolicy, intfile) |
| 241 | applyFloatPolicy(javaPolicy, floatfile) |
| 242 | applyFloatPolicy(intdefPolicy, floatfile) |
| 243 | print("}", file=intfile) |
| 244 | print("}", file=floatfile) |
| 245 | intfile.close() |
| 246 | floatfile.close() |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 247 | |
Enrico Granata | d20ed36 | 2017-03-30 11:12:26 -0700 | [diff] [blame] | 248 | def generatePython(filepath): |
| 249 | """Generate Python code for all sensors.""" |
| 250 | destfile = open(filepath, "w") |
| 251 | print("#!/usr/bin/env python3", file=destfile) |
| 252 | print("#", file=destfile) |
| 253 | print("# Copyright (C) 2017 The Android Open Source Project", file=destfile) |
| 254 | print("#", file=destfile) |
| 255 | print("# Licensed under the Apache License, Version 2.0 (the \"License\");", file=destfile) |
| 256 | print("# you may not use this file except in compliance with the License.", file=destfile) |
| 257 | print("# You may obtain a copy of the License at", file=destfile) |
| 258 | print("#", file=destfile) |
| 259 | print("# http://www.apache.org/licenses/LICENSE-2.0", file=destfile) |
| 260 | print("#", file=destfile) |
| 261 | print("# Unless required by applicable law or agreed to in writing, software", file=destfile) |
| 262 | print("# distributed under the License is distributed on an \"AS IS\" BASIS,", file=destfile) |
| 263 | print("# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", file=destfile) |
| 264 | print("# See the License for the specific language governing permissions and", file=destfile) |
| 265 | print("# limitations under the License.", file=destfile) |
| 266 | print("#", file=destfile) |
| 267 | print("# This file is generated by types.hal by packages/services/Car/tools/update-obd2-sensors.py", file=destfile) |
| 268 | print("# DO NOT EDIT MANUALLY", file=destfile) |
| 269 | python(destfile) |
| 270 | |
Enrico Granata | 4c43add | 2017-03-15 18:01:34 -0700 | [diff] [blame] | 271 | def load(filepath): |
| 272 | """Load sensor data from Vehicle HAL.""" |
| 273 | ast = hidl_parser.parser.parse(filepath) |
Enrico Granata | 976cee4 | 2017-07-25 17:23:45 -0700 | [diff] [blame] | 274 | integerSensors = ast['enums']['DiagnosticIntegerSensorIndex'] |
| 275 | floatSensors = ast['enums']['DiagnosticFloatSensorIndex'] |
Enrico Granata | 4c43add | 2017-03-15 18:01:34 -0700 | [diff] [blame] | 276 | for case in integerSensors.cases: |
| 277 | intSensor(name=case.name, id=case.value) |
| 278 | for case in floatSensors.cases: |
| 279 | floatSensor(name=case.name, id=case.value) |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 280 | |
Enrico Granata | 4c43add | 2017-03-15 18:01:34 -0700 | [diff] [blame] | 281 | import os |
Enrico Granata | 6988f00 | 2017-02-13 12:07:52 -0800 | [diff] [blame] | 282 | |
Enrico Granata | d20ed36 | 2017-03-30 11:12:26 -0700 | [diff] [blame] | 283 | if len(sys.argv) != 4: |
Enrico Granata | 25e8946 | 2017-08-02 11:41:31 -0700 | [diff] [blame] | 284 | print('syntax: update-obd2-sensors.py <path/to/types.hal> <path/to/android.car.diagnostic> <path/to/diagnostic_sensors.py>') |
Enrico Granata | d20ed36 | 2017-03-30 11:12:26 -0700 | [diff] [blame] | 285 | print('This script will parse types.hal, and use the resulting', end='') |
Enrico Granata | 25e8946 | 2017-08-02 11:41:31 -0700 | [diff] [blame] | 286 | print('parse tree to generate Java and Python lists of sensor identifiers.') |
Enrico Granata | 4c43add | 2017-03-15 18:01:34 -0700 | [diff] [blame] | 287 | sys.exit(1) |
| 288 | load(sys.argv[1]) |
Enrico Granata | d20ed36 | 2017-03-30 11:12:26 -0700 | [diff] [blame] | 289 | generateJava(sys.argv[2]) |
| 290 | generatePython(sys.argv[3]) |