• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.obd2;
18 
19 import android.os.SystemClock;
20 import android.util.JsonWriter;
21 import android.util.Log;
22 import com.android.car.obd2.Obd2Command.FreezeFrameCommand;
23 import com.android.car.obd2.Obd2Command.OutputSemanticHandler;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.Optional;
28 import java.util.Set;
29 
30 public class Obd2FreezeFrameGenerator {
31     public static final String FRAME_TYPE_FREEZE = "freeze";
32     public static final String TAG = Obd2FreezeFrameGenerator.class.getSimpleName();
33 
34     private final Obd2Connection mConnection;
35     private final List<OutputSemanticHandler<Integer>> mIntegerCommands = new ArrayList<>();
36     private final List<OutputSemanticHandler<Float>> mFloatCommands = new ArrayList<>();
37 
38     private List<String> mPreviousDtcs = new ArrayList<>();
39 
Obd2FreezeFrameGenerator(Obd2Connection connection)40     public Obd2FreezeFrameGenerator(Obd2Connection connection)
41             throws IOException, InterruptedException {
42         mConnection = connection;
43         Set<Integer> connectionPids = connection.getSupportedPIDs();
44         Set<Integer> apiIntegerPids = Obd2Command.getSupportedIntegerCommands();
45         Set<Integer> apiFloatPids = Obd2Command.getSupportedFloatCommands();
46         apiIntegerPids
47                 .stream()
48                 .filter(connectionPids::contains)
49                 .forEach((Integer pid) -> mIntegerCommands.add(Obd2Command.getIntegerCommand(pid)));
50         apiFloatPids
51                 .stream()
52                 .filter(connectionPids::contains)
53                 .forEach((Integer pid) -> mFloatCommands.add(Obd2Command.getFloatCommand(pid)));
54         Log.i(
55                 TAG,
56                 String.format(
57                         "connectionPids = %s\napiIntegerPids=%s\napiFloatPids = %s\n"
58                                 + "mIntegerCommands = %s\nmFloatCommands = %s\n",
59                         connectionPids,
60                         apiIntegerPids,
61                         apiFloatPids,
62                         mIntegerCommands,
63                         mFloatCommands));
64     }
65 
generate(JsonWriter jsonWriter)66     public JsonWriter generate(JsonWriter jsonWriter) throws IOException, InterruptedException {
67         return generate(jsonWriter, SystemClock.elapsedRealtimeNanos());
68     }
69 
70     // OBD2 does not have a notion of timestamping the fault codes
71     // As such, we need to perform additional magic in order to figure out
72     // whether a fault code we retrieved is the same as a fault code we already
73     // saw in a past iteration. The logic goes as follows:
74     // for every position i in currentDtcs, if mPreviousDtcs[i] is the same
75     // fault code, then assume they are identical. If they are not the same fault code,
76     // then everything in currentDtcs[i...size()) is assumed to be a new fault code as
77     // something in the list must have moved around; if currentDtcs is shorter than
78     // mPreviousDtcs then obviously exit at the end of currentDtcs; if currentDtcs
79     // is longer, however, anything in currentDtcs past the end of mPreviousDtcs is a new
80     // fault code and will be included
81     private final class FreezeFrameIdentity {
82         public final String dtc;
83         public final int id;
84 
FreezeFrameIdentity(String dtc, int id)85         FreezeFrameIdentity(String dtc, int id) {
86             this.dtc = dtc;
87             this.id = id;
88         }
89     }
90 
discoverNewDtcs(List<String> currentDtcs)91     private List<FreezeFrameIdentity> discoverNewDtcs(List<String> currentDtcs) {
92         List<FreezeFrameIdentity> newDtcs = new ArrayList<>();
93         int currentIndex = 0;
94         boolean inCopyAllMode = false;
95 
96         for (; currentIndex < currentDtcs.size(); ++currentIndex) {
97             if (currentIndex == mPreviousDtcs.size()) {
98                 // we have more current DTCs than previous DTCs, copy everything
99                 inCopyAllMode = true;
100                 break;
101             }
102             if (!currentDtcs.get(currentIndex).equals(mPreviousDtcs.get(currentIndex))) {
103                 // we found a different DTC, copy everything
104                 inCopyAllMode = true;
105                 break;
106             }
107             // same DTC, not at end of either list yet, keep looping
108         }
109 
110         if (inCopyAllMode) {
111             for (; currentIndex < currentDtcs.size(); ++currentIndex) {
112                 newDtcs.add(new FreezeFrameIdentity(currentDtcs.get(currentIndex), currentIndex));
113             }
114         }
115 
116         return newDtcs;
117     }
118 
generate(JsonWriter jsonWriter, long timestamp)119     public JsonWriter generate(JsonWriter jsonWriter, long timestamp)
120             throws IOException, InterruptedException {
121         List<String> currentDtcs = mConnection.getDiagnosticTroubleCodes();
122         List<FreezeFrameIdentity> newDtcs = discoverNewDtcs(currentDtcs);
123         mPreviousDtcs = currentDtcs;
124         for (FreezeFrameIdentity freezeFrame : newDtcs) {
125             jsonWriter.beginObject();
126             jsonWriter.name("type").value(FRAME_TYPE_FREEZE);
127             jsonWriter.name("timestamp").value(timestamp);
128             jsonWriter.name("stringValue").value(freezeFrame.dtc);
129             jsonWriter.name("intValues").beginArray();
130             for (OutputSemanticHandler<Integer> handler : mIntegerCommands) {
131                 FreezeFrameCommand<Integer> command =
132                         Obd2Command.getFreezeFrameCommand(handler, freezeFrame.id);
133                 try {
134                     Optional<Integer> result = command.run(mConnection);
135                     if (result.isPresent()) {
136                         jsonWriter.beginObject();
137                         jsonWriter.name("id").value(command.getPid());
138                         jsonWriter.name("value").value(result.get());
139                         jsonWriter.endObject();
140                     }
141                 } catch (IOException | InterruptedException e) {
142                     Log.w(
143                             TAG,
144                             String.format(
145                                     "unable to retrieve OBD2 pid %d due to exception: %s",
146                                     command.getPid(), e));
147                     // skip this entry
148                 }
149             }
150             jsonWriter.endArray();
151             jsonWriter.name("floatValues").beginArray();
152             for (OutputSemanticHandler<Float> handler : mFloatCommands) {
153                 FreezeFrameCommand<Float> command =
154                         Obd2Command.getFreezeFrameCommand(handler, freezeFrame.id);
155                 try {
156                     Optional<Float> result = command.run(mConnection);
157                     if (result.isPresent()) {
158                         jsonWriter.beginObject();
159                         jsonWriter.name("id").value(command.getPid());
160                         jsonWriter.name("value").value(result.get());
161                         jsonWriter.endObject();
162                     }
163                 } catch (IOException | InterruptedException e) {
164                     Log.w(
165                             TAG,
166                             String.format(
167                                     "unable to retrieve OBD2 pid %d due to exception: %s",
168                                     command.getPid(), e));
169                     // skip this entry
170                 }
171             }
172             jsonWriter.endArray();
173             jsonWriter.endObject();
174         }
175         return jsonWriter;
176     }
177 }
178