//----------------------------------------------------------------------- // // 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 { using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Threading; using System.Windows.Forms; using global::DataSource; using global::DataSource.FileAccess; using global::DeviceApi.J2534; using global::Protocol; using global::SharedObjects; using global::SharedObjects.Api; using global::SharedObjects.DataMgmt; using global::SharedObjects.GUI; using global::SharedObjects.GUI.Popup; using global::SharedObjects.Misc; using global::SharedObjects.Protocol; using global::SharedObjects.Protocol.OBD; using global::UserInterface.GUI.Objects; /// /// Protocols panel declaration. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Reviewed.")] public partial class ProtocolSelectPanel : UserControl, IProtocolCallback, IEnableButtons, IModeParser { /// /// Number of ECUs in chunk to probe. /// private const int ECU_CHUNK = 8; /// /// Image for yellow cog icon. /// private static Image cogYellow = null; /// /// Image for red cog icon. /// private static Image cogRed = null; /// /// Image for green cog icon. /// private static Image cogGreen = null; /// /// Image for used cog icon. /// private static Image cogNeutral = null; /// /// Image for used cog icon. /// private static Image cogError = null; /// /// Image for used cog icon. /// private static Image cogUsed = null; /// /// List of currently used protocol families. Used to reduce list of protocols in the UI to only the protocols that can be used. /// private List usedProtocolFamilyList = new List(); /// /// List of available protocols. /// private List availableProtocols = new List(); /// /// Indicate if all protocols shall be presented even though the device isn't listed to support them. /// private bool allProtocols = false; /// /// Last selected protocol. /// /// Notice that multiple protocols can be handled, and that this is a scratch area used when creating a new protocol tab. /// /// private ProtocolItemWrapper currentProtocolItemWrapper; /// /// Current device instance. /// private IPassThruDevice passThruDevice; /// /// Current PassThru interface instance. /// private IPassThru iPassThru; /// /// Current vehicle instance. /// private XmlClass.vehicle vehicle; /// /// Current node in the tree which this instance is under. /// private PanelTreeNode panelTreeNode; /// /// Current serial port handler. /// private SerialPortHandler serialPortHandler; /// /// Composite OBD code file access, this is an union of the general and vehicle specific codes. /// private ObdCodeFileAccess compositeObd; /// /// Current logging instance. /// private ILogging iLogging; /// /// Current preferences instance. /// private IPreferences iPreferences; /// /// Misc functions interface instance. /// private IMiscFunc iMiscFunc; /// /// Application tree instance. /// private IApplicationTree applicationTree; /// /// Data Source instance. /// private IDataSource iDataSource; /// /// Rows in the data grid view. /// private DataGridViewRowCollection rows; /// /// Boolean indicating that a response arrived. /// private bool gotResponse = false; /// /// Object to synchronize threads with. /// private object lockObject = new object(); /// /// Detected usable source address for tester. /// private string detectedSrcAddress; /// /// Detected source address. /// private uint srcAddr = 0; /// /// List of detected ECU addresses during a probe. /// private List detectedEcus = new List(); /// /// If extended scan of ECUs shall be done. /// /// Only applicable for 29 bit address. /// /// private bool extendedScan = false; /// /// Hint activator instance. /// private IHintActivator hintActivator; /// /// Data file directory path. /// private string dataFileDir; /// /// Initializes a new instance of the class. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Reviewed.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "CanApp.CustomToolTip", Justification = "Reviewed.")] public ProtocolSelectPanel() { this.InitializeComponent(); this.rows = this.protocolsDgv.Rows; 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.setAcceptedSrcAddrTextbox(string.Empty); this.detectedSrcAddress = string.Empty; this.toolTip1.SetToolTip(this.extendedScanCB, "Scans through all ECU address alternatives on ISO15765 29bit addressing."); } /// /// Initialize the instance. /// /// Misc functions interface instance. /// Current logging instance. /// Current preferences instance. /// Data source instance. /// Current PassThru device instance. /// Current PassThru interface instance. /// Current vehicle instance. /// Current serial port handler. /// Composite OBD code file access, this is an union of the general and vehicle specific codes. /// Hint activator instance. /// Data file directory path. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "iDataSource", Justification = "Reviewed, intentional.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "iPreferences", Justification = "Reviewed, intentional.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "iMiscFunc", Justification = "Reviewed, intentional.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "iLogging", Justification = "Reviewed, intentional.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "compositeObd", Justification = "Reviewed, intentional.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "vehicle", Justification = "Reviewed, intentional.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "passThruDevice", Justification = "Reviewed, intentional.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "iPassThru", Justification = "Reviewed, intentional.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "serialPortHandler", Justification = "Reviewed, intentional.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "hintActivator", Justification = "Reviewed, intentional.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "dataFileDir", Justification = "Reviewed, intentional.")] public void init( IMiscFunc iMiscFunc, ILogging iLogging, IPreferences iPreferences, IDataSource iDataSource, IPassThruDevice passThruDevice, IPassThru iPassThru, XmlClass.vehicle vehicle, SerialPortHandler serialPortHandler, ObdCodeFileAccess compositeObd, IHintActivator hintActivator, string dataFileDir) { this.iMiscFunc = iMiscFunc; this.iLogging = iLogging; this.iPreferences = iPreferences; this.iDataSource = iDataSource; this.passThruDevice = passThruDevice; this.iPassThru = iPassThru; this.vehicle = vehicle; this.serialPortHandler = serialPortHandler; this.compositeObd = compositeObd; this.hintActivator = hintActivator; this.dataFileDir = dataFileDir; ProtocolSelectPanel.fixImage(ref cogYellow, this.dataFileDir, "Icons\\cog_yellow.png"); ProtocolSelectPanel.fixImage(ref cogRed, this.dataFileDir, "Icons\\cog_red.png"); ProtocolSelectPanel.fixImage(ref cogGreen, this.dataFileDir, "Icons\\cog_green.png"); ProtocolSelectPanel.fixImage(ref cogNeutral, this.dataFileDir, "Icons\\cog.png"); ProtocolSelectPanel.fixImage(ref cogError, this.dataFileDir, "Icons\\cog_error.png"); ProtocolSelectPanel.fixImage(ref cogUsed, this.dataFileDir, "Icons\\cog_delete.png"); // Refresh the Protocols drop-down list. this.updateProtocols(); } /// /// Set the mapping of tree node to this instance. /// /// Application tree instance. /// Current node in the tree which this instance is under. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "panelTreeNode", Justification = "Reviewed, intentional.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "applicationTree", Justification = "Reviewed, intentional.")] public void setPanelTreeNode(IApplicationTree applicationTree, PanelTreeNode panelTreeNode) { this.applicationTree = applicationTree; this.panelTreeNode = panelTreeNode; } /// /// Close tab for connection. /// /// Panel to close. /// Protocol to release. /// Node in application tree that was closed. public void closeTab(ConnectionPanel panel, Protocols freeProtocol, PanelTreeNode protocolNode) { if (this.panelTreeNode.Nodes.Contains(protocolNode)) { this.panelTreeNode.Nodes.Remove(protocolNode); } this.applicationTree.selectNode(this.panelTreeNode); // TabPage tabPage = this.connectionList[panel]; // tabPage.Controls.Remove(panel); //// this.mainTabControl.Controls.Remove(tabPage); if (freeProtocol != null) { // Free the protocol family. foreach (int family in freeProtocol.families) { this.usedProtocolFamilyList.Remove(family); } } // Refresh the Protocols drop-down list. this.updateProtocols(); } /// /// Set the protocol text. /// /// Protocol text. public void setProtocol(string protocolText) { int i = 0; foreach (DataGridViewRow row in this.rows) { DataGridViewCellCollection cells = row.Cells; ProtocolItemWrapper protocolItemWrapper = (ProtocolItemWrapper)cells["col_item"].Value; if (protocolItemWrapper.protocol.name == protocolText) { this.selectRow(i); break; } i++; } } /// /// Enable fields on the control. /// /// 'true' to enable, 'false' to disable. public void enableFields(bool enable) { this.protocolsDgv.Enabled = enable; this.allProtocolsCB.Enabled = enable; this.detectButton.Enabled = enable && !this.iPassThru.isSerial(); this.extendedScanCB.Enabled = this.detectButton.Enabled; this.allProtocolsCB.Enabled = this.detectButton.Enabled; this.newConnectionBT.Enabled = enable && (this.passThruDevice != null) && (this.availableProtocols.Count > 0); } /// /// Clean up at a disconnection. /// public void disconnect() { foreach (PanelTreeNode protocolNode in this.panelTreeNode.Nodes) { if (protocolNode.userControl is ConnectionPanel) { ConnectionPanel connectionPanel = (ConnectionPanel)protocolNode.userControl; connectionPanel.close(); } } this.panelTreeNode.Nodes.Clear(); this.usedProtocolFamilyList.Clear(); // Refresh the Protocols drop-down list. this.updateProtocols(); } /// /// Prepare connection. /// /// Connection panel instance. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Reviewed, intentional.")] public ConnectionPanel prepareConnection() { ConnectionPanel connectionPanel = null; if (this.InvokeRequired) { try { connectionPanel = (ConnectionPanel)this.Invoke(new prepareConnectionFunc(this.prepareConnection), new object[] { }); } 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 { IPassThruConnection passThruConnection = new PassThruConnection(this.passThruDevice); connectionPanel = new ConnectionPanel( this.iLogging, this.vehicle, this.applicationTree, this.iMiscFunc, this.iPreferences, this.iDataSource, this, passThruConnection, this.currentProtocolItemWrapper, this.compositeObd.obdcodes, this.serialPortHandler, this.detectedSrcAddress, this.hintActivator, this.dataFileDir); PanelTreeNode protocolNode = new PanelTreeNode(this.currentProtocolItemWrapper.protocol.name, "cog.png", "cog_edit.png", connectionPanel); connectionPanel.setOwningNode(protocolNode); this.panelTreeNode.Nodes.Add(protocolNode); this.panelTreeNode.Expand(); this.applicationTree.selectNode(protocolNode); // Mark protocol family as used. foreach (int family in this.currentProtocolItemWrapper.protocol.families) { this.usedProtocolFamilyList.Add(family); } // Refresh the Protocols drop-down list. this.updateProtocols(); } return connectionPanel; } /// /// Indicate that the buttons shall be enabled. /// public void enableButtons() { if (this.InvokeRequired) { try { this.Invoke(new enableButtonsFunc(this.enableButtons), new object[] { }); } 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.detectButton.Enabled = !this.iPassThru.isSerial(); this.extendedScanCB.Enabled = this.detectButton.Enabled; this.allProtocolsCB.Enabled = this.detectButton.Enabled; } } /// /// Signal that initialization failed when starting connection. /// public void initFail() { } /// /// Update the progress bar with the initialization progress. /// /// Step processed. /// Total steps. public void updateProgress(int progressStep, int total) { } /// /// Provide information about detected protocol. /// /// Name of protocol. public void setProtocolInfo(string protocolName) { } #region IModeParser /// /// Gets Current PID parser instance. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Justification = "Reviewed, intentional.")] public IPidParser pidParser { get { throw new NotImplementedException("Not implemented, only here to conform to interface IModeParser."); } } /// /// Receive data to be parsed and presented. /// /// Data to be presented. /// Source address of message. /// Detected protocol by ELM/AGV adapter. public void receiveMessageData(byte[] payload, uint srcAddress, uint detectedProtocol) { if (payload != null) { if (!this.detectedEcus.Contains(srcAddress)) { this.detectedEcus.Add(srcAddress); } #if TRACE string txt = string.Empty; for (int i = 0; i < payload.Length; i++) { if (i > 0) { txt += ", "; } txt += "0x" + payload[i].ToString("x2"); } this.iLogging.appendText("0x" + srcAddress.ToString("x2") + "====> " + txt + "\r\n"); #endif lock (this.lockObject) { if (payload.Length >= 2 && payload[0] == 0x41 && payload[1] == 0x00) { this.gotResponse = true; Monitor.PulseAll(this.lockObject); } } #if TRACE this.iLogging.appendText("PULSED!!!\r\n"); #endif } } /// /// Close the service. /// public void close() { throw new NotImplementedException("Not implemented, only here to conform to interface IModeParser."); } /// /// Set up a dictionary of the known Mode/PID combinations. /// Notice that the list we use is the list that is filtered depending on preferred units. (Metric or Imperial) /// public void configureModePidDictionary() { throw new NotImplementedException("Not implemented, only here to conform to interface IModeParser."); } /// /// Register a panel able to receive data. /// /// Panel to register. public void registerPanel(IDataPanel panel) { throw new NotImplementedException("Not implemented, only here to conform to interface IModeParser."); } /// /// Set the pending requests that can be performed after the current request has had an answer. /// /// List of pending requests. public void setPendingRequests(System.Collections.Generic.IList pendingModeRequests) { throw new NotImplementedException("Not implemented, only here to conform to interface IModeParser."); } /// /// Remove registered panel. /// /// Panel to unregister. public void unregisterPanel(IDataPanel panel) { throw new NotImplementedException("Not implemented, only here to conform to interface IModeParser."); } #endregion /// /// Detect protocol. /// /// 'true' if successful. public bool detectProtocol() { bool success = false; if (this.serialPortHandler == null) { ProtocolItemWrapper matchedProtocol = null; this.detectedEcus.Clear(); try { this.setImageRows(cogNeutral); this.setAcceptedSrcAddrTextbox(string.Empty); this.srcAddr = 0; bool failure = false; for (int i = 0; i < this.rows.Count && !success && !failure; i++) { this.setRowImage(i, cogYellow); DataGridViewCellCollection cells = this.getRowCells(i); ProtocolItemWrapper protocolItemWrapper = this.getProtocolItemWrapper(cells); this.logInfo(cells, protocolItemWrapper); bool is29bit; uint currentSourceAddress; getProtocolBaseInfo(protocolItemWrapper, out is29bit, out currentSourceAddress); List ecus = this.getProbeEcus(protocolItemWrapper, is29bit); IPassThruConnection passThruConnection = new PassThruConnection(this.passThruDevice); PassThruConstants.resultCode status = performConnect(protocolItemWrapper, passThruConnection); try { if (status == PassThruConstants.resultCode.ERR_SUCCESS) { int timeout = protocolItemWrapper.protocol.timeout; MessageHandler messageHandler = new MessageHandler(this.iLogging, timeout, passThruConnection, protocolItemWrapper.protocol); if (is29bit) { this.try29bitProtocol(ref success, ref matchedProtocol, ref failure, i, protocolItemWrapper, is29bit, ecus, passThruConnection, messageHandler); } else { this.tryOtherProtocol(ref success, ref matchedProtocol, ref failure, i, protocolItemWrapper, is29bit, currentSourceAddress, ecus, passThruConnection, messageHandler); } } else { this.setRowImage(i, cogUsed); } } catch (Exception ex) { this.iLogging.appendText(ex.GetType().ToString() + "\r\n" + ex.Message + "\r\n" + ex.StackTrace + "\r\n"); this.setRowImage(i, cogError); } finally { PassThruConstants.resultCode closeStatus = passThruConnection.Gl_PassThruDisconnect(); this.iLogging.errTestConnection(passThruConnection, closeStatus); } if (success) { this.setRowImage(i, cogGreen); } else { if (failure) { this.setRowImage(i, cogError); } else { this.setRowImage(i, cogRed); } } } } catch (Exception ex) { this.iLogging.appendText(ex.Message + "\r\n" + ex.StackTrace + "\r\n"); } finally { this.enableButtons(); } this.iLogging.appendText("detectedEcus = " + this.detectedEcus.Count + "\r\n"); } else { // Serial port dongles shall handle the handshake themselves, so we just accept them. success = true; } return success; } /// /// Get some base info for the protocol. /// /// Protocol data wrapper. /// set to 'true' if ISO15765 29-bit. /// Source address to try (for some protocols). private static void getProtocolBaseInfo(ProtocolItemWrapper protocolItemWrapper, out bool is29bit, out uint currentSourceAddress) { is29bit = false; currentSourceAddress = 0x00; if (protocolItemWrapper.addressData != null && protocolItemWrapper.addressData.baseSourceAddress != null) { currentSourceAddress = Utils.hexParse(protocolItemWrapper.addressData.baseSourceAddress); is29bit = (protocolItemWrapper.addressData.bits == 29); } } /// /// Perform connection. /// /// Protocol item wrapper. /// Passthru connection instance. /// Status code. private static PassThruConstants.resultCode performConnect(ProtocolItemWrapper protocolItemWrapper, IPassThruConnection passThruConnection) { PassThruConstants.resultCode status; if (protocolItemWrapper.addressData != null) { status = passThruConnection.Gl_PassThruConnect((int)protocolItemWrapper.protocol.id, protocolItemWrapper.addressData.connFlags, protocolItemWrapper.commspeed.bps); } else { if (protocolItemWrapper.protocol.id == Protocols.ISO9141 || protocolItemWrapper.protocol.id == Protocols.ISO14230) { status = passThruConnection.Gl_PassThruConnect( (int)protocolItemWrapper.protocol.id, PassThruConstants.connflags.ISO9141_K_LINE_ONLY | PassThruConstants.connflags.ISO9141_NO_CHECKSUM, protocolItemWrapper.commspeed.bps); } else { status = passThruConnection.Gl_PassThruConnect( (int)protocolItemWrapper.protocol.id, 0x00, protocolItemWrapper.commspeed.bps); } } return status; } /// /// A little workaround to resolve the problem of not knowing the path /// in the designer mode. /// /// Image instance. /// Data file directory path. /// Sub-path to specific image. E.g. "Icons\cog_yellow.png" private static void fixImage(ref Image img, string dataFileDir, string imgPath) { if (img == null) { string basePath; if (dataFileDir == null) { DataSourceImplementation.locateDataFiles(); } if (dataFileDir == null) { // Ugly hard-coding. basePath = @"E:\VsProjects\CanApp2\CanApp\AppData\"; } else { basePath = dataFileDir; } img = Image.FromFile(basePath + imgPath); } } /// /// Prepare connection. /// /// Connection panel instance. private delegate ConnectionPanel prepareConnectionFunc(); /// /// Create a new connection. /// This doesn't open the connection, just creates a new tab with the values /// applicable for connection. /// /// Sending object. /// Event data. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Reviewed, intentional.")] private void newConnectionBT_Click(object sender, EventArgs e) { this.prepareConnection(); } /// /// Update protocols drop-down. /// private void updateProtocols() { this.availableProtocols.Clear(); this.rows.Clear(); if (this.iPassThru != null) { Protocols[] deviceSupportedProtocols = this.iPassThru.getProtocols(); for (int i1 = 0; i1 < Protocols.protocolScanOrder.Length; i1++) { Protocols currentProtocol = Protocols.getProtocolById(Protocols.protocolScanOrder[i1]); #if TRACE this.iLogging.appendText("currentProtocol=" + currentProtocol.name + ", currentProtocol.canDetect=" + currentProtocol.canDetect + "\r\n"); #endif bool supportedProtocol = this.allProtocols; if (!supportedProtocol) { foreach (Protocols deviceSupportedProtocol in deviceSupportedProtocols) { if (deviceSupportedProtocol.id == Protocols.protocolScanOrder[i1]) { supportedProtocol = true; break; } } } if (supportedProtocol) { bool usedFamily = false; foreach (int family in currentProtocol.families) { if (this.usedProtocolFamilyList.Contains(family)) { usedFamily = true; break; } } if (!usedFamily) { this.availableProtocols.Add(currentProtocol); } List speeds = this.getSpeedsForProtocol(currentProtocol); if (speeds != null && speeds.Count > 0) { speeds.Reverse(); foreach (XmlClass.commspeed speed in speeds) { if (currentProtocol.addressDataList != null && currentProtocol.addressDataList.Count > 0) { foreach (Protocols.AddressData addr in currentProtocol.addressDataList) { string nameData = currentProtocol.name + " / " + speed.name + " / " + addr.bits + " bits"; this.addRow(currentProtocol, usedFamily, speed, addr, nameData); } } else { string nameData = currentProtocol.name + " / " + speed.name; this.addRow(currentProtocol, usedFamily, speed, null, nameData); } } } else { string nameData = currentProtocol.name; this.addRow(currentProtocol, usedFamily, null, null, nameData); } } } } this.newConnectionBT.Enabled = (this.passThruDevice != null) && (this.availableProtocols.Count > 0); this.detectButton.Enabled = (this.rows.Count > 1) && !this.iPassThru.isSerial(); this.extendedScanCB.Enabled = this.detectButton.Enabled; this.allProtocolsCB.Enabled = this.detectButton.Enabled; if (this.rows.Count > 0) { this.updateProtocolFromRow(0); } } /// /// Add one row to the protocols table. /// /// Protocol to add. /// Protocol family. /// Interface speed. /// Address data information. /// Text description of row data. private void addRow(Protocols currentProtocol, bool usedFamily, XmlClass.commspeed speed, Protocols.AddressData addr, string nameData) { int rowNum = this.rows.Add(); DataGridViewRow row = this.rows[rowNum]; DataGridViewCellCollection cells = row.Cells; cells["col_protocol"].Value = nameData; cells["col_item"].Value = new ProtocolItemWrapper(currentProtocol, speed, addr); if (!usedFamily) { cells["col_status"].Value = cogNeutral; } else { cells["col_status"].Value = cogUsed; } } /// /// Handle change of check state in checkbox for All Protocols. /// /// Sending object. /// Event data. private void allProtocolsCB_CheckedChanged(object sender, EventArgs e) { CheckBox cb = (CheckBox)sender; if (cb.Focused) { this.allProtocols = cb.Checked; // Refresh Protocol list. this.updateProtocols(); } } /// /// Try to detect protocol. /// /// Sending object. /// Event data. private void detectButton_Click(object sender, EventArgs e) { if (this.rows.Count > 1) { this.detectButton.Enabled = false; this.extendedScanCB.Enabled = this.detectButton.Enabled; this.allProtocolsCB.Enabled = this.detectButton.Enabled; Thread thr = new Thread(this.detectThread); thr.Start(); } } /// /// Thread performing the detection in the background to avoid locking up the UI. /// private void detectThread() { this.detectProtocol(); } /// /// Try ISO15765 29-bit. /// /// Set to 'true' if successful match. /// Instance of matched protocol. /// Set to 'true' if failure. /// Index value. /// Probed protocol. /// 'true' if 29 bit. (shall always be true here) /// Array of ECUs to probe. /// Current device connection. /// Current message handler instance. private void try29bitProtocol( ref bool success, ref ProtocolItemWrapper matchedProtocol, ref bool failure, int i, ProtocolItemWrapper protocolItemWrapper, bool is29bit, List ecus, IPassThruConnection passThruConnection, MessageHandler messageHandler) { #if TRACE int i0 = 0; #endif if (this.srcAddr != 0) { int start = 0; int count = ecus.Count - start; while (count > 0 && !failure) { if (count > ECU_CHUNK) { count = ECU_CHUNK; } XmlClass.vehicle.ecuData[] ecuArr = ecus.GetRange(start, count).ToArray(); this.iLogging.appendText("ECUS:"); foreach (XmlClass.vehicle.ecuData ecuItem in ecuArr) { this.iLogging.appendText(" " + ecuItem.id); } this.iLogging.appendText("\r\n"); start += count; count = ecus.Count - start; if (ecuArr.Length > 0) { this.tryProtocol( ref success, ref matchedProtocol, ecuArr, ref failure, i, protocolItemWrapper, passThruConnection, this.srcAddr, is29bit, messageHandler); } } } else { foreach (uint addrPart in protocolItemWrapper.addressData.srcAddr) { #if TRACE this.iLogging.appendText("srcAddr[" + i0 + "]='0x" + addrPart.ToString("x2") + "'\r\n"); i0++; #endif int start = 0; int count = ecus.Count - start; while (count > 0 && !failure) { if (count > ECU_CHUNK) { count = ECU_CHUNK; } XmlClass.vehicle.ecuData[] ecuArr = ecus.GetRange(start, count).ToArray(); this.iLogging.appendText("ECUS:"); foreach (XmlClass.vehicle.ecuData ecuItem in ecuArr) { this.iLogging.appendText(" " + ecuItem.id); } this.iLogging.appendText("\r\n"); start += count; count = ecus.Count - start; if (ecuArr.Length > 0) { this.tryProtocol( ref success, ref matchedProtocol, ecuArr, ref failure, i, protocolItemWrapper, passThruConnection, addrPart, is29bit, messageHandler); if (success && this.srcAddr == 0) { this.srcAddr = addrPart; this.detectedSrcAddress = "0x" + addrPart.ToString("x2"); this.setAcceptedSrcAddrTextbox(this.detectedSrcAddress); } } } if (success) { break; } } } } /// /// Try any other protocol but ISO15765 29-bit. /// /// Set to 'true' if successful match. /// Instance of matched protocol. /// Set to 'true' if failure. /// Index value. /// Probed protocol. /// 'true' if 29 bit. (shall always be false here) /// Current source address to use. /// Array of ECUs to probe. /// Passthru connection instance. /// Current message handler instance. private void tryOtherProtocol( ref bool success, ref ProtocolItemWrapper matchedProtocol, ref bool failure, int i, ProtocolItemWrapper protocolItemWrapper, bool is29bit, uint currentSourceAddress, List ecus, IPassThruConnection passThruConnection, MessageHandler messageHandler) { #if TRACE this.iLogging.appendText("srcAddr='0x" + currentSourceAddress.ToString("x2") + "'\r\n"); #endif int start = 0; int count = ecus.Count - start; while (count > 0 && !failure) { if (count > ECU_CHUNK) { count = ECU_CHUNK; } XmlClass.vehicle.ecuData[] ecuArr = ecus.GetRange(start, count).ToArray(); this.iLogging.appendText("ECUS:"); foreach (XmlClass.vehicle.ecuData ecuItem in ecuArr) { this.iLogging.appendText(" " + ecuItem.id); } this.iLogging.appendText("\r\n"); start += count; count = ecus.Count - start; if (ecuArr.Length > 0) { this.tryProtocol( ref success, ref matchedProtocol, ecuArr, ref failure, i, protocolItemWrapper, passThruConnection, currentSourceAddress, is29bit, messageHandler); } } } /// /// Get list of ECUs to probe. /// /// Wrapper for current protocol data. /// 'true' if 29 bit addressing. /// List of ECUs private List getProbeEcus(ProtocolItemWrapper protocolItemWrapper, bool is29bit) { List ecus; if (is29bit && this.extendedScan) { ecus = new List(); for (int j = 0; j < 256; j++) { XmlClass.vehicle.ecuData ecuData = new XmlClass.vehicle.ecuData(); ecuData.id = "0x" + j.ToString("x2"); ecuData.name = string.Empty; ecus.Add(ecuData); } } else { ecus = this.getProbeEcus(protocolItemWrapper); } return ecus; } /// /// Get list of ECUs to probe. /// /// Wrapper for current protocol data. /// List of ECUs private List getProbeEcus(ProtocolItemWrapper protocolItemWrapper) { List ecus = new List(); if (protocolItemWrapper.protocol.id == Protocols.ISO15765 && protocolItemWrapper.addressData.bits == 11) { for (int j = 0; j < 8; j++) { XmlClass.vehicle.ecuData ecuItem = new XmlClass.vehicle.ecuData(); ecuItem.id = "0x" + j.ToString("x2"); ecuItem.name = string.Empty; ecus.Add(ecuItem); } } else { if (this.vehicle.ecus != null && this.vehicle.ecus.Count > 0) { ecus = new List(this.vehicle.ecus); } foreach (uint ecuAddr in Protocols.genericAddressList) { XmlClass.vehicle.ecuData ecuItem = new XmlClass.vehicle.ecuData(); ecuItem.id = "0x" + ecuAddr.ToString("x2"); ecuItem.name = string.Empty; ecus.Add(ecuItem); } } return ecus; } /// /// Set text to source address text box. /// /// Detected source address text string. private delegate void setAcceptedSrcAddrTextboxFunc(string detectedSrcAddress); /// /// Set text to source address text box. /// /// Detected source address text string. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "detectedSrcAddress", Justification = "Reviewed, intentional.")] private void setAcceptedSrcAddrTextbox(string detectedSrcAddress) { if (this.acceptedSrcAddrTB.InvokeRequired) { try { this.Invoke(new setAcceptedSrcAddrTextboxFunc(this.setAcceptedSrcAddrTextbox), new object[] { detectedSrcAddress }); } 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.acceptedSrcAddrTB.Text = detectedSrcAddress; } } /// /// Try one protocol. /// /// Set to 'true' if successful. /// Wrapper for detected protocol. /// Array of ECUs to probe. /// 'true' if probe failed. /// Index value. /// Wrapper for probed protocol. /// Current device connection. /// Source address (tester address). /// 'true' if 29 bit addressing. /// Message handler instance. private void tryProtocol( ref bool success, ref ProtocolItemWrapper matchedProtocol, XmlClass.vehicle.ecuData[] ecuArr, ref bool failure, int i, ProtocolItemWrapper protocolItemWrapper, IPassThruConnection passThruConnection, uint currentSourceAddress, bool is29bit, MessageHandler messageHandler) { IProtocolHandler protocolHandler = this.getProtocolHandler(protocolItemWrapper, currentSourceAddress, is29bit, messageHandler); if (protocolHandler != null) { protocolHandler.addModeParser(this); try { if (protocolItemWrapper.protocol.id > 0) { uint messageFlags; if (is29bit) { messageFlags = PassThruConstants.txflags.CAN_29BIT_ID | PassThruConstants.txflags.ISO15765_FRAME_PAD; } else { messageFlags = PassThruConstants.txflags.ISO15765_FRAME_PAD; } FilterHandler filterHandler = null; try { filterHandler = new FilterHandler( this.iLogging, passThruConnection, protocolHandler, ecuArr, messageFlags, protocolItemWrapper); messageHandler.startThread(); protocolHandler.init(0x00); this.gotResponse = false; this.sendProbeMessage(protocolHandler.srcAddr, is29bit, protocolHandler); lock (this.lockObject) { if (!this.gotResponse) { if (!Monitor.Wait(this.lockObject, protocolHandler.protocol.timeout * 2)) { if (this.gotResponse) { this.iLogging.appendTextLn("ProtocolSelectPanel.tryProtocol(): Timeout with data present."); } } } } matchedProtocol = this.handlePossibleResponse(protocolItemWrapper); #if TRACE this.iLogging.appendText("this.gotResponse='" + this.gotResponse + "'\r\n"); #endif if (matchedProtocol != null && matchedProtocol.protocol != null) { this.iLogging.appendText("Matched Protocol = '" + matchedProtocol.protocol.name + "'\r\n"); this.selectRow(i); success = true; } } catch (ProtocolInitException ex) { #if TRACE this.iLogging.appendText(ex.Message + "\r\n" + ex.StackTrace + "\r\n"); #endif this.iLogging.appendText(ex.Message + "\r\n"); } catch (Exception ex) { #if TRACE this.iLogging.appendText(ex.Message + "\r\n" + ex.StackTrace + "\r\n"); #endif this.iLogging.appendText(ex.Message + "\r\n"); failure = true; } finally { #if TRACE this.iLogging.appendText("Stopping messageHandler Thread.\r\n"); #endif messageHandler.stopThread(); if (filterHandler != null) { filterHandler.removeAllFilters(); } } } } finally { protocolHandler.removeModeParser(this); protocolHandler.stopProtocol(); } } } /// /// Select the given row in the table. /// /// Number of row to select. private delegate void selectRowFunc(int i); /// /// Select the given row in the table. /// /// Number of row to select. private void selectRow(int i) { if (this.InvokeRequired) { try { this.Invoke(new selectRowFunc(this.selectRow), new object[] { i }); } 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.protocolsDgv.ClearSelection(); this.protocolsDgv.Rows[i].Selected = true; this.protocolsDgv.CurrentCell = this.protocolsDgv[0, i]; } } /// /// Handle and act on possible response from vehicle. /// /// Protocol item wrapper. /// Wrapper containing information about detected protocol. private ProtocolItemWrapper handlePossibleResponse(ProtocolItemWrapper protocolItemWrapper) { ProtocolItemWrapper matchedProtocol = null; if (this.gotResponse) { matchedProtocol = protocolItemWrapper; } return matchedProtocol; } /// /// Send a probe message that shall provide a response. /// /// Source address (address of tester). /// 'true' if 29 bit addressing. /// Protocol handler instance. private void sendProbeMessage(uint currentSourceAddress, bool is29bit, IProtocolHandler protocolHandler) { this.iLogging.appendText("is29bit=" + is29bit + "\r\n"); if (is29bit) { protocolHandler.sendProbeMessage(currentSourceAddress, PassThruConstants.txflags.CAN_29BIT_ID | PassThruConstants.txflags.ISO15765_FRAME_PAD, TxMsg.GET_SUPPORTED_PIDS); } else { protocolHandler.sendProbeMessage(0x00, PassThruConstants.txflags.ISO15765_FRAME_PAD, TxMsg.GET_SUPPORTED_PIDS); } } /// /// Get the protocol handler instance for the protocol. /// /// Protocol item wrapper instance. /// Source Address. /// 'true' if 29 bit addressing. /// Message Handler instance. /// Protocol handler instance. private IProtocolHandler getProtocolHandler(ProtocolItemWrapper protocolItemWrapper, uint currentSourceAddress, bool is29bit, MessageHandler messageHandler) { IProtocolHandler protocolHandler; switch (protocolItemWrapper.protocol.id) { case Protocols.J1850VPW: case Protocols.J1850PWM: protocolHandler = new Protocol_J1850( this.iLogging, protocolItemWrapper.protocol, messageHandler, currentSourceAddress, true, this); break; case Protocols.ISO9141: case Protocols.ISO14230: protocolHandler = new Protocol_ISO14230( this.iLogging, protocolItemWrapper.protocol, messageHandler, currentSourceAddress, false, this); break; case Protocols.ISO15765: protocolHandler = new Protocol_ISO15765( this.iLogging, protocolItemWrapper.protocol, messageHandler, currentSourceAddress, false, is29bit, this); break; default: protocolHandler = null; break; } return protocolHandler; } /// /// Write some info to the event log. /// /// Cells from the current row. /// Protocol item wrapper instance. private delegate void logInfoFunc(DataGridViewCellCollection cells, ProtocolItemWrapper protocolItemWrapper); /// /// Write some info to the event log. /// /// Cells from the current row. /// Protocol item wrapper instance. private void logInfo(DataGridViewCellCollection cells, ProtocolItemWrapper protocolItemWrapper) { if (this.InvokeRequired) { try { this.Invoke(new logInfoFunc(this.logInfo), new object[] { cells, protocolItemWrapper }); } 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 { #if TRACE this.iLogging.appendText("===================================================\r\n"); this.iLogging.appendText(cells["col_protocol"].Value + "\r\n"); this.iLogging.appendText("protocolItemWrapper.protocol.canDetect = " + protocolItemWrapper.protocol.canDetect + ", cells[\"col_status\"].Value=" + cells["col_status"].Value + "\r\n"); #endif } } /// /// Get the protocol item wrapper instance from the cell collection. /// /// Cell collection to get the item wrapper from. /// Item wrapper instance. private delegate ProtocolItemWrapper getProtocolItemWrapperFunc(DataGridViewCellCollection cells); /// /// Get the protocol item wrapper instance from the cell collection. /// /// Cell collection to get the item wrapper from. /// Item wrapper instance. private ProtocolItemWrapper getProtocolItemWrapper(DataGridViewCellCollection cells) { ProtocolItemWrapper protocolItemWrapper = null; if (this.InvokeRequired) { try { protocolItemWrapper = (ProtocolItemWrapper)this.Invoke(new getProtocolItemWrapperFunc(this.getProtocolItemWrapper), new object[] { cells }); } 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 { protocolItemWrapper = (ProtocolItemWrapper)cells["col_item"].Value; } return protocolItemWrapper; } /// /// Get the cells for a table row. /// /// Row number. /// Collection of cells for the row. private delegate DataGridViewCellCollection getRowCellsFunc(int i); /// /// Get the cells for a table row. /// /// Row number. /// Collection of cells for the row. private DataGridViewCellCollection getRowCells(int i) { DataGridViewCellCollection cells = null; if (this.InvokeRequired) { try { cells = (DataGridViewCellCollection)this.Invoke(new getRowCellsFunc(this.getRowCells), new object[] { i }); } 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 { cells = this.rows[i].Cells; } return cells; } /// /// Set the status image of the status cell on all rows. /// /// Image to place in cell. private void setImageRows(Image img) { for (int i = 0; i < this.rows.Count; i++) { this.setRowImage(i, img); } } /// /// Set the status image of the status cell on the given row. /// /// Row number to update. /// Image to place in cell. private delegate void setRowImageFunc(int i, Image img); /// /// Set the status image of the status cell on the given row. /// /// Row number to update. /// Image to place in cell. private void setRowImage(int i, Image img) { if (this.InvokeRequired) { try { this.Invoke(new setRowImageFunc(this.setRowImage), new object[] { i, img }); } 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 { DataGridViewRow row = this.rows[i]; DataGridViewCellCollection cells = row.Cells; ProtocolItemWrapper protocolItemWrapper = (ProtocolItemWrapper)cells["col_item"].Value; if (protocolItemWrapper.protocol.canDetect) { this.setStatusImage(cells, img); } } } /// /// Set the status image of the status cell. /// /// Cell collection for the row to be updated. /// Image to place in cell. private delegate void setStatusImageFunc(DataGridViewCellCollection cells, Image img); /// /// Set the status image of the status cell. /// /// Cell collection for the row to be updated. /// Image to place in cell. private void setStatusImage(DataGridViewCellCollection cells, Image img) { if (this.InvokeRequired) { try { this.Invoke(new setStatusImageFunc(this.setStatusImage), new object[] { cells, img }); } 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 { cells["col_status"].Value = img; } } /// /// Get the speeds supporting the giving protocol. /// /// Protocol to get speed list for. /// Speed list. private List getSpeedsForProtocol(Protocols probeProtocol) { List speeds = new List(); foreach (XmlClass.commspeed speedItem in this.iDataSource.commspeeds) { if (probeProtocol.id == Protocols.ISO15765 && speedItem.validISO15765) { speeds.Add(speedItem); } if ((probeProtocol.id == Protocols.ISO9141 || probeProtocol.id == Protocols.ISO14230) && speedItem.validISO9141) { speeds.Add(speedItem); } if ((probeProtocol.id == Protocols.J1850PWM || probeProtocol.id == Protocols.J1850VPW) && speedItem.validJ1850) { speeds.Add(speedItem); } } return speeds; } /// /// Handle changed row in table. /// /// Sending object. /// Event data. private void protocolsDgv_RowEnter(object sender, DataGridViewCellEventArgs e) { int rowIndex = e.RowIndex; this.updateProtocolFromRow(rowIndex); } /// /// Update the protocol from the given row. /// /// Row number. private void updateProtocolFromRow(int rowIndex) { DataGridViewRow row = this.rows[rowIndex]; DataGridViewCellCollection cells = row.Cells; this.newConnectionBT.Enabled = (this.passThruDevice != null) && (cells["col_status"].Value != cogUsed); ProtocolItemWrapper protocolItemWrapper = (ProtocolItemWrapper)cells["col_item"].Value; if (protocolItemWrapper != null) { this.currentProtocolItemWrapper = protocolItemWrapper; if (protocolItemWrapper.addressData != null && protocolItemWrapper.addressData.srcAddr != null && protocolItemWrapper.addressData.srcAddr.Length > 0) { this.detectedSrcAddress = "0x" + protocolItemWrapper.addressData.srcAddr[0].ToString("x2"); } else { this.detectedSrcAddress = "0xf1"; // Best guess. } } else { this.currentProtocolItemWrapper = null; this.detectedSrcAddress = null; } } /// /// Indicate that the buttons shall be enabled. /// private delegate void enableButtonsFunc(); /// /// Toggle the extended scan flag. /// /// Sending object. /// Event data. private void extendedScanCB_CheckedChanged(object sender, EventArgs e) { this.extendedScan = this.extendedScanCB.Checked; } } }