blob: 30fca0c1d1dee760462d60bb6c457cf03610b3cb [file] [log] [blame]
/*
* 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();
}
}
}