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