//----------------------------------------------------------------------- // // 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); } } } }