1759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata/* 2759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * Copyright (C) 2017 The Android Open Source Project 3759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * 4759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * Licensed under the Apache License, Version 2.0 (the "License"); 5759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * you may not use this file except in compliance with the License. 6759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * You may obtain a copy of the License at 7759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * 8759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * http://www.apache.org/licenses/LICENSE-2.0 9759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * 10759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * Unless required by applicable law or agreed to in writing, software 11759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * distributed under the License is distributed on an "AS IS" BASIS, 12759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * See the License for the specific language governing permissions and 14759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * limitations under the License. 15759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata */ 16759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 17759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granatapackage com.android.car.obd2; 18759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 19759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granataimport com.android.car.obd2.commands.AmbientAirTemperature; 2024475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.CalculatedEngineLoad; 2124475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.EngineCoolantTemperature; 22759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granataimport com.android.car.obd2.commands.EngineOilTemperature; 2324475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.EngineRuntime; 2424475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.FuelGaugePressure; 2524475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.FuelSystemStatus; 2624475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.FuelTankLevel; 2724475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.FuelTrimCommand.Bank1LongTermFuelTrimCommand; 2824475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.FuelTrimCommand.Bank1ShortTermFuelTrimCommand; 2924475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.FuelTrimCommand.Bank2LongTermFuelTrimCommand; 3024475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.FuelTrimCommand.Bank2ShortTermFuelTrimCommand; 3124475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.RPM; 3224475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.Speed; 3324475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.ThrottlePosition; 34759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granataimport java.io.IOException; 35759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granataimport java.util.HashMap; 3624475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport java.util.Objects; 37759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granataimport java.util.Optional; 38759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granataimport java.util.Set; 39759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 40759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata/** 41759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * Base class of OBD2 command objects that query a "vehicle" and return an individual data point 42759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * represented as a Java type. 43759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * 44759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * @paramThe Java type that represents the value of this command's output. 45759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata */ 46759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granatapublic abstract class Obd2Command<ValueType> { 47759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 48759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata /** 49759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * Abstract representation of an object whose job it is to receive the bytes read from the OBD2 50759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * connection and return a Java representation of a command's value. 51759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * 52759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * @param 53759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata */ 54759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata public interface OutputSemanticHandler<ValueType> { 55759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata int getPid(); 56759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 57759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata Optional<ValueType> consume(IntegerArrayStream data); 58759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata } 59759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 60759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata public static final int LIVE_FRAME = 1; 61759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata public static final int FREEZE_FRAME = 2; 62759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 63759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata private static final HashMap<Integer, OutputSemanticHandler<Integer>> 64759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata SUPPORTED_INTEGER_COMMANDS = new HashMap<>(); 65759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata private static final HashMap<Integer, OutputSemanticHandler<Float>> SUPPORTED_FLOAT_COMMANDS = 66759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata new HashMap<>(); 67759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 6824475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata private static void addSupportedIntegerCommands( 6924475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata OutputSemanticHandler<Integer>... integerOutputSemanticHandlers) { 7024475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata for (OutputSemanticHandler<Integer> integerOutputSemanticHandler : 7124475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata integerOutputSemanticHandlers) { 7224475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata SUPPORTED_INTEGER_COMMANDS.put( 7324475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata integerOutputSemanticHandler.getPid(), integerOutputSemanticHandler); 7424475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata } 75759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata } 76759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 7724475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata private static void addSupportedFloatCommands( 7824475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata OutputSemanticHandler<Float>... floatOutputSemanticHandlers) { 7924475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata for (OutputSemanticHandler<Float> floatOutputSemanticHandler : 8024475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata floatOutputSemanticHandlers) { 8124475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata SUPPORTED_FLOAT_COMMANDS.put( 8224475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata floatOutputSemanticHandler.getPid(), floatOutputSemanticHandler); 8324475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata } 84759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata } 85759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 86759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata public static Set<Integer> getSupportedIntegerCommands() { 87759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata return SUPPORTED_INTEGER_COMMANDS.keySet(); 88759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata } 89759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 90759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata public static Set<Integer> getSupportedFloatCommands() { 91759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata return SUPPORTED_FLOAT_COMMANDS.keySet(); 92759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata } 93759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 94759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata public static OutputSemanticHandler<Integer> getIntegerCommand(int pid) { 95759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata return SUPPORTED_INTEGER_COMMANDS.get(pid); 96759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata } 97759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 98759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata public static OutputSemanticHandler<Float> getFloatCommand(int pid) { 99759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata return SUPPORTED_FLOAT_COMMANDS.get(pid); 100759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata } 101759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 102759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata static { 10324475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata addSupportedFloatCommands( 10424475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata new AmbientAirTemperature(), 10524475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata new CalculatedEngineLoad(), 10624475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata new FuelTankLevel(), 10724475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata new Bank2ShortTermFuelTrimCommand(), 10824475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata new Bank2LongTermFuelTrimCommand(), 10924475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata new Bank1LongTermFuelTrimCommand(), 11024475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata new Bank1ShortTermFuelTrimCommand(), 11124475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata new ThrottlePosition()); 11224475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata addSupportedIntegerCommands( 11324475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata new EngineOilTemperature(), 11424475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata new EngineCoolantTemperature(), 11524475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata new FuelGaugePressure(), 11624475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata new FuelSystemStatus(), 11724475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata new RPM(), 11824475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata new EngineRuntime(), 11924475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata new Speed()); 120759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata } 121759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 122759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata protected final int mMode; 123759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata protected final OutputSemanticHandler<ValueType> mSemanticHandler; 124759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 125759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata Obd2Command(int mode, OutputSemanticHandler<ValueType> semanticHandler) { 126759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata mMode = mode; 12724475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata mSemanticHandler = Objects.requireNonNull(semanticHandler); 128759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata } 129759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 130759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata public abstract Optional<ValueType> run(Obd2Connection connection) throws Exception; 131759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 13287c0545b6f0814657df555902300d7b3dfe6b96fEnrico Granata public int getPid() { 13387c0545b6f0814657df555902300d7b3dfe6b96fEnrico Granata return mSemanticHandler.getPid(); 13487c0545b6f0814657df555902300d7b3dfe6b96fEnrico Granata } 13587c0545b6f0814657df555902300d7b3dfe6b96fEnrico Granata 136759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata public static final <T> LiveFrameCommand<T> getLiveFrameCommand(OutputSemanticHandler handler) { 137759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata return new LiveFrameCommand<>(handler); 138759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata } 139759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 140759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata public static final <T> FreezeFrameCommand<T> getFreezeFrameCommand( 141759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata OutputSemanticHandler handler, int frameId) { 142759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata return new FreezeFrameCommand<>(handler, frameId); 143759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata } 144759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 145759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata /** 146759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * An OBD2 command that returns live frame data. 147759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * 148759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * @param The Java type that represents the command's result type. 149759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata */ 150759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata public static class LiveFrameCommand<ValueType> extends Obd2Command<ValueType> { 151759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata private static final int RESPONSE_MARKER = 0x41; 152759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 153759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata LiveFrameCommand(OutputSemanticHandler<ValueType> semanticHandler) { 154759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata super(LIVE_FRAME, semanticHandler); 155759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata } 156759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 157759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata public Optional<ValueType> run(Obd2Connection connection) 158759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata throws IOException, InterruptedException { 159759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata String command = String.format("%02X%02X", mMode, mSemanticHandler.getPid()); 160759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata int[] data = connection.run(command); 161759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata IntegerArrayStream stream = new IntegerArrayStream(data); 162759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata if (stream.expect(RESPONSE_MARKER, mSemanticHandler.getPid())) { 163759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata return mSemanticHandler.consume(stream); 164759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata } 165759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata return Optional.empty(); 166759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata } 167759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata } 168759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 169759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata /** 170759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * An OBD2 command that returns freeze frame data. 171759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * 172759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * @param The Java type that represents the command's result type. 173759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata */ 174759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata public static class FreezeFrameCommand<ValueType> extends Obd2Command<ValueType> { 1751d59008988765a4da37c87c9099e2ae0fc661ad6Enrico Granata private static final int RESPONSE_MARKER = 0x42; 176759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 177759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata private int mFrameId; 178759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 179759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata FreezeFrameCommand(OutputSemanticHandler<ValueType> semanticHandler, int frameId) { 180759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata super(FREEZE_FRAME, semanticHandler); 181759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata mFrameId = frameId; 182759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata } 183759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata 184759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata public Optional<ValueType> run(Obd2Connection connection) 185759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata throws IOException, InterruptedException { 186759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata String command = 187759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata String.format("%02X%02X %02X", mMode, mSemanticHandler.getPid(), mFrameId); 188759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata int[] data = connection.run(command); 189759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata IntegerArrayStream stream = new IntegerArrayStream(data); 190759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata if (stream.expect(RESPONSE_MARKER, mSemanticHandler.getPid(), mFrameId)) { 191759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata return mSemanticHandler.consume(stream); 192759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata } 193759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata return Optional.empty(); 194759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata } 195759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata } 196759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata} 197