//-----------------------------------------------------------------------
//
// 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 VectorComm
{
using System;
using System.Collections.Generic;
using System.Threading;
using SharedObjects;
using SharedObjects.CAN;
using vxlapi_NET20;
///
/// Vector (http://www.vector.com/) XL-driver library interface implementation.
///
public class VectorCanCommunicator : ICanCommunicator, IDisposable
{
///
/// Flag indicating that thread shall run.
///
private bool doRun = false;
///
/// Tx channel for CAN device.
///
private xlSingleChannelCAN_Port CANDemo_TxChannel;
///
/// Rx channel for CAN device.
///
private xlSingleChannelCAN_Port CANDemo_RxChannel;
///
/// Event logging appender.
///
private ILogging iAppender;
///
/// List of data receivers.
///
private IList iDataReceiverList = new List();
///
/// Thread for receiving of data.
///
private Thread rxDataThread;
///
/// Lock object to avoid race conditions.
///
private object lockObject = new object();
///
/// Timestamp for last send.
///
private long t0 = 0;
///
/// Gets or sets time in milliseconds between sent frames.
///
public long frameSpacing { get; set; }
///
/// Gets The DLL version.
///
public string dllVersion { get; private set; }
///
/// Initializes a new instance of the class.
///
/// Name of application.
/// Event logging appender.
/// Time in milliseconds between sent frames.
public VectorCanCommunicator(string name, ILogging iAppender, long frameSpacing)
{
this.iAppender = iAppender;
this.dllVersion = string.Empty;
this.frameSpacing = frameSpacing;
this.iAppender.appendTextLn(">>> VectorCanCommunicator initializing, name='" + name + "'");
this.CANDemo_TxChannel = new xlSingleChannelCAN_Port(name, 0);
this.CANDemo_RxChannel = new xlSingleChannelCAN_Port(name, 1);
if (!this.CANDemo_TxChannel.xlCheckPort())
{
throw new Exception("CheckPort failed. (Application not configured? You may need to restart after config.)");
}
else
{
XLDriver xd = new XLDriver();
XLClass.xl_driver_config conf = new XLClass.xl_driver_config();
xd.XL_GetDriverConfig(ref conf);
this.dllVersion = xd.VersionToString(conf.dllVersion);
}
this.iAppender.appendTextLn("VectorCanCommunicator initialized, dllVersion=" + this.dllVersion);
}
///
/// Clean up.
///
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Add data receiver instance to current instance.
///
/// Data receiver instance.
public void addDataReceiver(IDataReceiver iDataReceiver)
{
this.iDataReceiverList.Add(iDataReceiver);
}
///
/// Remove data receiver connection from instance.
///
/// Data receiver instance.
public void removeDataReceiver(IDataReceiver iDataReceiver)
{
this.iDataReceiverList.Remove(iDataReceiver);
}
///
/// Start data receiver thread.
///
public void startCan()
{
if (this.CANDemo_RxChannel.xlCheckPort())
{
this.CANDemo_TxChannel.xlActivate();
this.CANDemo_RxChannel.xlActivate();
this.doRun = true;
this.rxDataThread = new Thread(this.rxDataThreadImpl);
this.rxDataThread.Start();
}
}
///
/// Stop data receiver thread.
///
public void stopCan()
{
this.doRun = false;
this.rxDataThread.Abort();
this.rxDataThread.Interrupt();
}
///
/// Send data.
///
/// Target address.
/// Data to send.
public void send(uint id, byte[] bytes)
{
if (bytes != null)
{
lock (this.lockObject)
{
long t1 = DateTime.Now.Ticks;
long tDiff = (this.frameSpacing - ((t1 - this.t0) / TimeSpan.TicksPerMillisecond));
// Sanity check.
if (tDiff > 0 && tDiff < 1000)
{
Thread.Sleep((int)tDiff);
}
this.t0 = t1;
ulong data = 0;
if (bytes.Length <= 8)
{
ushort len = (ushort)bytes.Length;
for (int i = 0; i < len; i++)
{
data |= ((ulong)bytes[i] << (i * 8));
}
// If >11 bit address then set the extended address flag.
if (id >= 0x800)
{
id |= 0x80000000;
}
this.CANDemo_TxChannel.xlTransmit(id, len, data);
}
else
{
this.iAppender.appendTextLn("Trying to send more data (" + bytes.Length + " bytes) than can fit in a CAN frame.");
System.Diagnostics.StackTrace t = new System.Diagnostics.StackTrace();
this.iAppender.appendTextLn(t.ToString());
}
}
}
else
{
this.iAppender.appendTextLn(" data, nothing sent.");
}
}
///
/// Clean up.
///
/// 'true' if disposing.
protected virtual void Dispose(bool disposing)
{
this.doRun = false;
this.stopCan();
}
///
/// Implementation of thread to receive data.
///
private void rxDataThreadImpl()
{
XLClass.xl_event receivedEvent = new XLClass.xl_event();
XLClass.XLstatus xlStatus = XLClass.XLstatus.XL_SUCCESS;
while (this.doRun)
{
WaitResult waitResult = (WaitResult)NativeMethods.WaitForSingleObject((IntPtr)this.CANDemo_RxChannel.eventHandle, 1000);
if (waitResult != WaitResult.WAIT_TIMEOUT)
{
xlStatus = XLClass.XLstatus.XL_SUCCESS;
while (xlStatus != XLClass.XLstatus.XL_ERR_QUEUE_IS_EMPTY)
{
xlStatus = this.CANDemo_RxChannel.xlReceive(ref receivedEvent);
if (xlStatus == XLClass.XLstatus.XL_SUCCESS)
{
byte[] ba = new byte[receivedEvent.tagData.can_Msg.dlc];
#if TRACE
this.iAppender.appendTextLn("datalen=" + receivedEvent.tagData.can_Msg.dlc.ToString());
#endif
Array.Copy(receivedEvent.tagData.can_Msg.data, ba, receivedEvent.tagData.can_Msg.dlc);
foreach (IDataReceiver iDataReceiver in this.iDataReceiverList)
{
ushort flags = receivedEvent.tagData.can_Msg.flags;
ulong res1 = receivedEvent.tagData.can_Msg.res1;
ulong res2 = receivedEvent.tagData.can_Msg.res2;
// Mask off the extended addressing indication bit.
iDataReceiver.receiveData((receivedEvent.tagData.can_Msg.id & 0x7FFFFFFF), ba, flags, res1, res2);
}
#if TRACE
}
else
{
this.iAppender.appendTextLn("xlStatus=" + xlStatus.ToString());
#endif
}
}
}
}
this.iAppender.appendTextLn("Thread end");
}
}
}