//-----------------------------------------------------------------------
//
// 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 UserInterface.GUI.OBD
{
using System;
using global::Protocol.OBD;
using global::SharedObjects;
using global::SharedObjects.DataMgmt;
using global::SharedObjects.GUI;
using global::SharedObjects.Misc;
using global::SharedObjects.Misc.Objects;
using global::SharedObjects.Protocol;
using global::SharedObjects.Protocol.OBD;
///
/// Panel for accessing on-board monitoring test results.
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "OnBoard", Justification = "Reviewed.")]
public partial class OnBoardDiagTestPanel : AbstractUserControl, IDataPanel
{
///
/// Messaging instance.
///
private IMessaging iMessaging;
///
/// Mode Parser instance.
///
private ModeParser modeParser;
///
/// Initializes a new instance of the class.
///
public OnBoardDiagTestPanel()
{
this.iMessaging = null;
this.modeParser = null;
this.InitializeComponent();
this.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
}
///
/// Initializes a new instance of the class.
///
/// Logging instance.
/// Application tree instance.
/// Messaging instance.
/// Data source interface instance.
/// Current vehicle instance.
/// Mode parser instance.
/// Data file directory path.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "CanApp.CustomToolTip", Justification = "Reviewed.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Reviewed.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "Reviewed.")]
public OnBoardDiagTestPanel(
ILogging iLogging,
IApplicationTree applicationTree,
IMessaging iMessaging,
IDataSource iDataSource,
XmlClass.vehicle vehicle,
ModeParser modeParser,
string dataFileDir)
: base(iLogging, vehicle, applicationTree)
{
this.iMessaging = iMessaging;
this.modeParser = modeParser;
this.InitializeComponent();
this.obdDataPanel1.init(
iLogging,
iMessaging,
applicationTree,
0x06,
iDataSource,
vehicle,
dataFileDir);
this.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
}
///
/// Perform parsing of data.
///
/// Message mode.
/// Actual data payload.
/// Related transmitted message to help with parsing.
/// Pop next request and send.
/// Source address of received data.
/// Detected protocol by ELM/AGV adapter.
/// 'true' if successfully parsed by panel.
public bool parse(uint mode, byte[] payload, IRequestData requestData, ref bool popNext, uint srcAddress, uint detectedProtocol)
{
bool success = false;
if (payload != null)
{
switch (mode)
{
case 0x46: // On-board monitoring test results
uint pid = (uint)(payload[1] & 0xff);
byte[] pidPayload = Utils.extractData(payload, 1);
this.parsePid(srcAddress, mode, pid, pidPayload);
success = true;
break;
default:
break;
}
}
return success;
}
///
/// Initialize and refresh data.
///
public void init()
{
IRequestData rd = new RequestData(null, TxMsg.MODE_CURRENT, null, new TxMsg(TxMsg.GET_SUPPORTED_OBDMIDS));
this.iMessaging.queueMsg(rd);
this.iMessaging.sendMsg();
}
///
/// Scale the value and get the unit for the value.
///
/// Scaling and Unit ID
/// Raw Value
/// Out: Unit string.
/// Scaled Value.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Reviewed.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode", Justification = "Reviewed.")]
private static double scaleValue(byte unitScalingId, int value, out string unit)
{
double result = 0;
unit = string.Empty;
switch (unitScalingId)
{
case 0x01:
result = value;
break;
case 0x02:
result = value / 10.0;
break;
case 0x03:
result = value / 100.0;
break;
case 0x04:
result = value / 1000.0;
break;
case 0x05:
result = value / 32768.0;
break;
case 0x06:
result = value / 3276.8;
break;
case 0x07:
result = value / 4.0;
unit = "rpm";
break;
case 0x08:
result = value / 100.0;
unit = "km/h";
break;
case 0x09:
result = value;
unit = "km/h";
break;
case 0x0a:
result = value / 8192.0;
unit = "mV";
break;
case 0x0b:
result = value / 1000.0;
unit = "V";
break;
case 0x0c:
result = value / 100.0;
unit = "V";
break;
case 0x0d:
result = value / 256.0;
unit = "mA";
break;
case 0x0e:
result = value / 1000.0;
unit = "A";
break;
case 0x0f:
result = value / 100.0;
unit = "A";
break;
case 0x10:
result = value;
unit = "ms";
break;
case 0x11:
result = value / 10.0;
unit = "ms";
break;
case 0x12:
result = value;
unit = "s";
break;
case 0x13:
result = value;
unit = "mOhm";
break;
case 0x14:
result = value;
unit = "Ohm";
break;
case 0x15:
result = value;
unit = "kOhm";
break;
case 0x16:
result = value / 10.0;
unit = "°C";
break;
case 0x17:
result = value / 100.0;
unit = "kPa";
break;
case 0x18:
result = value / 85.47;
unit = "kPa";
break;
case 0x19:
result = value * 0.079;
unit = "kPa";
break;
case 0x1a:
result = value;
unit = "kPa";
break;
case 0x1b:
result = value * 10.0;
unit = "kPa";
break;
case 0x1c:
result = value / 100.0;
unit = "°";
break;
case 0x1d:
result = value / 2.0;
unit = "°";
break;
case 0x1e:
result = value / 32768.0;
unit = "lambda";
break;
case 0x1f:
result = value / 20.0;
unit = "Air/Fuel ratio.";
break;
case 0x20:
result = value / 256.0;
unit = string.Empty;
break;
case 0x21:
result = value / 1000.0;
unit = "Hz";
break;
case 0x22:
result = value;
unit = "Hz";
break;
case 0x23:
result = value;
unit = "kHz";
break;
case 0x24:
result = value;
unit = "counts";
break;
case 0x25:
result = value;
unit = "km";
break;
case 0x26:
result = value / 10000.0;
unit = "V/ms";
break;
case 0x27:
result = value / 100.0;
unit = "g/s";
break;
case 0x28:
result = value;
unit = "g/s";
break;
case 0x29:
result = value / 4.0;
unit = "Pa/s";
break;
case 0x2a:
result = value / 1000.0;
unit = "kg/h";
break;
case 0x2b:
result = value;
unit = string.Empty;
break;
case 0x2c:
result = value / 100.0;
unit = "g/cyl";
break;
case 0x2d:
result = value / 100.0;
unit = "mg/stroke";
break;
case 0x2e:
result = value;
unit = string.Empty;
break;
case 0x2f:
result = value / 100.0;
unit = "%";
break;
case 0x30:
result = value / 655.36;
unit = "%";
break;
case 0x31:
result = value / 1000.0;
unit = "l";
break;
case 0x32:
result = value / 32768.0;
unit = "inch/bit";
break;
case 0x33:
result = value / 4096.0;
unit = "Air/Fuel ratio";
break;
case 0x34:
result = value;
unit = "min";
break;
case 0x35:
result = value / 100.0;
unit = "%";
break;
case 0x36:
result = value / 100.0;
unit = "g";
break;
case 0x37:
result = value / 10;
unit = "g";
break;
case 0x38:
result = value;
unit = "g";
break;
case 0x39:
result = (value - 32768) / 100.0;
unit = "%";
break;
case 0x81:
result = (short)value;
unit = string.Empty;
break;
case 0x82:
result = ((short)value) / 10.0;
unit = string.Empty;
break;
case 0x83:
result = ((short)value) / 100.0;
unit = string.Empty;
break;
case 0x84:
result = ((short)value) / 1000.0;
unit = string.Empty;
break;
case 0x85:
result = ((short)value) / 32768.0;
unit = string.Empty;
break;
case 0x86:
result = ((short)value) / 3276.8;
unit = string.Empty;
break;
case 0x8a:
result = ((short)value) / 8192.0;
unit = "V";
break;
case 0x8b:
result = ((short)value) / 1000.0;
unit = "V";
break;
case 0x8c:
result = ((short)value) / 100.0;
unit = "V";
break;
case 0x8d:
result = ((short)value) / 256.0;
unit = "mA";
break;
case 0x8e:
result = ((short)value) / 1000.0;
unit = "A";
break;
case 0x90:
result = ((short)value) / 1000.0;
unit = "s";
break;
case 0x96:
result = ((short)value) / 10.0;
unit = "°C";
break;
case 0x9c:
result = ((short)value) / 100.0;
unit = "°";
break;
case 0x9d:
result = ((short)value) / 2.0;
unit = "°";
break;
case 0xa8:
result = ((short)value);
unit = "g/s";
break;
case 0xa9:
result = ((short)value) / 4.0;
unit = "Pa/s";
break;
case 0xaf:
result = ((short)value) / 100.0;
unit = "%";
break;
case 0xb0:
result = ((short)value) / 327.68;
unit = "%";
break;
case 0xb1:
result = ((short)value) * 2.0;
unit = "mV";
break;
case 0xfd:
result = ((short)value) / 1000.0;
unit = "kPa";
break;
case 0xfe:
result = ((short)value) / 4.0;
unit = "Pa";
break;
default:
result = value;
unit = "N/A";
break;
}
return result;
}
///
/// Parse PID data.
///
/// Source address of received data.
/// Mode byte value.
/// PID value.
/// PID data payload.
private void parsePid(uint srcAddress, uint mode_int, uint pid, byte[] pidPayload)
{
XmlClass.pidgroup.pidlist pidItem = this.modeParser.findPidItem(mode_int, pid);
if (pidItem != null)
{
this.iLogging.appendText("OnBoardDiagTestPanel: pidItem=" + pidItem.name + "\r\n");
switch (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
byte[] subPidPayload = Utils.extractData(pidPayload, 1);
this.pidParser(srcAddress, mode_int, pid, subPidPayload);
break;
default:
this.genericHandler(srcAddress, pidPayload);
break;
}
}
}
///
/// A generic handler of data that uses data from definition in XML files
/// to format the data to be presented to the user.
///
/// Source ECU address.
/// Payload data.
private void genericHandler(uint srcAddress, byte[] pidPayload)
{
byte[] data = pidPayload;
EcuDataPanel ecuDataPanel = getDataPanelByAddress(srcAddress, -1);
if (data.Length >= 9)
{
byte mid = data[0];
byte tid = data[1];
byte unitScalingId = data[2];
int value = data[3] << 8 | data[4];
int minLimit = data[5] << 8 | data[6];
int maxLimit = data[7] << 8 | data[8];
string valueUnitStr;
string minLimitUnitStr;
string maxLimitUnitStr;
double scaledValue = scaleValue(unitScalingId, value, out valueUnitStr);
double scaledMinLimit = scaleValue(unitScalingId, minLimit, out minLimitUnitStr);
double scaledMaxLimit = scaleValue(unitScalingId, maxLimit, out maxLimitUnitStr);
data = Utils.extractData(data, 9);
}
}
///
/// Parse PID bitmap.
///
/// Source ECU address.
/// Mode byte value.
/// Current PID.
/// Payload data.
/// 'true' if it's OK to pop the next message to send and send it.
private bool pidParser(uint srcAddress, uint mode_int, uint pid, byte[] pidPayload)
{
bool popNext = true;
if (pidPayload.Length >= 4)
{
int bitmap = 0;
for (int i = 0; i < 4; i++)
{
bitmap = bitmap << 8 | (int)(pidPayload[i] & 0xff);
}
byte nextPid = 0;
switch (pid)
{
case 0x00: // Supported PID:s 01-20
nextPid = 0x20;
break;
case 0x20: // Supported PID:s 21-40
nextPid = 0x40;
break;
case 0x40: // Supported PID:s 41-60
nextPid = 0x60;
break;
case 0x60: // Supported PID:s 61-80
nextPid = 0x80;
break;
case 0x80: // Supported PID:s 81-A0
nextPid = 0xA0;
break;
case 0xA0: // Supported PID:s A1-E0
nextPid = 0xC0;
break;
case 0xC0: // Supported PID:s C1-E0
nextPid = 0xE0;
break;
default:
break;
}
this.updateObdDataPanels(srcAddress, mode_int, pid, bitmap, nextPid);
if (nextPid != 0)
{
// When we got one PID set we ask for next set.
this.iMessaging.queueMsg(null, new TxMsg(new byte[] { TxMsg.MODE_OBD_MID, nextPid }));
this.iMessaging.sendMsg();
popNext = false;
}
}
return popNext;
}
///
/// Update the OBD data panels to display PID entries according to bitmap.
///
/// Source ECU address.
/// Mode byte value.
/// Current PID.
/// Bitmap for entries to display.
/// Next PID in sequence.
private void updateObdDataPanels(uint srcAddress, uint mode_int, uint pid, int bitmap, byte nextPid)
{
this.obdDataPanel1.addDataItem(srcAddress, (byte)mode_int, pid, bitmap);
if (nextPid == 0)
{
this.obdDataPanel1.displayFields();
}
}
///
/// Manually refresh the data.
///
/// Sending object.
/// Event data.
private void refreshButton_Click(object sender, EventArgs e)
{
this.init();
}
}
}