| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.car.obd2; |
| |
| import com.android.car.obd2.commands.AmbientAirTemperature; |
| import com.android.car.obd2.commands.CalculatedEngineLoad; |
| import com.android.car.obd2.commands.EngineCoolantTemperature; |
| import com.android.car.obd2.commands.EngineOilTemperature; |
| import com.android.car.obd2.commands.EngineRuntime; |
| import com.android.car.obd2.commands.FuelGaugePressure; |
| import com.android.car.obd2.commands.FuelSystemStatus; |
| import com.android.car.obd2.commands.FuelTankLevel; |
| import com.android.car.obd2.commands.FuelTrimCommand.Bank1LongTermFuelTrimCommand; |
| import com.android.car.obd2.commands.FuelTrimCommand.Bank1ShortTermFuelTrimCommand; |
| import com.android.car.obd2.commands.FuelTrimCommand.Bank2LongTermFuelTrimCommand; |
| import com.android.car.obd2.commands.FuelTrimCommand.Bank2ShortTermFuelTrimCommand; |
| import com.android.car.obd2.commands.RPM; |
| import com.android.car.obd2.commands.Speed; |
| import com.android.car.obd2.commands.ThrottlePosition; |
| import java.io.IOException; |
| import java.util.HashMap; |
| import java.util.Objects; |
| import java.util.Optional; |
| import java.util.Set; |
| |
| /** |
| * Base class of OBD2 command objects that query a "vehicle" and return an individual data point |
| * represented as a Java type. |
| * |
| * @param <ValueType> The Java type that represents the value of this command's output. |
| */ |
| public abstract class Obd2Command<ValueType> { |
| |
| /** |
| * Abstract representation of an object whose job it is to receive the bytes read from the OBD2 |
| * connection and return a Java representation of a command's value. |
| * |
| * @param <ValueType> |
| */ |
| public interface OutputSemanticHandler<ValueType> { |
| int getPid(); |
| |
| Optional<ValueType> consume(IntegerArrayStream data); |
| } |
| |
| public static final int LIVE_FRAME = 1; |
| public static final int FREEZE_FRAME = 2; |
| |
| private static final HashMap<Integer, OutputSemanticHandler<Integer>> |
| SUPPORTED_INTEGER_COMMANDS = new HashMap<>(); |
| private static final HashMap<Integer, OutputSemanticHandler<Float>> SUPPORTED_FLOAT_COMMANDS = |
| new HashMap<>(); |
| |
| private static void addSupportedIntegerCommands( |
| OutputSemanticHandler<Integer>... integerOutputSemanticHandlers) { |
| for (OutputSemanticHandler<Integer> integerOutputSemanticHandler : |
| integerOutputSemanticHandlers) { |
| SUPPORTED_INTEGER_COMMANDS.put( |
| integerOutputSemanticHandler.getPid(), integerOutputSemanticHandler); |
| } |
| } |
| |
| private static void addSupportedFloatCommands( |
| OutputSemanticHandler<Float>... floatOutputSemanticHandlers) { |
| for (OutputSemanticHandler<Float> floatOutputSemanticHandler : |
| floatOutputSemanticHandlers) { |
| SUPPORTED_FLOAT_COMMANDS.put( |
| floatOutputSemanticHandler.getPid(), floatOutputSemanticHandler); |
| } |
| } |
| |
| public static Set<Integer> getSupportedIntegerCommands() { |
| return SUPPORTED_INTEGER_COMMANDS.keySet(); |
| } |
| |
| public static Set<Integer> getSupportedFloatCommands() { |
| return SUPPORTED_FLOAT_COMMANDS.keySet(); |
| } |
| |
| public static OutputSemanticHandler<Integer> getIntegerCommand(int pid) { |
| return SUPPORTED_INTEGER_COMMANDS.get(pid); |
| } |
| |
| public static OutputSemanticHandler<Float> getFloatCommand(int pid) { |
| return SUPPORTED_FLOAT_COMMANDS.get(pid); |
| } |
| |
| static { |
| addSupportedFloatCommands( |
| new AmbientAirTemperature(), |
| new CalculatedEngineLoad(), |
| new FuelTankLevel(), |
| new Bank2ShortTermFuelTrimCommand(), |
| new Bank2LongTermFuelTrimCommand(), |
| new Bank1LongTermFuelTrimCommand(), |
| new Bank1ShortTermFuelTrimCommand(), |
| new ThrottlePosition()); |
| addSupportedIntegerCommands( |
| new EngineOilTemperature(), |
| new EngineCoolantTemperature(), |
| new FuelGaugePressure(), |
| new FuelSystemStatus(), |
| new RPM(), |
| new EngineRuntime(), |
| new Speed()); |
| } |
| |
| protected final int mMode; |
| protected final OutputSemanticHandler<ValueType> mSemanticHandler; |
| |
| Obd2Command(int mode, OutputSemanticHandler<ValueType> semanticHandler) { |
| mMode = mode; |
| mSemanticHandler = Objects.requireNonNull(semanticHandler); |
| } |
| |
| public abstract Optional<ValueType> run(Obd2Connection connection) throws Exception; |
| |
| public int getPid() { |
| return mSemanticHandler.getPid(); |
| } |
| |
| public static final <T> LiveFrameCommand<T> getLiveFrameCommand(OutputSemanticHandler handler) { |
| return new LiveFrameCommand<>(handler); |
| } |
| |
| public static final <T> FreezeFrameCommand<T> getFreezeFrameCommand( |
| OutputSemanticHandler handler, int frameId) { |
| return new FreezeFrameCommand<>(handler, frameId); |
| } |
| |
| /** |
| * An OBD2 command that returns live frame data. |
| * |
| * @param <ValueType> The Java type that represents the command's result type. |
| */ |
| public static class LiveFrameCommand<ValueType> extends Obd2Command<ValueType> { |
| private static final int RESPONSE_MARKER = 0x41; |
| |
| LiveFrameCommand(OutputSemanticHandler<ValueType> semanticHandler) { |
| super(LIVE_FRAME, semanticHandler); |
| } |
| |
| public Optional<ValueType> run(Obd2Connection connection) |
| throws IOException, InterruptedException { |
| String command = String.format("%02X%02X", mMode, mSemanticHandler.getPid()); |
| int[] data = connection.run(command); |
| IntegerArrayStream stream = new IntegerArrayStream(data); |
| if (stream.expect(RESPONSE_MARKER, mSemanticHandler.getPid())) { |
| return mSemanticHandler.consume(stream); |
| } |
| return Optional.empty(); |
| } |
| } |
| |
| /** |
| * An OBD2 command that returns freeze frame data. |
| * |
| * @param <ValueType> The Java type that represents the command's result type. |
| */ |
| public static class FreezeFrameCommand<ValueType> extends Obd2Command<ValueType> { |
| private static final int RESPONSE_MARKER = 0x42; |
| |
| private int mFrameId; |
| |
| FreezeFrameCommand(OutputSemanticHandler<ValueType> semanticHandler, int frameId) { |
| super(FREEZE_FRAME, semanticHandler); |
| mFrameId = frameId; |
| } |
| |
| public Optional<ValueType> run(Obd2Connection connection) |
| throws IOException, InterruptedException { |
| String command = |
| String.format("%02X%02X %02X", mMode, mSemanticHandler.getPid(), mFrameId); |
| int[] data = connection.run(command); |
| IntegerArrayStream stream = new IntegerArrayStream(data); |
| if (stream.expect(RESPONSE_MARKER, mSemanticHandler.getPid(), mFrameId)) { |
| return mSemanticHandler.consume(stream); |
| } |
| return Optional.empty(); |
| } |
| } |
| } |