1759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata/*
2759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * Copyright (C) 2017 The Android Open Source Project
3759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata *
4759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * Licensed under the Apache License, Version 2.0 (the "License");
5759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * you may not use this file except in compliance with the License.
6759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * You may obtain a copy of the License at
7759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata *
8759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata *      http://www.apache.org/licenses/LICENSE-2.0
9759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata *
10759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * Unless required by applicable law or agreed to in writing, software
11759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * distributed under the License is distributed on an "AS IS" BASIS,
12759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * See the License for the specific language governing permissions and
14759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * limitations under the License.
15759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata */
16759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
17759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granatapackage com.android.car.obd2;
18759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
19759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granataimport com.android.car.obd2.commands.AmbientAirTemperature;
2024475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.CalculatedEngineLoad;
2124475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.EngineCoolantTemperature;
22759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granataimport com.android.car.obd2.commands.EngineOilTemperature;
2324475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.EngineRuntime;
2424475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.FuelGaugePressure;
2524475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.FuelSystemStatus;
2624475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.FuelTankLevel;
2724475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.FuelTrimCommand.Bank1LongTermFuelTrimCommand;
2824475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.FuelTrimCommand.Bank1ShortTermFuelTrimCommand;
2924475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.FuelTrimCommand.Bank2LongTermFuelTrimCommand;
3024475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.FuelTrimCommand.Bank2ShortTermFuelTrimCommand;
3124475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.RPM;
3224475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.Speed;
3324475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport com.android.car.obd2.commands.ThrottlePosition;
34759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granataimport java.io.IOException;
35759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granataimport java.util.HashMap;
3624475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granataimport java.util.Objects;
37759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granataimport java.util.Optional;
38759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granataimport java.util.Set;
39759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
40759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata/**
41759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * Base class of OBD2 command objects that query a "vehicle" and return an individual data point
42759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * represented as a Java type.
43759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata *
44759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata * @param  The Java type that represents the value of this command's output.
45759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata */
46759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granatapublic abstract class Obd2Command<ValueType> {
47759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
48759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    /**
49759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata     * Abstract representation of an object whose job it is to receive the bytes read from the OBD2
50759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata     * connection and return a Java representation of a command's value.
51759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata     *
52759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata     * @param 
53759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata     */
54759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    public interface OutputSemanticHandler<ValueType> {
55759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata        int getPid();
56759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
57759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata        Optional<ValueType> consume(IntegerArrayStream data);
58759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    }
59759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
60759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    public static final int LIVE_FRAME = 1;
61759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    public static final int FREEZE_FRAME = 2;
62759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
63759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    private static final HashMap<Integer, OutputSemanticHandler<Integer>>
64759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata            SUPPORTED_INTEGER_COMMANDS = new HashMap<>();
65759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    private static final HashMap<Integer, OutputSemanticHandler<Float>> SUPPORTED_FLOAT_COMMANDS =
66759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata            new HashMap<>();
67759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
6824475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata    private static void addSupportedIntegerCommands(
6924475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata            OutputSemanticHandler<Integer>... integerOutputSemanticHandlers) {
7024475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata        for (OutputSemanticHandler<Integer> integerOutputSemanticHandler :
7124475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata                integerOutputSemanticHandlers) {
7224475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata            SUPPORTED_INTEGER_COMMANDS.put(
7324475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata                    integerOutputSemanticHandler.getPid(), integerOutputSemanticHandler);
7424475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata        }
75759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    }
76759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
7724475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata    private static void addSupportedFloatCommands(
7824475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata            OutputSemanticHandler<Float>... floatOutputSemanticHandlers) {
7924475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata        for (OutputSemanticHandler<Float> floatOutputSemanticHandler :
8024475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata                floatOutputSemanticHandlers) {
8124475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata            SUPPORTED_FLOAT_COMMANDS.put(
8224475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata                    floatOutputSemanticHandler.getPid(), floatOutputSemanticHandler);
8324475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata        }
84759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    }
85759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
86759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    public static Set<Integer> getSupportedIntegerCommands() {
87759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata        return SUPPORTED_INTEGER_COMMANDS.keySet();
88759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    }
89759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
90759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    public static Set<Integer> getSupportedFloatCommands() {
91759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata        return SUPPORTED_FLOAT_COMMANDS.keySet();
92759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    }
93759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
94759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    public static OutputSemanticHandler<Integer> getIntegerCommand(int pid) {
95759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata        return SUPPORTED_INTEGER_COMMANDS.get(pid);
96759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    }
97759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
98759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    public static OutputSemanticHandler<Float> getFloatCommand(int pid) {
99759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata        return SUPPORTED_FLOAT_COMMANDS.get(pid);
100759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    }
101759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
102759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    static {
10324475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata        addSupportedFloatCommands(
10424475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata                new AmbientAirTemperature(),
10524475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata                new CalculatedEngineLoad(),
10624475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata                new FuelTankLevel(),
10724475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata                new Bank2ShortTermFuelTrimCommand(),
10824475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata                new Bank2LongTermFuelTrimCommand(),
10924475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata                new Bank1LongTermFuelTrimCommand(),
11024475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata                new Bank1ShortTermFuelTrimCommand(),
11124475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata                new ThrottlePosition());
11224475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata        addSupportedIntegerCommands(
11324475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata                new EngineOilTemperature(),
11424475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata                new EngineCoolantTemperature(),
11524475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata                new FuelGaugePressure(),
11624475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata                new FuelSystemStatus(),
11724475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata                new RPM(),
11824475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata                new EngineRuntime(),
11924475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata                new Speed());
120759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    }
121759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
122759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    protected final int mMode;
123759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    protected final OutputSemanticHandler<ValueType> mSemanticHandler;
124759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
125759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    Obd2Command(int mode, OutputSemanticHandler<ValueType> semanticHandler) {
126759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata        mMode = mode;
12724475d0e9c67c758d05fa69bfe34013babfeb176Enrico Granata        mSemanticHandler = Objects.requireNonNull(semanticHandler);
128759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    }
129759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
130759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    public abstract Optional<ValueType> run(Obd2Connection connection) throws Exception;
131759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
13287c0545b6f0814657df555902300d7b3dfe6b96fEnrico Granata    public int getPid() {
13387c0545b6f0814657df555902300d7b3dfe6b96fEnrico Granata        return mSemanticHandler.getPid();
13487c0545b6f0814657df555902300d7b3dfe6b96fEnrico Granata    }
13587c0545b6f0814657df555902300d7b3dfe6b96fEnrico Granata
136759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    public static final <T> LiveFrameCommand<T> getLiveFrameCommand(OutputSemanticHandler handler) {
137759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata        return new LiveFrameCommand<>(handler);
138759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    }
139759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
140759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    public static final <T> FreezeFrameCommand<T> getFreezeFrameCommand(
141759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata            OutputSemanticHandler handler, int frameId) {
142759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata        return new FreezeFrameCommand<>(handler, frameId);
143759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    }
144759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
145759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    /**
146759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata     * An OBD2 command that returns live frame data.
147759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata     *
148759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata     * @param  The Java type that represents the command's result type.
149759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata     */
150759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    public static class LiveFrameCommand<ValueType> extends Obd2Command<ValueType> {
151759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata        private static final int RESPONSE_MARKER = 0x41;
152759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
153759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata        LiveFrameCommand(OutputSemanticHandler<ValueType> semanticHandler) {
154759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata            super(LIVE_FRAME, semanticHandler);
155759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata        }
156759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
157759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata        public Optional<ValueType> run(Obd2Connection connection)
158759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata                throws IOException, InterruptedException {
159759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata            String command = String.format("%02X%02X", mMode, mSemanticHandler.getPid());
160759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata            int[] data = connection.run(command);
161759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata            IntegerArrayStream stream = new IntegerArrayStream(data);
162759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata            if (stream.expect(RESPONSE_MARKER, mSemanticHandler.getPid())) {
163759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata                return mSemanticHandler.consume(stream);
164759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata            }
165759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata            return Optional.empty();
166759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata        }
167759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    }
168759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
169759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    /**
170759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata     * An OBD2 command that returns freeze frame data.
171759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata     *
172759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata     * @param  The Java type that represents the command's result type.
173759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata     */
174759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    public static class FreezeFrameCommand<ValueType> extends Obd2Command<ValueType> {
1751d59008988765a4da37c87c9099e2ae0fc661ad6Enrico Granata        private static final int RESPONSE_MARKER = 0x42;
176759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
177759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata        private int mFrameId;
178759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
179759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata        FreezeFrameCommand(OutputSemanticHandler<ValueType> semanticHandler, int frameId) {
180759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata            super(FREEZE_FRAME, semanticHandler);
181759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata            mFrameId = frameId;
182759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata        }
183759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata
184759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata        public Optional<ValueType> run(Obd2Connection connection)
185759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata                throws IOException, InterruptedException {
186759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata            String command =
187759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata                    String.format("%02X%02X %02X", mMode, mSemanticHandler.getPid(), mFrameId);
188759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata            int[] data = connection.run(command);
189759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata            IntegerArrayStream stream = new IntegerArrayStream(data);
190759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata            if (stream.expect(RESPONSE_MARKER, mSemanticHandler.getPid(), mFrameId)) {
191759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata                return mSemanticHandler.consume(stream);
192759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata            }
193759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata            return Optional.empty();
194759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata        }
195759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata    }
196759d291564677287e6a8c0aa8397b7e7de81ee22Enrico Granata}
197