1#!/usr/bin/env python3.4 2# 3# Copyright (C) 2017 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# 17 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. 22 23import sys 24sys.dont_write_bytecode = True 25 26import hidl_parser.parser 27 28class 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 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 54class 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 71 def suffix(self, theSensors): 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 103class 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): 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" + \ 129"package android.car.diagnostic;\n" + \ 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 148 return s 149 150 def indent(self): 151 return 4 152 153class PythonSensorPolicy(SensorPolicy): 154 """The sensor policy that emits Python sensor descriptions.""" 155 def sensor(self, theSensor, theSensors): 156 return "DIAGNOSTIC_SENSOR_%s_%s = %s" % ( 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 164 return "DIAGNOSTIC_SENSOR_%s_%s" % (descriptor, sensorId.upper()) 165 166class IntDefSensorPolicy(SensorPolicy): 167 """The sensor policy that emits @IntDef sensor descriptions.""" 168 def sensor(self, theSensor, theSensors): 169 sensorName = theSensor.name.replace("_INDEX", "") 170 return "%sSensorIndex.%s," % (theSensors.descriptor,sensorName) 171 172 def prefix(self, theSensors): 173 return " /** @hide */\n @Retention(RetentionPolicy.SOURCE)\n @IntDef({" 174 175 def indent(self): 176 return 8 177 178 def suffix(self, theSensors): 179 return " })\n public @interface SensorIndex {}" 180 181class 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 202intSensors = SensorList(descriptor="Integer") 203floatSensors = SensorList(descriptor="Float") 204 205class intSensor(metaclass=SensorMeta): 206 sensorList = intSensors 207 208class floatSensor(metaclass=SensorMeta): 209 sensorList = floatSensors 210 211def applyPolicy(policy, destfile): 212 """Given a sensor policy, apply it to all known sensor types""" 213 applyIntPolicy(policy, destfile) 214 applyFloatPolicy(policy, destfile) 215 216def applyIntPolicy(policy, destfile): 217 "Given a sensor policy, apply it to integer sensors" 218 print(policy.sensors(intSensors), file=destfile) 219 220def applyFloatPolicy(policy, destfile): 221 "Given a sensor policy, apply it to float sensors" 222 print(policy.sensors(floatSensors), file=destfile) 223 224def java(destfile): 225 applyPolicy(JavaSensorPolicy(), destfile) 226 227def intdef(destfile): 228 applyPolicy(IntDefSensorPolicy(), destfile) 229 230def python(destfile): 231 applyPolicy(PythonSensorPolicy(), destfile) 232 233def generateJava(filepath): 234 """Generate Java code for all sensors.""" 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() 247 248def 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 271def load(filepath): 272 """Load sensor data from Vehicle HAL.""" 273 ast = hidl_parser.parser.parse(filepath) 274 integerSensors = ast['enums']['DiagnosticIntegerSensorIndex'] 275 floatSensors = ast['enums']['DiagnosticFloatSensorIndex'] 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) 280 281import os 282 283if len(sys.argv) != 4: 284 print('syntax: update-obd2-sensors.py <path/to/types.hal> <path/to/android.car.diagnostic> <path/to/diagnostic_sensors.py>') 285 print('This script will parse types.hal, and use the resulting', end='') 286 print('parse tree to generate Java and Python lists of sensor identifiers.') 287 sys.exit(1) 288load(sys.argv[1]) 289generateJava(sys.argv[2]) 290generatePython(sys.argv[3]) 291