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 com.android.car.obd2.commands.AmbientAirTemperature; 20 import com.android.car.obd2.commands.CalculatedEngineLoad; 21 import com.android.car.obd2.commands.EngineCoolantTemperature; 22 import com.android.car.obd2.commands.EngineOilTemperature; 23 import com.android.car.obd2.commands.EngineRuntime; 24 import com.android.car.obd2.commands.FuelGaugePressure; 25 import com.android.car.obd2.commands.FuelSystemStatus; 26 import com.android.car.obd2.commands.FuelTankLevel; 27 import com.android.car.obd2.commands.FuelTrimCommand.Bank1LongTermFuelTrimCommand; 28 import com.android.car.obd2.commands.FuelTrimCommand.Bank1ShortTermFuelTrimCommand; 29 import com.android.car.obd2.commands.FuelTrimCommand.Bank2LongTermFuelTrimCommand; 30 import com.android.car.obd2.commands.FuelTrimCommand.Bank2ShortTermFuelTrimCommand; 31 import com.android.car.obd2.commands.RPM; 32 import com.android.car.obd2.commands.Speed; 33 import com.android.car.obd2.commands.ThrottlePosition; 34 35 import java.io.IOException; 36 import java.util.HashMap; 37 import java.util.Objects; 38 import java.util.Optional; 39 import java.util.Set; 40 41 /** 42 * Base class of OBD2 command objects that query a "vehicle" and return an individual data point 43 * represented as a Java type. 44 * 45 * @param <ValueType> The Java type that represents the value of this command's output. 46 */ 47 public abstract class Obd2Command<ValueType> { 48 49 /** 50 * Abstract representation of an object whose job it is to receive the bytes read from the OBD2 51 * connection and return a Java representation of a command's value. 52 * 53 * @param <ValueType> 54 */ 55 public interface OutputSemanticHandler<ValueType> { getPid()56 int getPid(); 57 consume(IntegerArrayStream data)58 Optional<ValueType> consume(IntegerArrayStream data); 59 } 60 61 public static final int LIVE_FRAME = 1; 62 public static final int FREEZE_FRAME = 2; 63 64 private static final HashMap<Integer, OutputSemanticHandler<Integer>> 65 SUPPORTED_INTEGER_COMMANDS = new HashMap<>(); 66 private static final HashMap<Integer, OutputSemanticHandler<Float>> SUPPORTED_FLOAT_COMMANDS = 67 new HashMap<>(); 68 addSupportedIntegerCommands( OutputSemanticHandler<Integer>.... integerOutputSemanticHandlers)69 private static void addSupportedIntegerCommands( 70 OutputSemanticHandler<Integer>... integerOutputSemanticHandlers) { 71 for (OutputSemanticHandler<Integer> integerOutputSemanticHandler : 72 integerOutputSemanticHandlers) { 73 SUPPORTED_INTEGER_COMMANDS.put( 74 integerOutputSemanticHandler.getPid(), integerOutputSemanticHandler); 75 } 76 } 77 addSupportedFloatCommands( OutputSemanticHandler<Float>.... floatOutputSemanticHandlers)78 private static void addSupportedFloatCommands( 79 OutputSemanticHandler<Float>... floatOutputSemanticHandlers) { 80 for (OutputSemanticHandler<Float> floatOutputSemanticHandler : 81 floatOutputSemanticHandlers) { 82 SUPPORTED_FLOAT_COMMANDS.put( 83 floatOutputSemanticHandler.getPid(), floatOutputSemanticHandler); 84 } 85 } 86 getSupportedIntegerCommands()87 public static Set<Integer> getSupportedIntegerCommands() { 88 return SUPPORTED_INTEGER_COMMANDS.keySet(); 89 } 90 getSupportedFloatCommands()91 public static Set<Integer> getSupportedFloatCommands() { 92 return SUPPORTED_FLOAT_COMMANDS.keySet(); 93 } 94 getIntegerCommand(int pid)95 public static OutputSemanticHandler<Integer> getIntegerCommand(int pid) { 96 return SUPPORTED_INTEGER_COMMANDS.get(pid); 97 } 98 getFloatCommand(int pid)99 public static OutputSemanticHandler<Float> getFloatCommand(int pid) { 100 return SUPPORTED_FLOAT_COMMANDS.get(pid); 101 } 102 103 static { addSupportedFloatCommands( new AmbientAirTemperature(), new CalculatedEngineLoad(), new FuelTankLevel(), new Bank2ShortTermFuelTrimCommand(), new Bank2LongTermFuelTrimCommand(), new Bank1LongTermFuelTrimCommand(), new Bank1ShortTermFuelTrimCommand(), new ThrottlePosition())104 addSupportedFloatCommands( 105 new AmbientAirTemperature(), 106 new CalculatedEngineLoad(), 107 new FuelTankLevel(), 108 new Bank2ShortTermFuelTrimCommand(), 109 new Bank2LongTermFuelTrimCommand(), 110 new Bank1LongTermFuelTrimCommand(), 111 new Bank1ShortTermFuelTrimCommand(), 112 new ThrottlePosition()); addSupportedIntegerCommands( new EngineOilTemperature(), new EngineCoolantTemperature(), new FuelGaugePressure(), new FuelSystemStatus(), new RPM(), new EngineRuntime(), new Speed())113 addSupportedIntegerCommands( 114 new EngineOilTemperature(), 115 new EngineCoolantTemperature(), 116 new FuelGaugePressure(), 117 new FuelSystemStatus(), 118 new RPM(), 119 new EngineRuntime(), 120 new Speed()); 121 } 122 123 protected final int mMode; 124 protected final OutputSemanticHandler<ValueType> mSemanticHandler; 125 Obd2Command(int mode, OutputSemanticHandler<ValueType> semanticHandler)126 Obd2Command(int mode, OutputSemanticHandler<ValueType> semanticHandler) { 127 mMode = mode; 128 mSemanticHandler = Objects.requireNonNull(semanticHandler); 129 } 130 run(Obd2Connection connection)131 public abstract Optional<ValueType> run(Obd2Connection connection) throws Exception; 132 getPid()133 public int getPid() { 134 return mSemanticHandler.getPid(); 135 } 136 getLiveFrameCommand(OutputSemanticHandler handler)137 public static final <T> LiveFrameCommand<T> getLiveFrameCommand(OutputSemanticHandler handler) { 138 return new LiveFrameCommand<>(handler); 139 } 140 getFreezeFrameCommand( OutputSemanticHandler handler, int frameId)141 public static final <T> FreezeFrameCommand<T> getFreezeFrameCommand( 142 OutputSemanticHandler handler, int frameId) { 143 return new FreezeFrameCommand<>(handler, frameId); 144 } 145 146 /** 147 * An OBD2 command that returns live frame data. 148 * 149 * @param <ValueType> The Java type that represents the command's result type. 150 */ 151 public static class LiveFrameCommand<ValueType> extends Obd2Command<ValueType> { 152 private static final int RESPONSE_MARKER = 0x41; 153 LiveFrameCommand(OutputSemanticHandler<ValueType> semanticHandler)154 LiveFrameCommand(OutputSemanticHandler<ValueType> semanticHandler) { 155 super(LIVE_FRAME, semanticHandler); 156 } 157 run(Obd2Connection connection)158 public Optional<ValueType> run(Obd2Connection connection) 159 throws IOException, InterruptedException { 160 String command = String.format("%02X%02X", mMode, mSemanticHandler.getPid()); 161 int[] data = connection.run(command); 162 IntegerArrayStream stream = new IntegerArrayStream(data); 163 if (stream.expect(RESPONSE_MARKER, mSemanticHandler.getPid())) { 164 return mSemanticHandler.consume(stream); 165 } 166 return Optional.empty(); 167 } 168 } 169 170 /** 171 * An OBD2 command that returns freeze frame data. 172 * 173 * @param <ValueType> The Java type that represents the command's result type. 174 */ 175 public static class FreezeFrameCommand<ValueType> extends Obd2Command<ValueType> { 176 private static final int RESPONSE_MARKER = 0x42; 177 178 private int mFrameId; 179 FreezeFrameCommand(OutputSemanticHandler<ValueType> semanticHandler, int frameId)180 FreezeFrameCommand(OutputSemanticHandler<ValueType> semanticHandler, int frameId) { 181 super(FREEZE_FRAME, semanticHandler); 182 mFrameId = frameId; 183 } 184 run(Obd2Connection connection)185 public Optional<ValueType> run(Obd2Connection connection) 186 throws IOException, InterruptedException { 187 String command = 188 String.format("%02X%02X %02X", mMode, mSemanticHandler.getPid(), mFrameId); 189 int[] data = connection.run(command); 190 IntegerArrayStream stream = new IntegerArrayStream(data); 191 if (stream.expect(RESPONSE_MARKER, mSemanticHandler.getPid(), mFrameId)) { 192 return mSemanticHandler.consume(stream); 193 } 194 return Optional.empty(); 195 } 196 } 197 } 198