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):
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 = "    public static final class Obd2%sSensorIndex {\n" % \
113            theSensors.descriptor
114        s += "        private Obd2%sSensorIndex() {}\n" % \
115            theSensors.descriptor
116        return s
117
118    def suffix(self, theSensors):
119        return "    }"
120
121    def indent(self):
122        return 8
123
124class IntDefSensorPolicy(SensorPolicy):
125    """The sensor policy that emits @IntDef sensor descriptions."""
126    def sensor(self, theSensor, theSensors):
127        sensorName = theSensor.name.replace("_INDEX", "")
128        return "Obd2%sSensorIndex.%s," % (theSensors.descriptor,sensorName)
129
130    def prefix(self, theSensors):
131        return "    @Retention(RetentionPolicy.SOURCE)\n    @IntDef({"
132
133    def indent(self):
134        return 8
135
136    def suffix(self, theSensors):
137        return "    })\n    public @interface %sSensorIndex {}" % \
138            theSensors.descriptor
139
140class SensorMeta(type):
141    """Metaclass for sensor classes."""
142    def __new__(cls, name, parents, dct):
143        sensorList = dct['sensorList']
144        class SensorBase(object):
145            def __init__(self, name, comment=None, id=None):
146                self.name = name
147                self.comment = comment if comment else ""
148                if id: self.id = id
149                sensorList.addSensor(self)
150            def __repr__(self):
151                s = ""
152                if self.comment:
153                    s = s + self.comment + "\n"
154                s = s + self.name + " = " + str(self.id)
155                return s
156
157        newClass = super().__new__(cls, name, (SensorBase,), dct)
158        sensorList.sensorClass = newClass
159        return newClass
160
161intSensors = SensorList(descriptor="Integer")
162floatSensors = SensorList(descriptor="Float")
163
164class intSensor(metaclass=SensorMeta):
165    sensorList = intSensors
166
167class floatSensor(metaclass=SensorMeta):
168    sensorList = floatSensors
169
170def applyPolicy(policy, destfile):
171    """Given a sensor policy, apply it to all known sensor types"""
172    print(policy.sensors(intSensors), file=destfile)
173    print(policy.sensors(floatSensors), file=destfile)
174
175def java(destfile):
176    applyPolicy(JavaSensorPolicy(), destfile)
177
178def intdef(destfile):
179    applyPolicy(IntDefSensorPolicy(), destfile)
180
181def generate(filepath):
182    """Generate data for all sensors."""
183    destfile = open(filepath, "w")
184    print("/*", file=destfile)
185    print(" * Copyright (C) 2017 The Android Open Source Project", file=destfile)
186    print(" *", file=destfile)
187    print(" * Licensed under the Apache License, Version 2.0 (the \"License\");", file=destfile)
188    print(" * you may not use this file except in compliance with the License.", file=destfile)
189    print(" * You may obtain a copy of the License at", file=destfile)
190    print(" *", file=destfile)
191    print(" *      http://www.apache.org/licenses/LICENSE-2.0", file=destfile)
192    print(" *", file=destfile)
193    print(" * Unless required by applicable law or agreed to in writing, software", file=destfile)
194    print(" * distributed under the License is distributed on an \"AS IS\" BASIS,", file=destfile)
195    print(" * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", file=destfile)
196    print(" * See the License for the specific language governing permissions and", file=destfile)
197    print(" * limitations under the License.", file=destfile)
198    print("*/", file=destfile)
199    print("", file=destfile)
200    print("package android.car.hardware;", file=destfile)
201    print("", file=destfile)
202    print("import android.annotation.IntDef;", file=destfile)
203    print("import java.lang.annotation.Retention;", file=destfile)
204    print("import java.lang.annotation.RetentionPolicy;", file=destfile)
205    print("", file=destfile)
206    print("/**", file=destfile)
207    print(" * This class is a container for the indices of integer and float diagnostic sensors.", file=destfile)
208    print(" * These values are extracted from types.hal by packages/services/Car/tools/update-obd2-sensors.py", file=destfile)
209    print(" *", file=destfile)
210    print(" * DO NOT EDIT MANUALLY", file=destfile)
211    print(" *", file=destfile)
212    print(" * @hide", file=destfile)
213    print(" */", file=destfile)
214    print("public final class CarDiagnosticSensorIndices {", file=destfile)
215    java(destfile)
216    intdef(destfile)
217    print("}", file=destfile)
218
219def load(filepath):
220    """Load sensor data from Vehicle HAL."""
221    ast = hidl_parser.parser.parse(filepath)
222    integerSensors = ast['enums']['Obd2IntegerSensorIndex']
223    floatSensors = ast['enums']['Obd2FloatSensorIndex']
224    for case in integerSensors.cases:
225        intSensor(name=case.name, id=case.value)
226    for case in floatSensors.cases:
227        floatSensor(name=case.name, id=case.value)
228
229import os
230
231if len(sys.argv) != 3:
232    print('syntax: update-obd2-sensors.py <path/to/types.hal> <path/to/CarDiagnosticSensorIndices.java>')
233    print('This scrippt will parse types.hal, and use the resulting', end='')
234    print('parse tree to generate CarDiagnosticSensorIndices.java.')
235    sys.exit(1)
236load(sys.argv[1])
237generate(sys.argv[2])
238