//----------------------------------------------------------------------- // // 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 { using System; using System.Threading; using System.Windows.Forms; using global::SharedObjects; using global::SharedObjects.Api; using global::SharedObjects.Protocol; /// /// A layer that acts as an intermediate between the API and the protocol handler. /// public class MessageHandler : IMessageHandler { /// /// Size of a temporary buffer when reading/writing to the raw log. /// public const int BUFFER_SIZE = 65536; /// /// Locking object to handle concurrency when writing to raw log file. /// private object messageLockObjectInstance = new object(); /// /// Gets locking object to handle concurrency when writing to raw log file. /// public object messageLockObject { get { return this.messageLockObjectInstance; } } /// /// Logger interface. /// private ILogging iLogging; /// /// Current timeout for an operation. /// private int timeout; /// /// Current connection instance. /// private IPassThruConnection passThruConnection; /// /// Current protocol handler instance. /// private IMsgCallback protocolHandler = null; /// /// Current raw data receiver. /// private IRawReceiver rawReceiver = null; /// /// Message counter. /// private uint msgNum = 0; /// /// Current protocol. /// private Protocols protocol; /// /// Thread handling receiving of messages. /// private Thread msgReceiverThread = null; /// /// Flag to control receiver thread. /// private bool doRun = true; /// /// Locking object to handle thread management. /// private object threadMgmtLockObject = new object(); /// /// Initializes a new instance of the class. /// /// Logger instance. /// Initial timeout value. /// Current device connection instance. /// Current protocol instance. public MessageHandler(ILogging iLogging, int timeout, IPassThruConnection passThruConnection, Protocols protocol) { this.iLogging = iLogging; this.timeout = timeout; this.passThruConnection = passThruConnection; this.protocol = protocol; } /// /// Configure the protocol handler for this message handler. /// /// Protocol handler instance. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "protocolHandler", Justification = "Intentional.")] public void setProtocolCallback(IMsgCallback protocolHandler) { this.protocolHandler = protocolHandler; } /// /// Change the timeout value. /// /// New timeout value. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "timeout", Justification = "Intentional.")] public void setTimeout(int timeout) { this.timeout = timeout; } /// /// Perform a five baud initialization. (Only for ISO9141, KWP2000) /// /// Target address. /// Number of received bytes. /// Keyword 1. /// Keyword 2. /// Result status, 0=success. public PassThruConstants.resultCode IoctlFiveBaudInit(uint target, out uint outBytes, out uint keyword1, out uint keyword2) { return this.passThruConnection.IoctlFiveBaudInit(target, out outBytes, out keyword1, out keyword2); } /// /// Perform a fast initialization. (Only KWP2000) /// /// Protocol ID /// Source address to use. /// Result message from init. /// Result status, 0=success. public PassThruConstants.resultCode IoctlFastInit(int protocolId, uint srcaddress, out IPassThruMsg result) { return this.passThruConnection.IoctlFastInit(protocolId, srcaddress, out result); } /// /// Set the receiver of raw messages for this protocol handler. /// Notice that the value may be 'null'. /// /// Instance of raw data receiver. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "rawReceiver", Justification = "Intentional.")] public void setRawReceiver(IRawReceiver rawReceiver) { this.rawReceiver = rawReceiver; } /// /// Send the given message. /// /// Message to send. /// Status code from operation, 0=Success. public PassThruConstants.resultCode sendMessage(IPassThruMsg txmsg) { #if TRACE_COMM this.iLogging.dispMsg("Tx", txmsg); #endif // Transmit message. int n1 = 1; PassThruConstants.resultCode status = this.passThruConnection.Gl_PassThruWriteMsgs(txmsg, ref n1, this.timeout); string errtext; if (!this.iLogging.errTestConnection(this.passThruConnection, status, out errtext)) { this.iLogging.appendText("WriteMsgs res=" + status + ", n=" + n1 + ", errtxt=" + errtext + "\r\n"); this.iLogging.dispMsg("TxErr", txmsg); } return status; } /// /// Start Message Listener thread. /// public void startThread() { lock (this.threadMgmtLockObject) { this.iLogging.appendText("CAN Msg Receiver Thread starting.\r\n"); this.msgReceiverThread = new Thread(new ThreadStart(this.receiverThread)); this.msgReceiverThread.Name = "Msg Receiver"; this.msgReceiverThread.Start(); } } /// /// Stop the Message Listener thread. /// public void stopThread() { lock (this.threadMgmtLockObject) { this.doRun = false; this.iLogging.appendText("CAN Msg Receiver Thread stopping.\r\n"); // Check if already dead. if (this.msgReceiverThread != null && !this.msgReceiverThread.IsAlive) { this.msgReceiverThread = null; } // Not dead, abort it! if (this.msgReceiverThread != null) { this.iLogging.appendText("CAN Msg Receiver Thread aborting...\r\n"); this.msgReceiverThread.Abort(); this.iLogging.appendText("CAN Msg Receiver Thread interrupted...\r\n"); this.msgReceiverThread.Interrupt(); this.iLogging.appendText("CAN Msg Receiver Thread joining...\r\n"); this.msgReceiverThread.Join(4000); this.msgReceiverThread = null; this.iLogging.appendText("CAN Msg Receiver Thread stopped.\r\n"); } } } /// /// Get the payload part of the message with the header stripped off. /// /// Message to get payload from. /// Byte array with payload data. public byte[] getPayload(IPassThruMsg msg) { return this.protocolHandler.getPayload(msg); } /// /// Starts a periodically sent message, e.g. for keep alive of a connection. /// /// Message to send. /// Message ID. /// Status of action. public PassThruConstants.resultCode startPeriodicMessage(IPassThruMsg msg, out int msgId) { return this.passThruConnection.Gl_PassThruStartPeriodicMsg(msg, out msgId, 3000); } /// /// Stops a periodically sent message. /// /// ID of message to stop as provided by startPeriodicMessage() public void stopPeriodicMessage(int msgId) { this.passThruConnection.Gl_PassThruStopPeriodicMsg(msgId); } /// /// Glue method for error handler. /// /// Result code. /// Error text. /// 'true' if execution can continue. public bool errTestConnection(PassThruConstants.resultCode res, out string errtext) { return this.iLogging.errTestConnection(this.passThruConnection, res, out errtext); } /// /// Handle execution of background thread that performs the communication /// with the USB device. /// The received message is then dispatched to one or two handlers /// depending on conditions. /// [System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions] private void receiverThread() { this.iLogging.appendText("Receiver Thread started.\r\n"); // Outside loop to avoid performance penalty when allocating/deallocating. IPassThruMsg rxmsg; this.doRun = true; try { while (this.doRun) { int numRxMsg = 1; this.msgNum++; PassThruConstants.resultCode res = this.passThruConnection.Gl_PassThruReadMsgs((int)this.protocol.id, out rxmsg, ref numRxMsg, this.timeout); if (this.doRun) { if (res != 0) { switch (res) { case PassThruConstants.resultCode.READ_FAIL: this.iLogging.appendText("[" + this.msgNum + "]: Read Fail - Terminating.\r\n", LogLevel.LOG_WARN); this.iLogging.appendText("Last Error: " + this.passThruConnection.lastError + "\r\n"); this.doRun = false; break; case PassThruConstants.resultCode.NO_CHANNEL: this.iLogging.appendText("[" + this.msgNum + "]: Not Connected - please connect first.\r\n", LogLevel.LOG_WARN); this.doRun = false; break; case PassThruConstants.resultCode.ERR_TIMEOUT: // Timeout - no action necessary. case PassThruConstants.resultCode.ERR_BUFFER_EMPTY: // Timeout - no action necessary. break; default: this.iLogging.appendText("[" + this.msgNum + "]: {" + numRxMsg + "} [" + res + "] " + this.passThruConnection.lastError + "\r\n", LogLevel.LOG_WARN); break; } Thread.Sleep(200); // To avoid flooding. } else { try { // Check which receivers that may want the message. // Notice that the rawReceiver can decide that it // shall be the only one that sees this message. bool interpretResponse = true; if ((rxmsg.RxStatus & PassThruConstants.rxflags.START_OF_MESSAGE) != 0) { #if TRACE_COMM this.iLogging.dispMsg("START_OF_MESSAGE: Rx", rxmsg); #endif } else { if ((rxmsg.RxStatus & PassThruConstants.rxflags.START_OF_MESSAGE) == 0 && (rxmsg.RxStatus & PassThruConstants.rxflags.TX_MSG_TYPE) == 0) { #if TRACE_COMM this.iLogging.dispMsg("Rx", rxmsg); #endif if (this.rawReceiver != null) { interpretResponse = this.rawReceiver.rawHandler(rxmsg); } if (interpretResponse && this.protocolHandler != null) { this.protocolHandler.receiveMessage(rxmsg); } } } } catch (ThreadAbortException) { throw; } catch (IndexOutOfRangeException ex) { this.iLogging.appendText("Index out of range, corrupt message?\r\n" + ex.Message + "\r\n\r\n" + ex.StackTrace + "\r\n", LogLevel.LOG_WARN); this.iLogging.dispMsg("Ex", rxmsg); } catch (Exception ex) { MessageBox.Show( ex.Message + "\r\n\r\n" + ex.StackTrace, "Exception Encountered!", MessageBoxButtons.OK, MessageBoxIcon.Stop); this.iLogging.dispMsg("Ex", rxmsg); throw; } } } } } catch (ThreadAbortException) { throw; } catch (Exception ex) { this.iLogging.appendText(ex.GetType().ToString() + "\r\n" + ex.Message + "\r\n" + ex.StackTrace + "\r\n"); } finally { this.iLogging.appendText("CAN Msg Receiver Thread ended.\r\n"); } } } }