blob: 3de48638df8354cd8b65912972a1afef4094cf0c [file] [log] [blame]
Enrico Granata1d590082017-04-12 17:25:23 -07001/*
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
17package com.android.car.obd2;
18
19import android.os.SystemClock;
20import android.util.JsonWriter;
21import android.util.Log;
22import com.android.car.obd2.Obd2Command.FreezeFrameCommand;
23import com.android.car.obd2.Obd2Command.OutputSemanticHandler;
24import java.io.IOException;
25import java.util.ArrayList;
26import java.util.List;
27import java.util.Optional;
28import java.util.Set;
29
30public 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
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
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
85 FreezeFrameIdentity(String dtc, int id) {
86 this.dtc = dtc;
87 this.id = id;
88 }
89 }
90
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
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}