//----------------------------------------------------------------------- // // 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.OBD { using System; using System.Collections.Generic; using System.Threading; using global::SharedObjects; using global::SharedObjects.DataMgmt; using global::SharedObjects.Misc; using global::SharedObjects.Misc.Objects; using global::SharedObjects.Protocol; /// /// TODO: Update summary. /// public class MessagingEngine : IMessaging { /// /// Gets Current protocol handler instance. /// public IProtocolHandler protocolHandler { get; private set; } /// /// Gets Buffer overrun counter. /// public int overruns { get; private set; } /// /// Gets or sets Source address. /// public uint sourceAddress { get; set; } /// /// Gets or sets Destination address. /// public uint destinationAddress { get; set; } /// /// Gets or sets Current message flags. /// private uint msgFlags; /// /// Preferences interface. /// private IPreferences iPreferences; /// /// Event logging instance. /// private ILogging iLogging; /// /// Queue of functional addressed messages to send. /// private List sendList = new List(); /// /// Indicate if restart can occur. /// /// Restart is used by the gauges. /// /// private bool canRestart = true; /// /// Thread execution flag. /// private bool doRun = false; /// /// Message transmit thread instance. /// private Thread sendMsgThread; /// /// Request matching engine. /// private IRequestMatcher iRequestMatcher; /// /// Initializes a new instance of the class. /// /// Event logging instance. /// Preferences instance. /// Current protocol handler. /// Request matching engine instance. /// Message flags. /// Source address. /// Default destination address. public MessagingEngine( ILogging iLogging, IPreferences iPreferences, IProtocolHandler protocolHandler, IRequestMatcher iRequestMatcher, uint msgFlags, uint sourceAddress, uint destinationAddress) { this.iLogging = iLogging; this.iPreferences = iPreferences; this.protocolHandler = protocolHandler; this.iRequestMatcher = iRequestMatcher; this.msgFlags = msgFlags; this.sourceAddress = sourceAddress; this.destinationAddress = destinationAddress; this.overruns = 0; } /// /// Send one message. /// public void sendMsg() { this.sendMsg(false); } /// /// Send one message. /// /// Message restart flag, used to ignore request while there are outstanding messages. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Reviewed, intentional.")] public void sendMsg(bool restart) { lock (this.sendList) { if ((!restart || (restart && this.canRestart)) && this.sendList.Count > 0) { Monitor.PulseAll(this.sendList); this.canRestart = false; } else { this.canRestart = true; } } } /// /// Queue one message. /// /// Destination address to send to. /// Message to queue. public void queueMsg(uint? destinationAddress, TxMsg msg) { if (msg != null && msg.payload.Length >= 1) { IRequestData rd2 = new RequestData(destinationAddress, msg.payload[0], null, msg); this.queueMsg(rd2); } } /// /// Queue one message. /// /// Notice that if the message already exists in the out queue it's discarded /// to avoid queue overflow. /// /// /// Message to queue. /// 'true' if data was queued, 'false' if data already existed. public bool queueMsg(IRequestData msg) { bool success = false; lock (this.sendList) { if (!this.sendList.Contains(msg)) { this.sendList.Add(msg); success = true; } else { this.overruns++; } } return success; } /// /// Start the transmit thread. /// public void startThread() { this.doRun = true; this.sendMsgThread = new Thread(this.sendMsgThreadImpl); this.sendMsgThread.Name = "Msg Sender"; this.sendMsgThread.Start(); this.iLogging.appendTextLn("startThread executed."); } /// /// Stop the transmitter thread. /// public void stopThread() { this.doRun = false; this.sendList.Clear(); if (this.sendMsgThread != null) { this.sendMsgThread.Abort(); this.sendMsgThread.Interrupt(); } } /// /// Thread for sending queued messages. /// private void sendMsgThreadImpl() { this.iLogging.appendText("sendMsgThread, doRun=" + this.doRun + "\r\n"); IRequestData msg = null; while (this.doRun) { lock (this.sendList) { if (msg == null) { // A 6 second timeout to cover for out of sync situations. if (!Monitor.Wait(this.sendList, 6000)) { if (this.sendList.Count > 0) { this.iLogging.appendTextLn("ConnectionPanel.sendMsgThread(): Timeout with data present. this.sendQueue.Count=" + this.sendList.Count); } } } msg = null; /* * Look in the list for physically addressed items * that we don't have an outstanding request for. */ bool hasPhysAddress = false; for (int i = 0; i < this.sendList.Count; i++) { if (this.sendList[i].destinationAddress != null) { hasPhysAddress = true; if (!this.iRequestMatcher.hasListedAddress((uint)this.sendList[i].destinationAddress)) { msg = this.sendList[i]; break; } } } /* * If no physical items found - check for functional addressed items. */ if (msg == null && !hasPhysAddress) { for (int i = 0; i < this.sendList.Count; i++) { if (this.sendList[i].destinationAddress == null) { msg = this.sendList[i]; break; } } } } /* * If there was a message that could be sent, do that. */ if (msg != null) { bool success = this.transmitMessage(msg); /* * Remove message from queue if transmit was successful. */ if (success) { lock (this.sendList) { this.sendList.Remove(msg); } } } } this.iLogging.appendText("sendMsgThread Ended!\r\n"); } /// /// Send one message. /// /// Message to send. /// Next message or 'null' if no next message. private bool transmitMessage(IRequestData msg) { bool success = this.iRequestMatcher.addRequestData(msg); if (success) { string str; msg.txTimestamp = DateTime.Now.Ticks; if (msg.destinationAddress != null) { str = this.protocolHandler.sendMessage(this.sourceAddress, (uint)msg.destinationAddress, (int)this.msgFlags, msg.message.payload); } else { str = this.protocolHandler.sendMessage(this.sourceAddress, this.destinationAddress, (int)this.msgFlags, msg.message.payload); } } else { this.iLogging.appendTextLn("Failed to add request data msg=" + msg); } // Delay after send. Some ECUs can't take it full throttle. Thread.Sleep(this.iPreferences.sendDelay); return success; } } }