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 * @param <ValueType> The 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 <ValueType> 53 */ 54 public interface OutputSemanticHandler<ValueType> { getPid()55 int getPid(); 56 consume(IntegerArrayStream data)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 addSupportedIntegerCommands( OutputSemanticHandler<Integer>.... integerOutputSemanticHandlers)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 addSupportedFloatCommands( OutputSemanticHandler<Float>.... floatOutputSemanticHandlers)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 getSupportedIntegerCommands()86 public static Set<Integer> getSupportedIntegerCommands() { 87 return SUPPORTED_INTEGER_COMMANDS.keySet(); 88 } 89 getSupportedFloatCommands()90 public static Set<Integer> getSupportedFloatCommands() { 91 return SUPPORTED_FLOAT_COMMANDS.keySet(); 92 } 93 getIntegerCommand(int pid)94 public static OutputSemanticHandler<Integer> getIntegerCommand(int pid) { 95 return SUPPORTED_INTEGER_COMMANDS.get(pid); 96 } 97 getFloatCommand(int pid)98 public static OutputSemanticHandler<Float> getFloatCommand(int pid) { 99 return SUPPORTED_FLOAT_COMMANDS.get(pid); 100 } 101 102 static { addSupportedFloatCommands( new AmbientAirTemperature(), new CalculatedEngineLoad(), new FuelTankLevel(), new Bank2ShortTermFuelTrimCommand(), new Bank2LongTermFuelTrimCommand(), new Bank1LongTermFuelTrimCommand(), new Bank1ShortTermFuelTrimCommand(), new ThrottlePosition())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()); addSupportedIntegerCommands( new EngineOilTemperature(), new EngineCoolantTemperature(), new FuelGaugePressure(), new FuelSystemStatus(), new RPM(), new EngineRuntime(), new Speed())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 Obd2Command(int mode, OutputSemanticHandler<ValueType> semanticHandler)125 Obd2Command(int mode, OutputSemanticHandler<ValueType> semanticHandler) { 126 mMode = mode; 127 mSemanticHandler = Objects.requireNonNull(semanticHandler); 128 } 129 run(Obd2Connection connection)130 public abstract Optional<ValueType> run(Obd2Connection connection) throws Exception; 131 getPid()132 public int getPid() { 133 return mSemanticHandler.getPid(); 134 } 135 getLiveFrameCommand(OutputSemanticHandler handler)136 public static final <T> LiveFrameCommand<T> getLiveFrameCommand(OutputSemanticHandler handler) { 137 return new LiveFrameCommand<>(handler); 138 } 139 getFreezeFrameCommand( OutputSemanticHandler handler, int frameId)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 <ValueType> 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 LiveFrameCommand(OutputSemanticHandler<ValueType> semanticHandler)153 LiveFrameCommand(OutputSemanticHandler<ValueType> semanticHandler) { 154 super(LIVE_FRAME, semanticHandler); 155 } 156 run(Obd2Connection connection)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 <ValueType> 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 FreezeFrameCommand(OutputSemanticHandler<ValueType> semanticHandler, int frameId)179 FreezeFrameCommand(OutputSemanticHandler<ValueType> semanticHandler, int frameId) { 180 super(FREEZE_FRAME, semanticHandler); 181 mFrameId = frameId; 182 } 183 run(Obd2Connection connection)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