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