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