1#!/usr/bin/env python3 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# OBD2 standard sensor indices are different from those used by the 19# Android Auto Diagnostics API. This script maps from OBD2 sensors to 20# those expected by the Diagnostics API. 21# To use: 22# ./obd2_to_diagjson.py --src file1.json --dst file2.json 23# It is acceptable and supported to point --src and --dst to the same file 24 25import collections 26import json 27import os, os.path, sys 28 29class Json(object): 30 @classmethod 31 def load(cls, file): 32 return Json(json.load(file)) 33 34 @classmethod 35 def wrapIfNeeded(cls, item): 36 if isinstance(item, list) or isinstance(item, dict): 37 return Json(item) 38 return item 39 40 def __init__(self, doc): 41 self.doc = doc 42 43 def __str__(self): 44 return str(self.doc) 45 46 def __repr__(self): 47 return self.__str__() 48 49 def __getattr__(self, attr): 50 return Json.wrapIfNeeded(self.doc.get(attr)) 51 52 def __iter__(self): 53 class Iter(object): 54 def __init__(self, doc): 55 self.doc = doc.__iter__() 56 57 def __next__(self): 58 return Json.wrapIfNeeded(self.doc.__next__()) 59 60 return Iter(self.doc) 61 62class OrderedStore(object): 63 def __init__(self): 64 self.__dict__['store'] = collections.OrderedDict() 65 66 def __setattr__(self, name, value): 67 self.__dict__['store'][name] = value 68 69 def __getattr__(self, name): 70 return self.__dict__['store'][name] 71 72 def get(self, name, default=None): 73 return self.__dict__['store'].get(name, default) 74 75 def getStore(self): 76 return self.__dict__['store'] 77 78 def __iter__(self): 79 return iter(self.__dict__['store']) 80 81 def __delattr__(self, name): 82 del self.__dict__['store'][name] 83 84 def __str__(self): 85 return str(self.__dict__['store']) 86 87 def toJSON(self): 88 return json.dumps(self.store) 89 90class Event(object): 91 def __init__(self): 92 self.store = OrderedStore() 93 94 def setTimestamp(self, timestamp): 95 self.store.timestamp = timestamp 96 return self 97 98 def getTimestamp(self): 99 return self.store.timestamp 100 101 def setType(self, type): 102 self.store.type = type 103 return self 104 105 def getType(self): 106 return self.store.type 107 108 def setStringValue(self, string): 109 if string: 110 self.store.stringValue = string 111 return self 112 113 def getStringValue(self): 114 return self.store.get('stringValue') 115 116 def setIntValue(self, id, value): 117 if 'intValues' not in self.store: 118 self.store.intValues = [] 119 d = collections.OrderedDict() 120 d['id'] = id 121 d['value'] = value 122 self.store.intValues.append(d) 123 return self 124 125 def intValues(self): 126 if 'intValues' not in self.store: 127 return [] 128 for value in self.store.intValues: 129 yield (value['id'], value['value']) 130 131 def setFloatValue(self, id, value): 132 if 'floatValues' not in self.store: 133 self.store.floatValues = [] 134 d = collections.OrderedDict() 135 d['id'] = id 136 d['value'] = value 137 self.store.floatValues.append(d) 138 return self 139 140 def floatValues(self): 141 if 'floatValues' not in self.store: 142 return [] 143 for value in self.store.floatValues: 144 yield (value['id'], value['value']) 145 146 @classmethod 147 def fromJson(cls, json): 148 event = Event() 149 event.setTimestamp(json.timestamp) 150 event.setType(json.type) 151 for intValue in json.intValues: 152 event.setIntValue(intValue.id, intValue.value) 153 for floatValue in json.floatValues: 154 event.setFloatValue(floatValue.id, floatValue.value) 155 event.setStringValue(json.stringValue) 156 return event 157 158 def transform(self, intMapping, floatMapping): 159 event = Event() 160 event.setTimestamp(self.getTimestamp()) 161 event.setType(self.getType()) 162 for id, value in self.intValues(): 163 if id in intMapping: 164 intMapping[id](event, value) 165 else: 166 print('warning: integer id 0x%x not found in mapping. dropped.' % id) 167 for id, value in self.floatValues(): 168 if id in floatMapping: 169 floatMapping[id](event, value) 170 else: 171 print('warning: float id 0x%x not found in mapping. dropped.' % id) 172 event.setStringValue(self.getStringValue()) 173 return event 174 175 def getStore(self): 176 return self.store.getStore() 177 178class EventEncoder(json.JSONEncoder): 179 def default(self, o): 180 if isinstance(o, Event): 181 return o.getStore() 182 183# Mappings between standard OBD2 sensors and the indices 184# used by Vehicle HAL 185intSensorsMapping = { 186 0x03 : lambda event,value: event.setIntValue(0, value), 187 0x05 : lambda event,value: event.setFloatValue(1, value), 188 0x0A : lambda event,value: event.setIntValue(22, value), 189 0x0C : lambda event,value: event.setFloatValue(8, value), 190 0x0D : lambda event,value: event.setFloatValue(9, value), 191 0x1F : lambda event,value: event.setIntValue(7, value), 192 0x5C : lambda event,value: event.setIntValue(23, value), 193} 194 195floatSensorsMapping = { 196 0x04 : lambda event, value: event.setFloatValue(0, value), 197 0x06 : lambda event, value: event.setFloatValue(2, value), 198 0x07 : lambda event, value: event.setFloatValue(3, value), 199 0x08 : lambda event, value: event.setFloatValue(4, value), 200 0x09 : lambda event, value: event.setFloatValue(5, value), 201 0x11 : lambda event, value: event.setFloatValue(12, value), 202 0x2F : lambda event, value: event.setFloatValue(42, value), 203 0x46 : lambda event, value: event.setIntValue(13, int(value)), 204} 205 206def parseOptions(): 207 from argparse import ArgumentParser 208 parser = ArgumentParser(description='OBD2 to Diagnostics JSON Converter') 209 parser.add_argument('--src', '-S', dest='source_file', 210 help='The source file to convert from', required=True) 211 parser.add_argument('--dst', '-D', dest='destination_file', 212 help='The destination file to convert to', required=True) 213 return parser.parse_args() 214 215args = parseOptions() 216if not os.path.exists(args.source_file): 217 print('source file %s does not exist' % args.source_file) 218 sys.exit(1) 219 220source_json = Json.load(open(args.source_file)) 221dest_events = [] 222 223for source_json_event in source_json: 224 source_event = Event.fromJson(source_json_event) 225 destination_event = source_event.transform(intSensorsMapping, floatSensorsMapping) 226 dest_events.append(destination_event) 227 228json.dump(dest_events, open(args.destination_file, 'w'), cls=EventEncoder) 229