blob: e161258558ae4c5a9adfd0b4b33f88d8493fc93c [file] [log] [blame]
#!/usr/bin/env python3.4
#
# Copyright (C) 2017 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This script generates useful Java representations for the OBD2 sensors
# defined in Vehicle HAL. It is meant to be as an easy way to update the
# list of diagnostic sensors and get all downstream users of that information
# updated in a consistent fashion.
import sys
sys.dont_write_bytecode = True
import hidl_parser.parser
class SensorList(object):
"""A list of sensors ordered by a unique identifier."""
def __init__(self, descriptor):
self.sensors = []
self.id = -1
self.descriptor = descriptor
def addSensor(self, sensor):
"""Add a new sensor to the list."""
if not hasattr(sensor, 'id'):
self.id += 1
sensor.id = self.id
self.sensors.append(sensor)
def finalizeList(self):
"""Complete the list, adding well-known sensor information."""
self.id -= 1
vendorStartSensor = self.sensorClass("VENDOR_START_INDEX",
id="LAST_SYSTEM_INDEX + 1")
# make calling finalizeList idempotent
self.finalizeList = lambda: self
return self
def __getitem__(self, key):
return self.sensors.__getitem__(key)
class SensorPolicy(object):
"""A formatter object that does processing on sensor data."""
@classmethod
def indentLines(cls, string, numSpaces):
indent = ' ' * numSpaces
parts = string.split('\n')
parts = [indent + part for part in parts]
return '\n'.join(parts) + "\n"
def sensor(self, theSensor, theSensors):
"""Produce output for a sensor."""
pass
def prefix(self, theSensors):
"""Prefix string before any sensor data is generated."""
return ""
def suffix(self, theSensors):
"""Suffix string after all sensor data is generated."""
return ""
def indent(self):
"""Indentation level for individual sensor data."""
return 0
def separator(self):
"""Separator between individual sensor data entries."""
return ""
def description(self):
"""A description of this policy."""
return "A sensor policy."
def sensors(self, theSensors):
"""Produce output for all sensors."""
theSensors = theSensors.finalizeList()
s = self.prefix(theSensors) + "\n"
first = True
for theSensor in theSensors:
if first:
first = False
else:
s += self.separator()
sensorLine = SensorPolicy.indentLines(self.sensor(theSensor,
theSensors), self.indent())
s += sensorLine
s += self.suffix(theSensors) + "\n"
return s
class JavaSensorPolicy(SensorPolicy):
"""The sensor policy that emits Java sensor descriptions."""
def sensor(self, theSensor, theSensors):
sensorName = theSensor.name.replace("_INDEX", "")
sensorId = str(theSensor.id).replace("_INDEX", "")
return "public static final int " + sensorName + " = " + \
str(sensorId) + ";"
def prefix(self, theSensors):
s = \
"/*\n" + \
" * Copyright (C) 2017 The Android Open Source Project\n" + \
" *\n" + \
" * Licensed under the Apache License, Version 2.0 (the \"License\");\n" + \
" * you may not use this file except in compliance with the License.\n" + \
" * You may obtain a copy of the License at\n" + \
" *\n" + \
" * http://www.apache.org/licenses/LICENSE-2.0\n" + \
" *\n" + \
" * Unless required by applicable law or agreed to in writing, software\n" + \
" * distributed under the License is distributed on an \"AS IS\" BASIS,\n" + \
" * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + \
" * See the License for the specific language governing permissions and\n" + \
" * limitations under the License.\n" + \
"*/\n" + \
"\n" + \
"package android.car.diagnostic;\n" + \
"\n" + \
"import android.annotation.IntDef;\n" + \
"import android.annotation.SystemApi;\n" + \
"import java.lang.annotation.Retention;\n" + \
"import java.lang.annotation.RetentionPolicy;\n" + \
"\n" + \
"/**\n" + \
" * This class is a container for the indices of diagnostic sensors. The values are extracted by\n" + \
" * running packages/services/Car/tools/update-obd2-sensors.py against types.hal.\n" + \
" *\n" + \
" * DO NOT EDIT MANUALLY\n" + \
" *\n" + \
" * @hide\n" + \
" */\n" + \
"@SystemApi\n" + \
"public final class %sSensorIndex {\n" % theSensors.descriptor + \
" private %sSensorIndex() {}\n" % theSensors.descriptor
return s
def indent(self):
return 4
class PythonSensorPolicy(SensorPolicy):
"""The sensor policy that emits Python sensor descriptions."""
def sensor(self, theSensor, theSensors):
return "DIAGNOSTIC_SENSOR_%s_%s = %s" % (
theSensors.descriptor.upper(),
theSensor.name.upper(),
self.adjustSensorId(theSensors.descriptor.upper(), str(theSensor.id))
)
def adjustSensorId(self, descriptor, sensorId):
if sensorId.isdigit(): return sensorId
return "DIAGNOSTIC_SENSOR_%s_%s" % (descriptor, sensorId.upper())
class IntDefSensorPolicy(SensorPolicy):
"""The sensor policy that emits @IntDef sensor descriptions."""
def sensor(self, theSensor, theSensors):
sensorName = theSensor.name.replace("_INDEX", "")
return "%sSensorIndex.%s," % (theSensors.descriptor,sensorName)
def prefix(self, theSensors):
return " /** @hide */\n @Retention(RetentionPolicy.SOURCE)\n @IntDef({"
def indent(self):
return 8
def suffix(self, theSensors):
return " })\n public @interface SensorIndex {}"
class SensorMeta(type):
"""Metaclass for sensor classes."""
def __new__(cls, name, parents, dct):
sensorList = dct['sensorList']
class SensorBase(object):
def __init__(self, name, comment=None, id=None):
self.name = name
self.comment = comment if comment else ""
if id: self.id = id
sensorList.addSensor(self)
def __repr__(self):
s = ""
if self.comment:
s = s + self.comment + "\n"
s = s + self.name + " = " + str(self.id)
return s
newClass = super().__new__(cls, name, (SensorBase,), dct)
sensorList.sensorClass = newClass
return newClass
intSensors = SensorList(descriptor="Integer")
floatSensors = SensorList(descriptor="Float")
class intSensor(metaclass=SensorMeta):
sensorList = intSensors
class floatSensor(metaclass=SensorMeta):
sensorList = floatSensors
def applyPolicy(policy, destfile):
"""Given a sensor policy, apply it to all known sensor types"""
applyIntPolicy(policy, destfile)
applyFloatPolicy(policy, destfile)
def applyIntPolicy(policy, destfile):
"Given a sensor policy, apply it to integer sensors"
print(policy.sensors(intSensors), file=destfile)
def applyFloatPolicy(policy, destfile):
"Given a sensor policy, apply it to float sensors"
print(policy.sensors(floatSensors), file=destfile)
def java(destfile):
applyPolicy(JavaSensorPolicy(), destfile)
def intdef(destfile):
applyPolicy(IntDefSensorPolicy(), destfile)
def python(destfile):
applyPolicy(PythonSensorPolicy(), destfile)
def generateJava(filepath):
"""Generate Java code for all sensors."""
intfile = open(os.path.join(filepath, "IntegerSensorIndex.java"), "w")
floatfile = open(os.path.join(filepath, "FloatSensorIndex.java"), "w")
javaPolicy = JavaSensorPolicy()
intdefPolicy = IntDefSensorPolicy()
applyIntPolicy(javaPolicy, intfile)
applyIntPolicy(intdefPolicy, intfile)
applyFloatPolicy(javaPolicy, floatfile)
applyFloatPolicy(intdefPolicy, floatfile)
print("}", file=intfile)
print("}", file=floatfile)
intfile.close()
floatfile.close()
def generatePython(filepath):
"""Generate Python code for all sensors."""
destfile = open(filepath, "w")
print("#!/usr/bin/env python3", file=destfile)
print("#", file=destfile)
print("# Copyright (C) 2017 The Android Open Source Project", file=destfile)
print("#", file=destfile)
print("# Licensed under the Apache License, Version 2.0 (the \"License\");", file=destfile)
print("# you may not use this file except in compliance with the License.", file=destfile)
print("# You may obtain a copy of the License at", file=destfile)
print("#", file=destfile)
print("# http://www.apache.org/licenses/LICENSE-2.0", file=destfile)
print("#", file=destfile)
print("# Unless required by applicable law or agreed to in writing, software", file=destfile)
print("# distributed under the License is distributed on an \"AS IS\" BASIS,", file=destfile)
print("# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", file=destfile)
print("# See the License for the specific language governing permissions and", file=destfile)
print("# limitations under the License.", file=destfile)
print("#", file=destfile)
print("# This file is generated by types.hal by packages/services/Car/tools/update-obd2-sensors.py", file=destfile)
print("# DO NOT EDIT MANUALLY", file=destfile)
python(destfile)
def load(filepath):
"""Load sensor data from Vehicle HAL."""
ast = hidl_parser.parser.parse(filepath)
integerSensors = ast['enums']['DiagnosticIntegerSensorIndex']
floatSensors = ast['enums']['DiagnosticFloatSensorIndex']
for case in integerSensors.cases:
intSensor(name=case.name, id=case.value)
for case in floatSensors.cases:
floatSensor(name=case.name, id=case.value)
import os
if len(sys.argv) != 4:
print('syntax: update-obd2-sensors.py <path/to/types.hal> <path/to/android.car.diagnostic> <path/to/diagnostic_sensors.py>')
print('This script will parse types.hal, and use the resulting', end='')
print('parse tree to generate Java and Python lists of sensor identifiers.')
sys.exit(1)
load(sys.argv[1])
generateJava(sys.argv[2])
generatePython(sys.argv[3])