diff options
10 files changed, 408 insertions, 18 deletions
diff --git a/obd2-lib/src/com/android/car/obd2/IntegerArrayStream.java b/obd2-lib/src/com/android/car/obd2/IntegerArrayStream.java index 56f64f77..4768cebc 100644 --- a/obd2-lib/src/com/android/car/obd2/IntegerArrayStream.java +++ b/obd2-lib/src/com/android/car/obd2/IntegerArrayStream.java | |||
@@ -43,6 +43,10 @@ public class IntegerArrayStream { | |||
43 | return mData.length - mIndex; | 43 | return mData.length - mIndex; |
44 | } | 44 | } |
45 | 45 | ||
46 | public boolean isEmpty() { | ||
47 | return residualLength() == 0; | ||
48 | } | ||
49 | |||
46 | public boolean hasAtLeast(int n) { | 50 | public boolean hasAtLeast(int n) { |
47 | return residualLength() >= n; | 51 | return residualLength() >= n; |
48 | } | 52 | } |
diff --git a/obd2-lib/src/com/android/car/obd2/Obd2Command.java b/obd2-lib/src/com/android/car/obd2/Obd2Command.java index e3366cfd..30fca0c1 100644 --- a/obd2-lib/src/com/android/car/obd2/Obd2Command.java +++ b/obd2-lib/src/com/android/car/obd2/Obd2Command.java | |||
@@ -172,7 +172,7 @@ public abstract class Obd2Command<ValueType> { | |||
172 | * @param <ValueType> The Java type that represents the command's result type. | 172 | * @param <ValueType> The Java type that represents the command's result type. |
173 | */ | 173 | */ |
174 | public static class FreezeFrameCommand<ValueType> extends Obd2Command<ValueType> { | 174 | public static class FreezeFrameCommand<ValueType> extends Obd2Command<ValueType> { |
175 | private static final int RESPONSE_MARKER = 0x2; | 175 | private static final int RESPONSE_MARKER = 0x42; |
176 | 176 | ||
177 | private int mFrameId; | 177 | private int mFrameId; |
178 | 178 | ||
diff --git a/obd2-lib/src/com/android/car/obd2/Obd2Connection.java b/obd2-lib/src/com/android/car/obd2/Obd2Connection.java index 577b7989..f7a2d369 100644 --- a/obd2-lib/src/com/android/car/obd2/Obd2Connection.java +++ b/obd2-lib/src/com/android/car/obd2/Obd2Connection.java | |||
@@ -20,12 +20,16 @@ import android.util.Log; | |||
20 | import java.io.IOException; | 20 | import java.io.IOException; |
21 | import java.io.InputStream; | 21 | import java.io.InputStream; |
22 | import java.io.OutputStream; | 22 | import java.io.OutputStream; |
23 | import java.util.ArrayList; | ||
23 | import java.util.HashSet; | 24 | import java.util.HashSet; |
25 | import java.util.List; | ||
24 | import java.util.Objects; | 26 | import java.util.Objects; |
25 | import java.util.Set; | 27 | import java.util.Set; |
26 | 28 | ||
27 | /** This class represents a connection between Java code and a "vehicle" that talks OBD2. */ | 29 | /** This class represents a connection between Java code and a "vehicle" that talks OBD2. */ |
28 | public class Obd2Connection { | 30 | public class Obd2Connection { |
31 | private static final String TAG = Obd2Connection.class.getSimpleName(); | ||
32 | private static final boolean DBG = false; | ||
29 | 33 | ||
30 | /** | 34 | /** |
31 | * The transport layer that moves OBD2 requests from us to the remote entity and viceversa. It | 35 | * The transport layer that moves OBD2 requests from us to the remote entity and viceversa. It |
@@ -116,6 +120,10 @@ public class Obd2Connection { | |||
116 | InputStream in = Objects.requireNonNull(mConnection.getInputStream()); | 120 | InputStream in = Objects.requireNonNull(mConnection.getInputStream()); |
117 | OutputStream out = Objects.requireNonNull(mConnection.getOutputStream()); | 121 | OutputStream out = Objects.requireNonNull(mConnection.getOutputStream()); |
118 | 122 | ||
123 | if (DBG) { | ||
124 | Log.i(TAG, "runImpl(" + command + ")"); | ||
125 | } | ||
126 | |||
119 | out.write((command + "\r").getBytes()); | 127 | out.write((command + "\r").getBytes()); |
120 | out.flush(); | 128 | out.flush(); |
121 | 129 | ||
@@ -131,6 +139,11 @@ public class Obd2Connection { | |||
131 | } | 139 | } |
132 | 140 | ||
133 | String responseValue = response.toString(); | 141 | String responseValue = response.toString(); |
142 | |||
143 | if (DBG) { | ||
144 | Log.i(TAG, "runImpl() returned " + responseValue); | ||
145 | } | ||
146 | |||
134 | return responseValue; | 147 | return responseValue; |
135 | } | 148 | } |
136 | 149 | ||
@@ -141,11 +154,30 @@ public class Obd2Connection { | |||
141 | return response; | 154 | return response; |
142 | } | 155 | } |
143 | 156 | ||
157 | String unpackLongFrame(String response) { | ||
158 | // long frames come back to us containing colon separated portions | ||
159 | if (response.indexOf(':') < 0) return response; | ||
160 | |||
161 | // remove everything until the first colon | ||
162 | response = response.substring(response.indexOf(':') + 1); | ||
163 | |||
164 | // then remove the <digit>: portions (sequential frame parts) | ||
165 | //TODO(egranata): maybe validate the sequence of digits is progressive | ||
166 | return response.replaceAll("[0-9]:", ""); | ||
167 | } | ||
168 | |||
144 | public int[] run(String command) throws IOException, InterruptedException { | 169 | public int[] run(String command) throws IOException, InterruptedException { |
145 | String responseValue = runImpl(command); | 170 | String responseValue = runImpl(command); |
146 | String originalResponseValue = responseValue; | 171 | String originalResponseValue = responseValue; |
147 | if (responseValue.startsWith(command)) | 172 | String unspacedCommand = command.replaceAll(" ", ""); |
148 | responseValue = responseValue.substring(command.length()); | 173 | if (responseValue.startsWith(unspacedCommand)) |
174 | responseValue = responseValue.substring(unspacedCommand.length()); | ||
175 | responseValue = unpackLongFrame(responseValue); | ||
176 | |||
177 | if (DBG) { | ||
178 | Log.i(TAG, "post-processed response " + responseValue); | ||
179 | } | ||
180 | |||
149 | //TODO(egranata): should probably handle these intelligently | 181 | //TODO(egranata): should probably handle these intelligently |
150 | responseValue = | 182 | responseValue = |
151 | removeSideData( | 183 | removeSideData( |
@@ -161,11 +193,12 @@ public class Obd2Connection { | |||
161 | if (responseValue.equals("?")) return new int[] {0}; | 193 | if (responseValue.equals("?")) return new int[] {0}; |
162 | if (responseValue.equals("NODATA")) return new int[] {}; | 194 | if (responseValue.equals("NODATA")) return new int[] {}; |
163 | if (responseValue.equals("UNABLETOCONNECT")) throw new IOException("connection failure"); | 195 | if (responseValue.equals("UNABLETOCONNECT")) throw new IOException("connection failure"); |
196 | if (responseValue.equals("CANERROR")) throw new IOException("CAN bus error"); | ||
164 | try { | 197 | try { |
165 | return toHexValues(responseValue); | 198 | return toHexValues(responseValue); |
166 | } catch (IllegalArgumentException e) { | 199 | } catch (IllegalArgumentException e) { |
167 | Log.e( | 200 | Log.e( |
168 | "OBD2", | 201 | TAG, |
169 | String.format( | 202 | String.format( |
170 | "conversion error: command: '%s', original response: '%s'" | 203 | "conversion error: command: '%s', original response: '%s'" |
171 | + ", processed response: '%s'", | 204 | + ", processed response: '%s'", |
@@ -228,7 +261,7 @@ public class Obd2Connection { | |||
228 | public Set<Integer> getSupportedPIDs() throws IOException, InterruptedException { | 261 | public Set<Integer> getSupportedPIDs() throws IOException, InterruptedException { |
229 | Set<Integer> result = new HashSet<>(); | 262 | Set<Integer> result = new HashSet<>(); |
230 | String[] pids = new String[] {"0100", "0120", "0140", "0160"}; | 263 | String[] pids = new String[] {"0100", "0120", "0140", "0160"}; |
231 | int basePid = 0; | 264 | int basePid = 1; |
232 | for (String pid : pids) { | 265 | for (String pid : pids) { |
233 | int[] responseData = run(pid); | 266 | int[] responseData = run(pid); |
234 | if (responseData.length >= 6) { | 267 | if (responseData.length >= 6) { |
@@ -236,11 +269,19 @@ public class Obd2Connection { | |||
236 | byte byte1 = (byte) (responseData[3] & 0xFF); | 269 | byte byte1 = (byte) (responseData[3] & 0xFF); |
237 | byte byte2 = (byte) (responseData[4] & 0xFF); | 270 | byte byte2 = (byte) (responseData[4] & 0xFF); |
238 | byte byte3 = (byte) (responseData[5] & 0xFF); | 271 | byte byte3 = (byte) (responseData[5] & 0xFF); |
272 | if (DBG) { | ||
273 | Log.i(TAG, String.format("supported PID at base %d payload %02X%02X%02X%02X", | ||
274 | basePid, byte0, byte1, byte2, byte3)); | ||
275 | } | ||
239 | FourByteBitSet fourByteBitSet = new FourByteBitSet(byte0, byte1, byte2, byte3); | 276 | FourByteBitSet fourByteBitSet = new FourByteBitSet(byte0, byte1, byte2, byte3); |
240 | for (int byteIndex = 0; byteIndex < 4; ++byteIndex) { | 277 | for (int byteIndex = 0; byteIndex < 4; ++byteIndex) { |
241 | for (int bitIndex = 7; bitIndex >= 0; --bitIndex) { | 278 | for (int bitIndex = 7; bitIndex >= 0; --bitIndex) { |
242 | if (fourByteBitSet.getBit(byteIndex, bitIndex)) { | 279 | if (fourByteBitSet.getBit(byteIndex, bitIndex)) { |
243 | result.add(basePid + 8 * byteIndex + 7 - bitIndex); | 280 | int command = basePid + 8 * byteIndex + 7 - bitIndex; |
281 | if (DBG) { | ||
282 | Log.i(TAG, "command " + command + " found supported"); | ||
283 | } | ||
284 | result.add(command); | ||
244 | } | 285 | } |
245 | } | 286 | } |
246 | } | 287 | } |
@@ -250,4 +291,46 @@ public class Obd2Connection { | |||
250 | 291 | ||
251 | return result; | 292 | return result; |
252 | } | 293 | } |
294 | |||
295 | String getDiagnosticTroubleCode(IntegerArrayStream source) { | ||
296 | final char[] components = new char[] {'P', 'C', 'B', 'U'}; | ||
297 | final char[] firstDigits = new char[] {'0', '1', '2', '3'}; | ||
298 | final char[] otherDigits = | ||
299 | new char[] { | ||
300 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' | ||
301 | }; | ||
302 | |||
303 | StringBuilder builder = new StringBuilder(5); | ||
304 | |||
305 | int byte0 = source.consume(); | ||
306 | int byte1 = source.consume(); | ||
307 | |||
308 | int componentMask = (byte0 & 0xC0) >> 6; | ||
309 | int firstDigitMask = (byte0 & 0x30) >> 4; | ||
310 | int secondDigitMask = (byte0 & 0x0F); | ||
311 | int thirdDigitMask = (byte1 & 0xF0) >> 4; | ||
312 | int fourthDigitMask = (byte1 & 0x0F); | ||
313 | |||
314 | builder.append(components[componentMask]); | ||
315 | builder.append(firstDigits[firstDigitMask]); | ||
316 | builder.append(otherDigits[secondDigitMask]); | ||
317 | builder.append(otherDigits[thirdDigitMask]); | ||
318 | builder.append(otherDigits[fourthDigitMask]); | ||
319 | |||
320 | return builder.toString(); | ||
321 | } | ||
322 | |||
323 | public List<String> getDiagnosticTroubleCodes() throws IOException, InterruptedException { | ||
324 | List<String> result = new ArrayList<>(); | ||
325 | int[] response = run("03"); | ||
326 | IntegerArrayStream stream = new IntegerArrayStream(response); | ||
327 | if (stream.isEmpty()) return result; | ||
328 | if (!stream.expect(0x43)) | ||
329 | throw new IllegalArgumentException("data from remote end not a mode 3 response"); | ||
330 | int count = stream.consume(); | ||
331 | for (int i = 0; i < count; ++i) { | ||
332 | result.add(getDiagnosticTroubleCode(stream)); | ||
333 | } | ||
334 | return result; | ||
335 | } | ||
253 | } | 336 | } |
diff --git a/obd2-lib/src/com/android/car/obd2/Obd2FreezeFrameGenerator.java b/obd2-lib/src/com/android/car/obd2/Obd2FreezeFrameGenerator.java new file mode 100644 index 00000000..3de48638 --- /dev/null +++ b/obd2-lib/src/com/android/car/obd2/Obd2FreezeFrameGenerator.java | |||
@@ -0,0 +1,177 @@ | |||
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 | |||
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 | } | ||
diff --git a/obd2-lib/src/com/android/car/obd2/commands/RPM.java b/obd2-lib/src/com/android/car/obd2/commands/RPM.java index b277abfc..a062e978 100644 --- a/obd2-lib/src/com/android/car/obd2/commands/RPM.java +++ b/obd2-lib/src/com/android/car/obd2/commands/RPM.java | |||
@@ -30,7 +30,7 @@ public class RPM implements Obd2Command.OutputSemanticHandler<Integer> { | |||
30 | public Optional<Integer> consume(IntegerArrayStream data) { | 30 | public Optional<Integer> consume(IntegerArrayStream data) { |
31 | return data.hasAtLeast( | 31 | return data.hasAtLeast( |
32 | 2, | 32 | 2, |
33 | theData -> Optional.of(theData.consume() * 256 + theData.consume() / 4), | 33 | theData -> Optional.of((theData.consume() * 256 + theData.consume()) / 4), |
34 | theData -> Optional.<Integer>empty()); | 34 | theData -> Optional.<Integer>empty()); |
35 | } | 35 | } |
36 | } | 36 | } |
diff --git a/tests/obd2_app/src/com/google/android/car/obd2app/Obd2CollectionTask.java b/tests/obd2_app/src/com/google/android/car/obd2app/Obd2CollectionTask.java index 31c3db2f..b38cf30b 100644 --- a/tests/obd2_app/src/com/google/android/car/obd2app/Obd2CollectionTask.java +++ b/tests/obd2_app/src/com/google/android/car/obd2app/Obd2CollectionTask.java | |||
@@ -22,11 +22,10 @@ import android.os.Environment; | |||
22 | import android.os.SystemClock; | 22 | import android.os.SystemClock; |
23 | import android.util.JsonWriter; | 23 | import android.util.JsonWriter; |
24 | import android.util.Log; | 24 | import android.util.Log; |
25 | |||
26 | import com.android.car.obd2.Obd2Connection; | 25 | import com.android.car.obd2.Obd2Connection; |
26 | import com.android.car.obd2.Obd2FreezeFrameGenerator; | ||
27 | import com.android.car.obd2.Obd2LiveFrameGenerator; | 27 | import com.android.car.obd2.Obd2LiveFrameGenerator; |
28 | import com.android.car.obd2.connections.BluetoothConnection; | 28 | import com.android.car.obd2.connections.BluetoothConnection; |
29 | |||
30 | import java.io.File; | 29 | import java.io.File; |
31 | import java.io.FileOutputStream; | 30 | import java.io.FileOutputStream; |
32 | import java.io.IOException; | 31 | import java.io.IOException; |
@@ -37,6 +36,7 @@ import java.util.TimerTask; | |||
37 | public class Obd2CollectionTask extends TimerTask { | 36 | public class Obd2CollectionTask extends TimerTask { |
38 | private final Obd2Connection mConnection; | 37 | private final Obd2Connection mConnection; |
39 | private final Obd2LiveFrameGenerator mLiveFrameGenerator; | 38 | private final Obd2LiveFrameGenerator mLiveFrameGenerator; |
39 | private final Obd2FreezeFrameGenerator mFreezeFrameGenerator; | ||
40 | private final StatusNotification mStatusNotification; | 40 | private final StatusNotification mStatusNotification; |
41 | private final JsonWriter mJsonWriter; | 41 | private final JsonWriter mJsonWriter; |
42 | 42 | ||
@@ -79,6 +79,7 @@ public class Obd2CollectionTask extends TimerTask { | |||
79 | try { | 79 | try { |
80 | synchronized (mJsonWriter) { | 80 | synchronized (mJsonWriter) { |
81 | mLiveFrameGenerator.generate(mJsonWriter); | 81 | mLiveFrameGenerator.generate(mJsonWriter); |
82 | mFreezeFrameGenerator.generate(mJsonWriter); | ||
82 | mJsonWriter.flush(); | 83 | mJsonWriter.flush(); |
83 | } | 84 | } |
84 | mStatusNotification.notifyDataCapture(); | 85 | mStatusNotification.notifyDataCapture(); |
@@ -88,7 +89,7 @@ public class Obd2CollectionTask extends TimerTask { | |||
88 | } | 89 | } |
89 | 90 | ||
90 | Obd2CollectionTask(Context context, StatusNotification statusNotification, String deviceAddress) | 91 | Obd2CollectionTask(Context context, StatusNotification statusNotification, String deviceAddress) |
91 | throws IOException, InterruptedException { | 92 | throws IOException, InterruptedException { |
92 | if (!isExternalStorageWriteable()) | 93 | if (!isExternalStorageWriteable()) |
93 | throw new IOException("Cannot write data to external storage"); | 94 | throw new IOException("Cannot write data to external storage"); |
94 | mStatusNotification = statusNotification; | 95 | mStatusNotification = statusNotification; |
@@ -99,10 +100,11 @@ public class Obd2CollectionTask extends TimerTask { | |||
99 | } | 100 | } |
100 | mConnection = new Obd2Connection(bluetoothConnection); | 101 | mConnection = new Obd2Connection(bluetoothConnection); |
101 | mLiveFrameGenerator = new Obd2LiveFrameGenerator(mConnection); | 102 | mLiveFrameGenerator = new Obd2LiveFrameGenerator(mConnection); |
103 | mFreezeFrameGenerator = new Obd2FreezeFrameGenerator(mConnection); | ||
102 | mJsonWriter = | 104 | mJsonWriter = |
103 | new JsonWriter( | 105 | new JsonWriter( |
104 | new OutputStreamWriter( | 106 | new OutputStreamWriter( |
105 | new FileOutputStream(getFilenameForStorage(context)))); | 107 | new FileOutputStream(getFilenameForStorage(context)))); |
106 | mJsonWriter.beginArray(); | 108 | mJsonWriter.beginArray(); |
107 | } | 109 | } |
108 | 110 | ||
diff --git a/tests/obd2_test/src/com/android/car/obd2/test/IntegerArrayStreamTest.java b/tests/obd2_test/src/com/android/car/obd2/test/IntegerArrayStreamTest.java index 3c3b1e6e..e9bf98b8 100644 --- a/tests/obd2_test/src/com/android/car/obd2/test/IntegerArrayStreamTest.java +++ b/tests/obd2_test/src/com/android/car/obd2/test/IntegerArrayStreamTest.java | |||
@@ -59,4 +59,14 @@ public class IntegerArrayStreamTest { | |||
59 | assertFalse(stream.expect(4, 6)); | 59 | assertFalse(stream.expect(4, 6)); |
60 | assertEquals(5, stream.peek()); | 60 | assertEquals(5, stream.peek()); |
61 | } | 61 | } |
62 | |||
63 | @Test | ||
64 | public void testIsEmpty() { | ||
65 | IntegerArrayStream stream = new IntegerArrayStream(DATA_SET); | ||
66 | assertFalse(stream.isEmpty()); | ||
67 | stream.expect(1, 2, 3, 4, 5); | ||
68 | assertFalse(stream.isEmpty()); | ||
69 | stream.consume(); | ||
70 | assertTrue(stream.isEmpty()); | ||
71 | } | ||
62 | } | 72 | } |
diff --git a/tests/obd2_test/src/com/android/car/obd2/test/Obd2CommandTest.java b/tests/obd2_test/src/com/android/car/obd2/test/Obd2CommandTest.java index b342cbc8..f4ac13db 100644 --- a/tests/obd2_test/src/com/android/car/obd2/test/Obd2CommandTest.java +++ b/tests/obd2_test/src/com/android/car/obd2/test/Obd2CommandTest.java | |||
@@ -106,7 +106,7 @@ public class Obd2CommandTest { | |||
106 | String[] commandToSend = new String[] {String.format("02%02X 01\r", pid)}; | 106 | String[] commandToSend = new String[] {String.format("02%02X 01\r", pid)}; |
107 | 107 | ||
108 | String[] responseToGet = | 108 | String[] responseToGet = |
109 | new String[] {String.format("02 %02X 01 %s", pid, responseBytes), OBD2_PROMPT}; | 109 | new String[] {String.format("42 %02X 01 %s", pid, responseBytes), OBD2_PROMPT}; |
110 | 110 | ||
111 | MockObd2UnderlyingTransport transport = | 111 | MockObd2UnderlyingTransport transport = |
112 | new MockObd2UnderlyingTransport( | 112 | new MockObd2UnderlyingTransport( |
@@ -134,7 +134,7 @@ public class Obd2CommandTest { | |||
134 | String[] commandToSend = new String[] {String.format("02%02X 01\r", pid)}; | 134 | String[] commandToSend = new String[] {String.format("02%02X 01\r", pid)}; |
135 | 135 | ||
136 | String[] responseToGet = | 136 | String[] responseToGet = |
137 | new String[] {String.format("02 %02X 01 %s", pid, responseBytes), OBD2_PROMPT}; | 137 | new String[] {String.format("42 %02X 01 %s", pid, responseBytes), OBD2_PROMPT}; |
138 | 138 | ||
139 | MockObd2UnderlyingTransport transport = | 139 | MockObd2UnderlyingTransport transport = |
140 | new MockObd2UnderlyingTransport( | 140 | new MockObd2UnderlyingTransport( |
@@ -213,7 +213,7 @@ public class Obd2CommandTest { | |||
213 | 213 | ||
214 | @Test | 214 | @Test |
215 | public void testRpm() { | 215 | public void testRpm() { |
216 | checkCommand(0x0C, "12 0F", 4611); | 216 | checkCommand(0x0C, "12 0F", 1155); |
217 | } | 217 | } |
218 | 218 | ||
219 | @Test | 219 | @Test |
diff --git a/tests/obd2_test/src/com/android/car/obd2/test/Obd2FreezeFrameGeneratorTest.java b/tests/obd2_test/src/com/android/car/obd2/test/Obd2FreezeFrameGeneratorTest.java new file mode 100644 index 00000000..20919a14 --- /dev/null +++ b/tests/obd2_test/src/com/android/car/obd2/test/Obd2FreezeFrameGeneratorTest.java | |||
@@ -0,0 +1,114 @@ | |||
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.test; | ||
18 | |||
19 | import static android.hardware.automotive.vehicle.V2_1.VehicleProperty.OBD2_FREEZE_FRAME; | ||
20 | import static com.android.car.obd2.test.Utils.concatIntArrays; | ||
21 | import static com.android.car.obd2.test.Utils.stringsToIntArray; | ||
22 | import static org.junit.Assert.*; | ||
23 | |||
24 | import android.hardware.automotive.vehicle.V2_0.VehiclePropValue; | ||
25 | import android.util.JsonReader; | ||
26 | import android.util.JsonWriter; | ||
27 | import com.android.car.obd2.Obd2Connection; | ||
28 | import com.android.car.obd2.Obd2FreezeFrameGenerator; | ||
29 | import com.android.car.vehiclehal.DiagnosticJsonReader; | ||
30 | import java.io.StringReader; | ||
31 | import java.io.StringWriter; | ||
32 | import org.junit.Test; | ||
33 | |||
34 | public class Obd2FreezeFrameGeneratorTest { | ||
35 | private static final String[] EXPECTED_INIT_COMMANDS = | ||
36 | new String[] { | ||
37 | "ATD\r", "ATZ\r", "AT E0\r", "AT L0\r", "AT S0\r", "AT H0\r", "AT SP 0\r" | ||
38 | }; | ||
39 | |||
40 | private static final String OBD2_PROMPT = ">"; | ||
41 | |||
42 | private static final String[] EXPECTED_INIT_RESPONSES = | ||
43 | new String[] { | ||
44 | OBD2_PROMPT, | ||
45 | OBD2_PROMPT, | ||
46 | OBD2_PROMPT, | ||
47 | OBD2_PROMPT, | ||
48 | OBD2_PROMPT, | ||
49 | OBD2_PROMPT, | ||
50 | OBD2_PROMPT | ||
51 | }; | ||
52 | |||
53 | private static final String[] EXPECTED_DISCOVERY_COMMANDS = | ||
54 | new String[] {"0100\r", "0120\r", "0140\r", "0160\r"}; | ||
55 | |||
56 | private static final String[] EXPECTED_DISCOVERY_RESPONSES = | ||
57 | new String[] {"00 00 00 18 00 00", OBD2_PROMPT, OBD2_PROMPT, OBD2_PROMPT, OBD2_PROMPT}; | ||
58 | |||
59 | private static final String[] EXPECTED_MODE3_COMMANDS = new String[] {"03\r"}; | ||
60 | |||
61 | private static final String[] EXPECTED_MODE3_RESPONSES = | ||
62 | new String[] { | ||
63 | "0300E0:4306010002001:030043008200C12:0000000000000043010101", OBD2_PROMPT | ||
64 | }; | ||
65 | |||
66 | private static final String[] EXPECTED_FRAME_COMMANDS = | ||
67 | new String[] {"020C 00\r", "020D 00\r", "020C 01\r", "020D 01\r"}; | ||
68 | |||
69 | private static final String[] EXPECTED_FRAME_RESPONSES = | ||
70 | new String[] { | ||
71 | "42 0C 00 12 0F", | ||
72 | OBD2_PROMPT, | ||
73 | "42 0D 00 82", | ||
74 | OBD2_PROMPT, | ||
75 | "42 0C 01 12 0F", | ||
76 | OBD2_PROMPT, | ||
77 | "42 0D 01 83", | ||
78 | OBD2_PROMPT | ||
79 | }; | ||
80 | |||
81 | @Test | ||
82 | public void testObd2FreezeFrameGeneration() throws Exception { | ||
83 | MockObd2UnderlyingTransport transport = | ||
84 | new MockObd2UnderlyingTransport( | ||
85 | concatIntArrays( | ||
86 | stringsToIntArray(EXPECTED_INIT_COMMANDS), | ||
87 | stringsToIntArray(EXPECTED_DISCOVERY_COMMANDS), | ||
88 | stringsToIntArray(EXPECTED_MODE3_COMMANDS), | ||
89 | stringsToIntArray(EXPECTED_FRAME_COMMANDS)), | ||
90 | concatIntArrays( | ||
91 | stringsToIntArray(EXPECTED_INIT_RESPONSES), | ||
92 | stringsToIntArray(EXPECTED_DISCOVERY_RESPONSES), | ||
93 | stringsToIntArray(EXPECTED_MODE3_RESPONSES), | ||
94 | stringsToIntArray(EXPECTED_FRAME_RESPONSES))); | ||
95 | Obd2Connection obd2Connection = new Obd2Connection(transport); | ||
96 | Obd2FreezeFrameGenerator obd2Generator = new Obd2FreezeFrameGenerator(obd2Connection); | ||
97 | StringWriter stringWriter = new StringWriter(1024); | ||
98 | JsonWriter jsonWriter = new JsonWriter(stringWriter); | ||
99 | jsonWriter.beginArray(); | ||
100 | obd2Generator.generate(jsonWriter); | ||
101 | jsonWriter.endArray(); | ||
102 | JsonReader jsonReader = new JsonReader(new StringReader(stringWriter.toString())); | ||
103 | DiagnosticJsonReader diagnosticJsonReader = new DiagnosticJsonReader(); | ||
104 | jsonReader.beginArray(); | ||
105 | VehiclePropValue vehiclePropValue = diagnosticJsonReader.build(jsonReader); | ||
106 | assertEquals(OBD2_FREEZE_FRAME, vehiclePropValue.prop); | ||
107 | assertEquals(1155, (long) vehiclePropValue.value.int32Values.get(0xC)); | ||
108 | assertEquals(130, (long) vehiclePropValue.value.int32Values.get(0xD)); | ||
109 | vehiclePropValue = diagnosticJsonReader.build(jsonReader); | ||
110 | assertEquals(OBD2_FREEZE_FRAME, vehiclePropValue.prop); | ||
111 | assertEquals(1155, (long) vehiclePropValue.value.int32Values.get(0xC)); | ||
112 | assertEquals(131, (long) vehiclePropValue.value.int32Values.get(0xD)); | ||
113 | } | ||
114 | } | ||
diff --git a/tests/obd2_test/src/com/android/car/obd2/test/Obd2LiveFrameGeneratorTest.java b/tests/obd2_test/src/com/android/car/obd2/test/Obd2LiveFrameGeneratorTest.java index e2022c1f..ba3dbb81 100644 --- a/tests/obd2_test/src/com/android/car/obd2/test/Obd2LiveFrameGeneratorTest.java +++ b/tests/obd2_test/src/com/android/car/obd2/test/Obd2LiveFrameGeneratorTest.java | |||
@@ -54,7 +54,7 @@ public class Obd2LiveFrameGeneratorTest { | |||
54 | new String[] {"0100\r", "0120\r", "0140\r", "0160\r"}; | 54 | new String[] {"0100\r", "0120\r", "0140\r", "0160\r"}; |
55 | 55 | ||
56 | private static final String[] EXPECTED_DISCOVERY_RESPONSES = | 56 | private static final String[] EXPECTED_DISCOVERY_RESPONSES = |
57 | new String[] {"00 00 00 0C 00 00", OBD2_PROMPT, OBD2_PROMPT, OBD2_PROMPT, OBD2_PROMPT}; | 57 | new String[] {"00 00 00 18 00 00", OBD2_PROMPT, OBD2_PROMPT, OBD2_PROMPT, OBD2_PROMPT}; |
58 | 58 | ||
59 | private static final String[] EXPECTED_FRAME_COMMANDS = new String[] {"010C\r", "010D\r"}; | 59 | private static final String[] EXPECTED_FRAME_COMMANDS = new String[] {"010C\r", "010D\r"}; |
60 | 60 | ||
@@ -82,7 +82,7 @@ public class Obd2LiveFrameGeneratorTest { | |||
82 | DiagnosticJsonReader diagnosticJsonReader = new DiagnosticJsonReader(); | 82 | DiagnosticJsonReader diagnosticJsonReader = new DiagnosticJsonReader(); |
83 | VehiclePropValue vehiclePropValue = diagnosticJsonReader.build(jsonReader); | 83 | VehiclePropValue vehiclePropValue = diagnosticJsonReader.build(jsonReader); |
84 | assertEquals(OBD2_LIVE_FRAME, vehiclePropValue.prop); | 84 | assertEquals(OBD2_LIVE_FRAME, vehiclePropValue.prop); |
85 | assertEquals(4611, (long) vehiclePropValue.value.int32Values.get(0xC)); | 85 | assertEquals(1155, (long) vehiclePropValue.value.int32Values.get(0xC)); |
86 | assertEquals(130, (long) vehiclePropValue.value.int32Values.get(0xD)); | 86 | assertEquals(130, (long) vehiclePropValue.value.int32Values.get(0xD)); |
87 | } | 87 | } |
88 | } | 88 | } |