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