//-----------------------------------------------------------------------
//
// Copyright © 2012 Nils Hammar. All rights reserved.
//
//-----------------------------------------------------------------------
/*
* Software to access vehicle information via the OBD-II connector.
*
* Copyright © 2012 Nils Hammar
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Alternative licensing is possible, see the licensing document.
*
* The above text may not be removed or modified.
*/
namespace Protocol.OBD
{
using System;
using System.Collections.Generic;
using System.Linq;
using global::SharedObjects;
using global::SharedObjects.CAN.Objects;
using global::SharedObjects.GUI;
using global::SharedObjects.Misc;
using global::SharedObjects.Protocol;
using global::SharedObjects.Protocol.OBD;
///
/// Class that parses PID data.
///
public class PidParser : IPidParser
{
/*
* Byte number according to J1979.
* The label to number mapping will make it easier
* whenever (if ever) necessary to change the offsets.
*/
///
/// Byte position A
///
public const int BYTE_A = 0;
///
/// Byte position B
///
public const int BYTE_B = 1;
///
/// Byte position C
///
public const int BYTE_C = 2;
///
/// Byte position D
///
public const int BYTE_D = 3;
///
/// Byte position E
///
public const int BYTE_E = 4;
///
/// Byte position F
///
public const int BYTE_F = 5;
///
/// Byte position G
///
public const int BYTE_G = 6;
///
/// Byte position H
///
public const int BYTE_H = 7;
///
/// Byte position I
///
public const int BYTE_I = 8;
///
/// Byte position J
///
public const int BYTE_J = 9;
///
/// Byte position K
///
public const int BYTE_K = 10;
///
/// Byte position L
///
public const int BYTE_L = 11;
///
/// Byte position M
///
public const int BYTE_M = 12;
///
/// Byte position N
///
public const int BYTE_N = 13;
///
/// Byte position O
///
public const int BYTE_O = 14;
///
/// Byte position P
///
public const int BYTE_P = 15;
///
/// Byte position Q
///
public const int BYTE_Q = 16;
///
/// Byte position R
///
public const int BYTE_R = 17;
///
/// Byte position S
///
public const int BYTE_S = 18;
///
/// Byte position T
///
public const int BYTE_T = 19;
///
/// Byte position U
///
public const int BYTE_U = 20;
///
/// Byte position V
///
public const int BYTE_V = 21;
///
/// Byte position W
///
public const int BYTE_W = 22;
///
/// Byte position X
///
public const int BYTE_X = 23;
///
/// Byte position Y
///
public const int BYTE_Y = 24;
///
/// Byte position Z
///
public const int BYTE_Z = 25;
/* Data formattings. */
///
/// Float with three decimals.
///
public const int DT_DECIMAL_FLOAT3 = -3;
///
/// Float with two decimals.
///
public const int DT_DECIMAL_FLOAT2 = -2;
///
/// Float with one decimal.
///
public const int DT_DECIMAL_FLOAT1 = -1;
///
/// String value.
///
public const int DT_STRING = 0;
///
/// Integer in decimal form.
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "int", Justification = "Suitable in this case.")]
public const int DT_DECIMAL_INT = 1;
///
/// Integer in hex(2) form.
///
public const int DT_HEX2 = 2;
///
/// Integer in hex(4) form.
///
public const int DT_HEX4 = 3;
///
/// Integer in hex(8) form.
///
public const int DT_HEX8 = 4;
///
/// Integer in hex(16) form.
///
public const int DT_HEX16 = 5;
///
/// Integer in hex(2 + 2) form.
///
public const int DT_HEX4_GROUP = 6;
///
/// Integer in hex(2 + 2 + 2 + 2) form.
///
public const int DT_HEX8_GROUP = 7;
///
/// Integer in hex(2 + 2 + 2 + 2 + 2 + 2 + 2 + 2) form.
///
public const int DT_HEX16_GROUP = 8;
///
/// Integer in hex(8 + 8) form.
///
public const int DT_HEX16_2GROUP = 9;
///
/// Integer in hex(4 + 4 + 4 + 4) form.
///
public const int DT_HEX16_4GROUP = 10;
///
/// Logger instance.
///
private ILogging iLogging;
///
/// Messaging instance.
///
private IMessaging iMessaging;
///
/// Current PID ID.
///
private uint pid;
///
/// Current PID item.
///
private XmlClass.pidgroup.pidlist pidItem;
///
/// List of result data receivers.
///
private List iDataPresentations = new List();
///
/// Current mode byte value.
///
private byte mode;
///
/// Frame number for freeze frame data.
///
private byte frame;
///
/// Lock object to avoid collission.
///
private object uiManipulationLockObject = new object();
///
/// Source address for message.
///
private uint srcAddress = 0;
///
/// Data panel instance.
///
private List obdDataPanels = new List();
///
/// Time marker.
///
private long t1 = 0;
///
/// Lock object for OBD data list.
///
private object obdDataLockObject = new object();
///
/// Gauge presentation instance.
///
private IGaugePresentation iGaugePresentation;
///
/// Dictionary of next PIDs per source address.
///
private List nextPidWaits = new List();
///
/// Initializes a new instance of the class.
///
/// Logger instance.
/// Messaging instance.
public PidParser(
ILogging iLogging,
IMessaging iMessaging)
{
this.iLogging = iLogging;
this.iMessaging = iMessaging;
}
///
/// Get the computed value for the given sensor.
/// 'sensorNumeric' is in numerical form for plotting and the function returns a formatted string
/// for presentation.
///
/// Logger instance.
/// Raw value.
/// Current sensor.
/// Variable useful for further computation - like plotting.
/// Interpreted sensor value in string.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "Reviewed.")]
public static string getSensorValue(
ILogging iLogging,
uint data,
XmlClass.pidgroup.pidlist.sensordata sensor,
out double sensorNumeric)
{
string sensorValue = string.Empty;
double value = data;
if (sensor != null && iLogging != null)
{
bool? boolValue = null;
if (sensor.startbit != null
&& sensor.endbit != null
&& sensor.startbit.Trim().Length > 0
&& sensor.endbit.Trim().Length > 0)
{
getBitMaskedValue(iLogging, data, sensor, ref sensorValue, ref value, ref boolValue);
}
if (boolValue == null)
{
switch (sensor.unit)
{
case "Emission":
sensorValue = getEmissionDesign((byte)value);
break;
case "Fuel":
sensorValue = getFuelType((byte)value);
break;
case "OBD":
sensorValue = getObdValue((byte)value);
break;
default:
value = scaleValue(sensor, value);
sensorValue = formatValue(sensor, value);
break;
}
}
}
sensorNumeric = value;
#if DISPLAY_RAW
sensorValue += " [0x" + data.ToString("x2") + "]";
#endif
return sensorValue;
}
///
/// Get the raw value according to sensor from the payload data.
///
/// Payload data.
/// Offset in payload for wanted data.
/// Number of bytes for wanted data.
/// Raw value.
public static uint getRawValue(byte[] pidPayload, uint offset, uint count)
{
uint rawdata = 0;
if (pidPayload != null)
{
for (int i = 0; i < count && (i + offset) < pidPayload.Length; i++)
{
rawdata = (rawdata << 8) | (uint)(pidPayload[i + offset] & 0xff);
}
}
return rawdata;
}
///
/// Sets Data panel instance.
///
/// Data panel instance.
public void addPidReceiver(IObdDataPanel obdDataPanel)
{
lock (this.obdDataLockObject)
{
this.obdDataPanels.Add(obdDataPanel);
}
}
///
/// Add data presentation instance.
///
/// Data presentation instance.
public void addDataPresentation(IDataPresentation iDataPresentation)
{
lock (this.uiManipulationLockObject)
{
if (!this.iDataPresentations.Contains(iDataPresentation))
{
this.iDataPresentations.Add(iDataPresentation);
}
}
}
///
/// Set gauge presentation instance.
///
/// Gauge presentation instance.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "iGaugePresentation", Justification = "Reviewed, intentional.")]
public void setGaugePresentation(IGaugePresentation iGaugePresentation)
{
this.iGaugePresentation = iGaugePresentation;
}
///
/// Remove data presentation instance.
///
/// Data presentation instance.
public void removeUi(IDataPresentation iDataPresentation)
{
lock (this.uiManipulationLockObject)
{
if (this.iDataPresentations.Contains(iDataPresentation))
{
this.iDataPresentations.Remove(iDataPresentation);
}
}
}
///
/// Parse PID.
///
/// PID Item.
/// Mode byte.
/// Frame number for freeze frame data.
/// Payload to parse.
/// Source address of received data.
/// 'true' if it's OK to pop the next message to send and send it.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "frame", Justification = "Reviewed, intentional.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "mode", Justification = "Reviewed, intentional.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "pidItem", Justification = "Reviewed, intentional.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "srcAddress", Justification = "Reviewed, intentional.")]
public bool parsePid(XmlClass.pidgroup.pidlist pidItem, byte mode, byte frame, byte[] pidPayload, uint srcAddress)
{
bool popNext = true;
if (pidItem != null)
{
this.pidItem = pidItem;
this.mode = mode;
this.frame = frame;
this.srcAddress = srcAddress;
this.t1 = DateTime.Now.Ticks / Utils.TICKS_PER_MILLISECOND;
this.pid = pidItem.pid_int;
switch (this.pid)
{
case 0x00: // Supported PID:s 01-20
case 0x20: // Supported PID:s 21-40
case 0x40: // Supported PID:s 41-60
case 0x60: // Supported PID:s 61-80
case 0x80: // Supported PID:s 81-A0
case 0xA0: // Supported PID:s A1-E0
case 0xC0: // Supported PID:s C1-E0
case 0xE0: // Supported PID:s E1-FF
popNext = this.pidParser(pidPayload);
break;
case 0x90: // World Wide Harmonized On-Board-Diagnostic
// this.wwhVehicleObdInfo(pidPayload);
break;
case 0x91: // World Wide Harmonized On-Board-Diagnostic
// this.wwhEcuObdInfo(pidPayload);
break;
case 0x93: // World Wide Harmonized On-Board-Diagnostic
// this.wwhObdCountersInfo(pidPayload);
break;
default:
popNext = this.genericHandler(pidPayload, popNext);
break;
}
}
return popNext;
}
///
/// The value is only some bits of the data.
///
/// Logger instance.
/// Raw data.
/// Sensor data.
/// Sensor value string.
/// Numeric value.
/// Boolean value. (Only set at single bit data)
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "iLogging", Justification = "Reviewed.")]
private static void getBitMaskedValue(
ILogging iLogging,
uint data,
XmlClass.pidgroup.pidlist.sensordata sensor,
ref string sensorValue,
ref double value,
ref bool? boolValue)
{
int startBit = Convert.ToInt32(sensor.startbit.Trim());
int endBit = Convert.ToInt32(sensor.endbit.Trim());
value = 0;
if (startBit == endBit)
{
bool checkFlag = ((data >> startBit) & 0x01) != 0;
boolValue = checkFlag;
sensorValue = checkFlag.ToString();
value = checkFlag ? 1 : 0;
value = (value * 0.8) + 0.05 + (startBit / 50.0);
}
else
{
int bits = endBit - startBit + 1;
if (bits > 0)
{
uint mask = (uint)(Math.Pow(2, bits) - 1);
uint intValue = (uint)((data >> startBit) & mask);
value = intValue;
}
else
{
sensorValue = "Invalid sensor declaration of 'startbit' and 'endbit'";
value = 0;
boolValue = false;
}
}
}
///
/// Scale the value according to sensor.
///
/// Sensor data.
/// Value to scale.
/// Scaled value.
private static double scaleValue(XmlClass.pidgroup.pidlist.sensordata sensor, double value)
{
value = value + sensor.scaleoffset;
value = value * sensor.scalefactor;
return value;
}
///
/// Format value according to sensor rules.
///
/// Sensor data.
/// Value to format.
/// Formatted value string.
private static string formatValue(XmlClass.pidgroup.pidlist.sensordata sensor, double value)
{
string format = sensor.sensorPresentation_format;
if (format == null || format.Trim().Length == 0)
{
format = "0.000";
}
string sensorValue;
try
{
sensorValue = value.ToString(format);
}
catch
{
sensorValue = value.ToString("0.000") + " ¹";
}
return sensorValue;
}
///
/// Filters the sensor list by using "controlbit" sensors to decide if a sensor shall be displayed or not.
///
/// Payload data.
/// Raw sensor list.
/// Filtered sensor list.
private static IList getFilteredSensorList(byte[] pidPayload, IList sensors)
{
IList actualSensors = new List();
IList inactiveControlSensors = new List();
bool hasControlSensor = false;
foreach (XmlClass.pidgroup.pidlist.sensordata sensor in sensors)
{
if (sensor.unit == "controlbit")
{
hasControlSensor = true;
try
{
uint offset = sensor.offset;
uint count = sensor.count;
int startBit = safeGetInt(sensor.startbit);
int endBit = safeGetInt(sensor.endbit);
if (startBit >= 0 && endBit >= 0)
{
if ((offset + count) <= pidPayload.Length && count == 1 && startBit == endBit)
{
if ((pidPayload[offset] & (1 << startBit)) == 0)
{
inactiveControlSensors.Add(sensor);
}
}
}
}
catch
{
}
}
else
{
actualSensors.Add(sensor);
}
}
if (hasControlSensor)
{
XmlClass.pidgroup.pidlist.sensordata[] actualSensorArray = actualSensors.ToArray();
foreach (XmlClass.pidgroup.pidlist.sensordata sensor in actualSensorArray)
{
bool match = false;
int startBit = safeGetInt(sensor.startbit);
int endBit = safeGetInt(sensor.endbit);
if (startBit >= 0 && endBit >= 0)
{
foreach (XmlClass.pidgroup.pidlist.sensordata controlSensor in inactiveControlSensors)
{
int controlStartBit = (int)controlSensor.presentation_min;
int controlEndBit = (int)controlSensor.presentation_max;
if (sensor.offset == controlSensor.scaleoffset
&& sensor.count == (uint)controlSensor.scalefactor
&& startBit == controlStartBit
&& endBit == controlEndBit)
{
match = true;
break;
}
}
if (match)
{
actualSensors.Remove(sensor);
}
}
}
}
return actualSensors;
}
///
/// Safely get an int value from string.
///
/// String to get int from.
/// Converted int value.
private static int safeGetInt(string strValue)
{
int intValue = 0;
try
{
intValue = Convert.ToInt32(strValue);
}
catch
{
}
return intValue;
}
///
/// Get the OBD description string.
///
/// Raw data for OBD type.
/// String describing the OBD variant.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Reviewed, not complex.")]
private static string getObdValue(byte rawData)
{
string value;
switch (rawData)
{
case 0x01:
value = "OBD-II as defined by the CARB";
break;
case 0x02:
value = "OBD as defined by the EPA";
break;
case 0x03:
value = "OBD and OBD-II";
break;
case 0x04:
value = "OBD-I";
break;
case 0x05:
value = "Not meant to comply with any OBD standard";
break;
case 0x06:
value = "EOBD (Europe)";
break;
case 0x07:
value = "EOBD and OBD-II";
break;
case 0x08:
value = "EOBD and OBD";
break;
case 0x09:
value = "EOBD, OBD and OBD II";
break;
case 0x0A:
value = "JOBD (Japan)";
break;
case 0x0B:
value = "JOBD and OBD II";
break;
case 0x0C:
value = "JOBD and EOBD";
break;
case 0x0D:
value = "JOBD, EOBD, and OBD II";
break;
case 0x11:
value = "Engine Manufacturer Diagnostics (EMD) - Heavy-duty vehicles (>14,000) certified to EMD under title 13, CCR section 1971 (e.g., 2007-2009 model year diesel and gasoline engines)";
break;
case 0x12:
value = "Engine Manufacturer Diagnostics Enhanced (EMD+) - Heavy-duty engines (>14,000) certified to EMD+ under title 13, CCR section 1971.1 (e.g., 2010-2012 model year diesel and gasoline engines not certified to HD OBD, 2013-2019 model year alternate fuel engines)";
break;
case 0x13:
value = "Heavy Duty On-Board Diagnostics (Child/Partial) - Heavy-duty engines (>14,000) certified to HDOBD as an extrapolated/child rating under title 13, CCR section 1971.1(d)(7.1.2) or (7.2.3) (e.g., 2010-2015 model year diesel and gasoline engines that are subject to HDOBD but are not the full OBD/parent rating)";
break;
case 0x14:
value = "Heavy Duty On-Board Diagnostics - Heavy-duty engines (>14,000) certified to HDOBD as a full OBD/parent rating under title 13, CCR section 1971.1(d)(7.1.1) or (7.2.2) (e.g., 2010 and beyond model year diesel and gasoline engines that are subject to full HDOBD)";
break;
case 0x15:
value = "World Wide Harmonized OBD";
break;
case 0x17:
value = "Heavy Duty Euro OBD Stage I without NOx control";
break;
case 0x18:
value = "Heavy Duty Euro OBD Stage I with NOx control";
break;
case 0x19:
value = "Heavy Duty Euro OBD Stage II without NOx control";
break;
case 0x1A:
value = "Heavy Duty Euro OBD Stage II with NOx control";
break;
case 0x1C:
value = "Brazil OBD Phase 1";
break;
case 0x1D:
value = "Brazil OBD Phase 2";
break;
case 0x1E:
value = "Korean OBD";
break;
case 0x1F:
value = "India OBD I";
break;
case 0x20:
value = "India OBD II";
break;
case 0x21:
value = "Heavy Duty Euro OBD Stage VI";
break;
default:
value = "ISO/SAE reserved or Undefined OBD standard: 0x" + string.Format("{0:x2} ", (int)rawData);
break;
}
return value;
}
///
/// Format data in a user friendly manner for the function.
///
/// Payload data.
/// String describing the fuel type.
private static string getFuelType(byte rawData)
{
string value;
switch (rawData)
{
case 0x01:
value = "Gasoline";
break;
case 0x02:
value = "Methanol";
break;
case 0x03:
value = "Ethanol";
break;
case 0x04:
value = "Diesel";
break;
case 0x05:
value = "LPG";
break;
case 0x06:
value = "CNG";
break;
case 0x07:
value = "Propane";
break;
case 0x08:
value = "Electric";
break;
case 0x09:
value = "Bifuel running Gasoline";
break;
case 0x0A:
value = "Bifuel running Methanol";
break;
case 0x0B:
value = "Bifuel running Ethanol";
break;
case 0x0C:
value = "Bifuel running LPG";
break;
case 0x0D:
value = "Bifuel running CNG";
break;
case 0x0E:
value = "Bifuel running Propane";
break;
case 0x0F:
value = "Bifuel running Electricity";
break;
case 0x10:
value = "Bifuel mixed gas/electric";
break;
case 0x11:
value = "Hybrid gasoline";
break;
case 0x12:
value = "Hybrid Ethanol";
break;
case 0x13:
value = "Hybrid Diesel";
break;
case 0x14:
value = "Hybrid Electric";
break;
case 0x15:
value = "Hybrid Mixed fuel";
break;
case 0x16:
value = "Hybrid Regenerative";
break;
case 0x17:
value = "Bifuel running Diesel";
break;
default:
value = "Undefined fuel type 0x" + string.Format("{0:x2} ", (int)rawData);
break;
}
return value;
}
///
/// Format data in a user friendly manner for the function.
///
/// Payload data.
/// String describing the emission design.
private static string getEmissionDesign(byte rawData)
{
string value;
switch (rawData)
{
case 0x0E:
value = "Heavy Duty Vehicles (EURO IV) B1";
break;
case 0x0F:
value = "Heavy Duty Vehicles (EURO V) B2";
break;
case 0x10:
value = "Heavy Duty Vehicles (EURO EEV) C";
break;
default:
value = " * Unknown * 0x" + string.Format("{0:x2} ", (int)rawData);
break;
}
return value;
}
///
/// A generic handler of data that uses data from definition in XML files
/// to format the data to be presented to the user.
///
/// Raw data.
/// Indicates if it's OK to pop the next message to send and send it.
/// 'true' if it's OK to pop the next message to send and send it.
private bool genericHandler(byte[] pidPayload, bool popNext)
{
IList sensors = this.pidItem.sensors;
if (sensors != null && sensors.Count > 0)
{
this.presentData(pidPayload, sensors);
}
return popNext;
}
///
/// Build presentation of data.
///
/// Raw data.
/// List of sensors to consider.
private void presentData(byte[] pidPayload, IList sensors)
{
IList actualSensors = getFilteredSensorList(pidPayload, sensors);
RawLogObject rawLogObject = new RawLogObject(this.srcAddress, this.mode, this.pidItem.pid_int, (uint)pidPayload.Length, pidPayload);
this.iLogging.addRawData(rawLogObject);
foreach (XmlClass.pidgroup.pidlist.sensordata sensor in actualSensors)
{
uint offset = sensor.offset;
uint count = sensor.count;
if ((offset + count) <= pidPayload.Length)
{
string name = this.pidItem.name;
string unit = sensor.unit != null ? sensor.unit : string.Empty;
uint rawValue = getRawValue(pidPayload, offset, count);
bool naFlag = Utils.checkNaData(sensor, rawValue);
double value;
string formattedValue = getSensorValue(this.iLogging, rawValue, sensor, out value);
// Don't update the GUI if we are logging but not plotting.
// Maybe add some GUI feedback just to tell that it is
// doing it's thing later on.
this.addSimpleData(sensor, name, unit, value, formattedValue, naFlag);
}
else
{
this.iLogging.appendText(
this.pidItem.name + ", " + sensor.name + " (" + sensor.unit + ")"
+ "; Expecting more data than we got: expected="
+ (offset + count) + ", we got=" + pidPayload.Length + "\r\n",
LogLevel.LOG_WARN);
}
}
}
///
/// Add a simple text row with data.
///
/// Current sensor to plot.
/// Name of sensor.
/// Presentation unit.
/// Value to plot.
/// Formatted value to display.
/// Value is potentially invalid.
private void addSimpleData(
XmlClass.pidgroup.pidlist.sensordata sensor,
string name,
string unit,
double value,
string formattedValue,
bool naFlag)
{
lock (this.uiManipulationLockObject)
{
if (this.iGaugePresentation != null)
{
this.iGaugePresentation.setGaugeValue(this.mode, this.pidItem.pid_int, sensor, (float)value);
}
else
{
foreach (IDataPresentation iDataPresentation in this.iDataPresentations)
{
// Output to table.
if (sensor.name != null && sensor.name.Trim().Length > 0)
{
name += ": " + sensor.name.Trim();
}
iDataPresentation.simpleDataAdd(
new PresentationData(
this.srcAddress,
this.mode,
this.pid,
this.pidItem,
sensor,
name,
unit,
formattedValue,
value,
this.t1,
naFlag));
}
}
}
}
///
/// Parse PID bitmap.
///
/// Payload data.
/// 'true' if it's OK to pop the next message to send and send it.
private bool pidParser(byte[] pidPayload)
{
bool popNext = true;
if (pidPayload.Length >= 4)
{
byte mode1 = (byte)(this.mode & 0xbf);
int bitmap = 0;
for (int i = 0; i < 4; i++)
{
bitmap = bitmap << 8 | (int)(pidPayload[i] & 0xff);
}
byte nextPid = 0;
switch (this.pid)
{
case 0x00: // Supported PID:s 01-20
if ((bitmap & 0x01) != 0)
{
nextPid = 0x20;
}
break;
case 0x20: // Supported PID:s 21-40
if ((bitmap & 0x01) != 0)
{
nextPid = 0x40;
}
break;
case 0x40: // Supported PID:s 41-60
if ((bitmap & 0x01) != 0)
{
nextPid = 0x60;
}
break;
case 0x60: // Supported PID:s 61-80
if ((bitmap & 0x01) != 0)
{
nextPid = 0x80;
}
break;
case 0x80: // Supported PID:s 81-A0
if ((bitmap & 0x01) != 0)
{
nextPid = 0xA0;
}
break;
case 0xA0: // Supported PID:s A1-E0
if ((bitmap & 0x01) != 0)
{
nextPid = 0xC0;
}
break;
case 0xC0: // Supported PID:s C1-E0
if ((bitmap & 0x01) != 0)
{
nextPid = 0xE0;
}
break;
case 0xE0: // Supported PID:s E1-FF
break;
default:
break;
}
this.updatePidWaits(nextPid);
this.updateObdDataPanels(bitmap, nextPid);
if (nextPid != 0)
{
this.requestNextPidBlock(mode1, nextPid);
}
}
return popNext;
}
///
/// Perform a request for the next PID block.
///
/// Request mode.
/// PID to request.
private void requestNextPidBlock(byte mode1, byte nextPid)
{
uint? destinationAddress = DataRequester.getDestinationAddress(this.iMessaging.protocolHandler, this.srcAddress);
// When we got one PID set we ask for next set.
// this.iMessaging.queueMsg(new TxMsg(new byte[] { 0x01, nextPid }));
if (mode1 == 0x02)
{
this.iMessaging.queueMsg(destinationAddress, new TxMsg(new byte[] { mode1, nextPid, this.frame }));
}
else
{
this.iMessaging.queueMsg(destinationAddress, new TxMsg(new byte[] { mode1, nextPid }));
}
}
///
/// Update the list of address where we are waiting for PID data from.
///
/// Next PID block to request.
private void updatePidWaits(byte nextPid)
{
if (nextPid != 0 && !this.nextPidWaits.Contains(this.srcAddress))
{
this.nextPidWaits.Add(this.srcAddress);
}
else
{
if (nextPid == 0 && this.nextPidWaits.Contains(this.srcAddress))
{
this.nextPidWaits.Remove(this.srcAddress);
}
}
}
///
/// Update the OBD data panels to display PID entries according to bitmap.
///
/// Bitmap for entries to display.
/// Next PID in sequence.
private void updateObdDataPanels(int bitmap, byte nextPid)
{
IObdDataPanel[] obdDataPanelArray;
lock (this.obdDataLockObject)
{
obdDataPanelArray = this.obdDataPanels.ToArray();
}
foreach (IObdDataPanel panel in obdDataPanelArray)
{
panel.addDataItem(this.srcAddress, this.mode, this.pid, bitmap);
}
// If we don't expect any more answers...
if (nextPid == 0 && this.nextPidWaits.Count == 0)
{
lock (this.obdDataLockObject)
{
obdDataPanelArray = this.obdDataPanels.ToArray();
}
foreach (IObdDataPanel panel in obdDataPanelArray)
{
panel.displayFields();
}
}
}
}
}