//-----------------------------------------------------------------------
//
// 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 System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using global::Protocol.OBD;
using global::SharedObjects;
using global::SharedObjects.DataMgmt;
using global::SharedObjects.GUI;
using global::SharedObjects.Misc;
using global::SharedObjects.Protocol;
using global::SharedObjects.Protocol.OBD;
///
/// Class for DTC handling.
///
public partial class ObdDtcPanel : AbstractUserControl, IDataPanel
{
///
/// Messaging instance.
///
private IMessaging iMessaging;
///
/// Current protocol.
///
private Protocols protocol;
///
/// List of OBD codes for vehicle.
///
private SortedDictionary obdcodes;
///
/// DTC counter.
///
private int dtcNum = 0;
///
/// Preferences instance.
///
private IPreferences iPreferences;
///
/// Initializes a new instance of the class.
///
/// Current logging instance.
/// Preferences instance.
/// Application tree instance.
/// Messaging instance.
/// Current protocol instance.
/// Dictionary of OBD codes.
public ObdDtcPanel(
ILogging iLogging,
IPreferences iPreferences,
IApplicationTree applicationTree,
IMessaging iMessaging,
Protocols protocol,
SortedDictionary obdcodes)
: base(iLogging, null, applicationTree)
{
this.iPreferences = iPreferences;
this.iMessaging = iMessaging;
this.protocol = protocol;
this.obdcodes = obdcodes;
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)));
// this.init();
}
///
/// 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;
switch (mode)
{
case 0x43: // Show stored DTC:s.
case 0x47: // Pending DTC:s.
case 0x4a: // Permanent DTC:s.
this.displayDtcs(payload, srcAddress, detectedProtocol);
success = true;
break;
default:
break;
}
return success;
}
///
/// Parse the payload for DTC:s.
///
/// Payload to parse.
/// Source address of received data.
/// Detected protocol by ELM/AGV adapter.
public void displayDtcs(byte[] payload, uint srcAddress, uint detectedProtocol)
{
if (this.InvokeRequired)
{
try
{
this.Invoke(new displayDtcsFunc(this.displayDtcs), new object[] { payload, srcAddress, detectedProtocol });
}
catch (ThreadAbortException)
{
throw;
}
catch (System.Reflection.TargetParameterCountException ex)
{
MessageBox.Show(
"Exception: " + ex.Message + "\r\n",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
}
catch (System.ObjectDisposedException)
{
// Ignore.
}
}
else
{
this.displayDtcsImpl(payload, srcAddress, detectedProtocol);
}
}
///
/// Initialize the panel.
///
private void init()
{
this.dtcNum = 0;
this.dtcDGV.Rows.Clear();
this.iMessaging.queueMsg(null, new TxMsg(TxMsg.GET_DTCS));
this.iMessaging.sendMsg();
}
///
/// Size changed.
///
/// Sending object.
/// Event data.
private void dtcDGW_SizeChanged(object sender, EventArgs e)
{
DataGridView dgv = (DataGridView)sender;
if (dgv != null)
{
int sz = dgv.Width - 260;
DataGridViewColumn col = dgv.Columns["description"];
col.Width = sz;
}
}
///
/// Get list of current DTCs from vehicle.
///
/// Sending object.
/// Event data.
private void currentDtcsButton_Click(object sender, EventArgs e)
{
this.init();
}
///
/// Get list of pending DTCs from vehicle.
///
/// Sending object.
/// Event data.
private void pendingDtcsButton_Click(object sender, EventArgs e)
{
this.dtcNum = 0;
this.dtcDGV.Rows.Clear();
this.iMessaging.queueMsg(null, new TxMsg(TxMsg.GET_PENDING_DTCS));
this.iMessaging.sendMsg();
}
///
/// Get list of permanent DTCs from vehicle.
///
/// Sending object.
/// Event data.
private void permanentDtcsButton_Click(object sender, EventArgs e)
{
this.dtcNum = 0;
this.dtcDGV.Rows.Clear();
this.iMessaging.queueMsg(null, new TxMsg(TxMsg.GET_PERMANENT_DTCS));
this.iMessaging.sendMsg();
}
///
/// Clear list of DTCs from vehicle.
///
/// Sending object.
/// Event data.
private void clearDtcsButton_Click(object sender, EventArgs e)
{
if (MessageBox.Show(
"Do you want to clear all DTC:s from the ECU?\r\n"
+ "Notice that this is a permanent action and can't be reversed.\r\n"
+ "You may want to save the DTC:s first if you haven't done that already.",
"Clear DTC:s",
MessageBoxButtons.OKCancel,
MessageBoxIcon.Question,
MessageBoxDefaultButton.Button2) == DialogResult.OK)
{
this.iMessaging.queueMsg(null, new TxMsg(TxMsg.CLEAR_DTCS));
this.iMessaging.sendMsg();
Thread.Sleep(600);
this.dtcDGV.Rows.Clear();
this.iMessaging.queueMsg(null, new TxMsg(TxMsg.GET_DTCS)); // Re-read the DTC list.
this.iMessaging.sendMsg();
}
}
///
/// Parse the payload for DTC:s.
///
/// Payload to parse.
/// Source address of received data.
/// Detected protocol by ELM/AGV adapter.
private delegate void displayDtcsFunc(byte[] payload, uint srcAddress, uint detectedProtocol);
///
/// Select implementation depending on protocol.
///
/// Payload to parse.
/// Source address of received data.
/// Detected protocol by ELM/AGV adapter.
private void displayDtcsImpl(byte[] payload, uint srcAddress, uint detectedProtocol)
{
this.iLogging.appendText("this.protocol.id=" + this.protocol.id + "\r\n");
switch (this.protocol.id)
{
case -1:
case -2:
switch (detectedProtocol)
{
case 6:
case 7:
case 8:
case 9:
this.displayDtcsImpl1(payload, srcAddress);
break;
default:
this.displayDtcsImpl2(payload, srcAddress);
break;
}
break;
case Protocols.ISO9141:
this.displayDtcsImpl2(payload, srcAddress);
break;
case Protocols.ISO15765:
this.displayDtcsImpl1(payload, srcAddress);
break;
default:
this.displayDtcsImpl2(payload, srcAddress);
break;
}
}
///
/// The actual implementation of displaying DTCs for ISO15765.
///
/// Payload to parse.
/// Source address of received data.
private void displayDtcsImpl1(byte[] payload, uint srcAddress)
{
int dtcs = payload[ModeParser.BYTE_PID];
this.iLogging.appendText("Expected DTCs=" + dtcs + "\r\n");
if (dtcs == 0)
{
// MessageBox.Show("No DTC:s exists on ECU with address 0x" + srcAddress.ToString("x2") + ".", "DTC Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
int n0 = 2;
while (n0 < payload.Length && dtcs-- > 0)
{
string dtcStr = Utils.getDtc(payload, ref n0);
this.addDtcRow(srcAddress, dtcStr);
}
}
}
///
/// The actual implementation of displaying DTCs for non-ISO15765.
///
/// Payload to parse.
/// Source address of received data.
private void displayDtcsImpl2(byte[] payload, uint srcAddress)
{
int n0 = 1;
while (n0 < payload.Length - 1)
{
string dtcStr = Utils.getDtc(payload, ref n0);
if (!dtcStr.Equals("P0000"))
{
this.addDtcRow(srcAddress, dtcStr);
}
}
}
///
/// Add one DTC row to table.
///
/// Address of originating ECU.
/// DTC to display.
private void addDtcRow(uint srcAddress, string dtcStr)
{
bool invalid = true;
string txt = string.Empty;
if (this.obdcodes.ContainsKey(dtcStr))
{
txt = (string)this.obdcodes[dtcStr];
invalid = (txt == null);
}
if (invalid)
{
txt = " * Unknown * ";
}
this.iLogging.appendText("dtcstr='" + dtcStr + "' " + txt + "\r\n");
DataGridViewRowCollection dgvrc = this.dtcDGV.Rows;
int n1 = dgvrc.Add(1);
DataGridViewRow dgvr = dgvrc[n1];
DataGridViewCellCollection dgvcc = dgvr.Cells;
dgvcc["count"].Value = (this.dtcNum++).ToString();
dgvcc["ecu"].Value = "0x" + srcAddress.ToString("x2");
dgvcc["dtc"].Value = dtcStr;
dgvcc["description"].Value = txt;
}
///
/// Button clicked for saving the DTC:s to file.
///
/// Sending object.
/// Event data.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "Reviewed.")]
private void saveDtcsButton_Click(object sender, EventArgs e)
{
if (this.dtcDGV.Rows.Count == 0)
{
MessageBox.Show(
"Nothing to save",
"Information",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
else
{
this.saveFileDialog1.FileName = "DTCS";
this.saveFileDialog1.DefaultExt = ".csv";
this.saveFileDialog1.AddExtension = true;
this.saveFileDialog1.ValidateNames = true;
this.saveFileDialog1.Filter = "Excel CSV Format|*.csv";
if (this.saveFileDialog1.ShowDialog() == DialogResult.OK)
{
if (!string.IsNullOrEmpty(this.saveFileDialog1.FileName))
{
string csvSep = ";";
if (CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator == ".")
{
csvSep = ",";
}
try
{
using (FileStream fs = (System.IO.FileStream)this.saveFileDialog1.OpenFile())
{
try
{
using (StreamWriter sw = new StreamWriter(fs))
{
try
{
sw.WriteLine("#" + csvSep + "\"ECU\"" + csvSep + "\"DTC\"" + csvSep + "\"Description\"");
DataGridViewRowCollection dgvrc = this.dtcDGV.Rows;
for (int i = 0; i < dgvrc.Count; i++)
{
DataGridViewRow dgvr = dgvrc[i];
DataGridViewCellCollection dgvcc = dgvr.Cells;
string ecuStr = (string)dgvcc["ecu"].Value;
string dtcStr = (string)dgvcc["dtc"].Value;
string descriptionStr = (string)dgvcc["description"].Value;
ecuStr = ecuStr.Replace("\"", "''");
dtcStr = dtcStr.Replace("\"", "''");
descriptionStr = descriptionStr.Replace("\"", "''");
sw.WriteLine(dgvcc["count"].Value + csvSep + "\"" + ecuStr + "\"" + csvSep + "\"" + dtcStr + "\"" + csvSep + "\"" + descriptionStr + "\"");
}
}
finally
{
try
{
sw.Close();
}
catch
{
}
}
}
}
finally
{
try
{
fs.Close();
}
catch
{
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(
ex.Message + "\r\n" + ex.StackTrace,
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
}
}
}
}
}
///
/// Link clicked for request for additional info about DTC.
///
/// This opens a web browser that points to a site with extended information.
///
///
/// Sending object.
/// Event data.
private void dtcDGV_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
int col = e.ColumnIndex;
if (col == 2)
{
string str = (string)this.dtcDGV.CurrentCell.Value;
string site = this.iPreferences.obdCodeDatabase;
if (site == null || site.Trim().Length == 0)
{
site = Utils.DEFAULT_OBD_CODE_SITE;
}
System.Diagnostics.Process.Start(site + str);
}
}
}
}