using System; using System.Collections.Generic; using System.IO.Ports; using System.Threading; using System.Threading.Tasks; using DTS.Common.DASResource; using DTS.Common.Interface.Connection; using DTS.Common.Utilities.Logging; namespace DTS.Common.WINUSBConnection { public class UsbRecAsyncResult : IAsyncResult { public object AsyncState { get; set; } public WaitHandle AsyncWaitHandle { get; set; } public bool CompletedSynchronously { get; set; } public bool IsCompleted { get; set; } public byte[] buffer { get; set; } public int Offset { get; set; } public int Size { get; set; } } public class CDCUSBConnection : IConnection { /// /// returns true if the das has been soft disconnected or not /// public bool IsSoftDisconnected { get; private set; } = false; /// /// connect the das /// :note does nothing for CDCUSB, we don't have a soft disconnect option /// public void SoftConnect() { } /// /// disconnect the das /// :note does nothing for CDCUSB, we don't have a soft disconnect option /// public void SoftDisconnect() { } void IConnection.KeepAliveErrorReceived() { } public string GetConnectionData() { return ""; } public const int DTS_VENDOR_ID = 0x1CB9; public const string DTS_VENDOR_ID_STR = "VID_1CB9"; private const string DTS_TSR2_CDCUSB_PRODUCT_ID_STR = "PID_001A"; private static List _regKeys; private static readonly object KEY_LOCK = new object(); public static IList RegKeys { get { lock (KEY_LOCK) { if (null != _regKeys) return _regKeys; _regKeys = new List(); ReadTSR2CDCUSBRegKeys(); } return _regKeys; } } /// /// Searches the system registry for a key that corresponds to the Peak CAN USB /// vendor and product ID. /// internal static void ReadTSR2CDCUSBRegKeys() { var ourKey = ReadOurNameRegistryKey(); if (null == ourKey) { return; } foreach (var subKey in ourKey.GetSubKeyNames()) { RegKeys.Add(subKey); } } internal static Microsoft.Win32.RegistryKey ReadOurNameRegistryKey() { var key = Microsoft.Win32.Registry.LocalMachine; const string ourName = @"SYSTEM\CurrentControlSet\Enum\USB\" + DTS_VENDOR_ID_STR + "&" + DTS_TSR2_CDCUSB_PRODUCT_ID_STR; key = key.OpenSubKey(ourName); return key; } /// /// current upload rate in b/s /// /// public double GetCurrentUploadRate() { return 0D; } /// /// current download rate in b/s /// /// public double GetCurrentDownloadRate() { return 0D; } public event EventHandler OnDisconnected; private int _baudRate = 9600; private Parity _parity = Parity.None; private StopBits _stopBits = StopBits.One; private const int DATA_BITS = 8; public string PortName { get; set; } private readonly SerialPort _comPort = new SerialPort(); private string _devicePathname; protected bool _Connected; protected volatile bool Disposed; protected volatile bool Disposing; public bool Connected => _Connected; public string ConnectString => _devicePathname; public System.Net.Sockets.SocketFlags Flags { get; set; } private class AUSBRecFix { public readonly AsyncCallback Callback; public readonly UsbRecAsyncResult USBResult; public AUSBRecFix(AsyncCallback callback, UsbRecAsyncResult usbResult) { Callback = callback; USBResult = usbResult; } } public CDCUSBConnection() { Disposed = false; Disposing = false; _Connected = false; } ~CDCUSBConnection() { Dispose(false); } public void Dispose() { Dispose(true); // Use SupressFinalize in case a subclass // of this type implements a finalizer. GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { // make sure we're not already disposed if (Disposed) return; if (disposing) { Disposing = true; } if (_comPort.IsOpen) { _comPort.Close(); _Connected = false; } Disposed = true; Disposing = false; } public void Create(string connectString) { //Create(connectString); - this is recursive! } public void Create(string connectString, string hostIPAddress) { foreach (var regKey in RegKeys) { if (!connectString.Contains(regKey.ToLower())) continue; var ourKey = ReadOurNameRegistryKey(); ourKey = ourKey.OpenSubKey(regKey); ourKey = ourKey?.OpenSubKey("Device Parameters"); var value = (string)ourKey?.GetValue("PortName"); PortName = value; break; } _devicePathname = connectString; } #region Disconnect public IAsyncResult BeginDisconnect(bool reuseSocket, AsyncCallback cb, Object state) { var rar = new UsbRecAsyncResult { AsyncState = state, AsyncWaitHandle = new ManualResetEvent(false), CompletedSynchronously = false, IsCompleted = false, buffer = null, Offset = 0, Size = 0 }; if (!ThreadPool.QueueUserWorkItem(NetCallbackFix, new AUSBRecFix(cb, rar))) { throw new Exception(DASResource.Strings.CDCUSBConnection_BeginDisconnect_Err1); } return rar; } public void EndDisconnect(IAsyncResult ar) { var rar = ar as UsbRecAsyncResult; if (_comPort.IsOpen) { _comPort.Close(); _Connected = false; _comPort.Dispose(); } ((ManualResetEvent)rar?.AsyncWaitHandle)?.Set(); } #endregion #region Accept public IAsyncResult BeginAccept(AsyncCallback callback, object state) { throw new NotSupportedException(); } public IConnection EndAccept(IAsyncResult asyncResult) { throw new NotSupportedException(); } #endregion #region Listen public void Listen(int backlog) { throw new NotSupportedException(); } #endregion #region Bind public void Bind(int port) { throw new NotSupportedException(); } #endregion #region Connect public IAsyncResult BeginConnect(AsyncCallback cb, object state) { var rar = new UsbRecAsyncResult { AsyncState = state, AsyncWaitHandle = new ManualResetEvent(false), CompletedSynchronously = false, IsCompleted = false, buffer = null, Offset = 0, Size = 0 }; if (!ThreadPool.QueueUserWorkItem(NetCallbackFix, new AUSBRecFix(cb, rar))) { // "USBConnection.BeginConnect: Unable to enqueue function" throw new Exception(DASResource.Strings.CDCUSBConnection_BeginConnect_Err1); } return rar; } private void NetCallbackFix(object obj) { try { if (Disposed) return; var ausbrf = obj as AUSBRecFix; ausbrf?.Callback(ausbrf.USBResult); } catch (Exception ex) { APILogger.Log(@"PcanUsbConnection.NetCallbackFix: Exception " + ex.Message + @" " + ex.StackTrace); } } public void EndConnect(IAsyncResult ar) { var rar = ar as UsbRecAsyncResult; if (_comPort.IsOpen) { _comPort.Close(); _Connected = false; } _comPort.BaudRate = _baudRate; _comPort.DataBits = DATA_BITS; _comPort.StopBits = _stopBits; _comPort.Parity = _parity; _comPort.PortName = PortName; try { _comPort.Open(); } catch (Exception) { _Connected = false; } _Connected = true; ((ManualResetEvent)rar?.AsyncWaitHandle)?.Set(); } #endregion #region Send public IAsyncResult BeginSend(byte[] buffer, int offset, int size, AsyncCallback cb, object state) { var rar = new UsbRecAsyncResult { AsyncState = state, AsyncWaitHandle = new ManualResetEvent(false), CompletedSynchronously = false, IsCompleted = false, buffer = buffer, Offset = offset, Size = size }; if (!ThreadPool.QueueUserWorkItem(NetCallbackFix, new AUSBRecFix(cb, rar))) { // "WINUSBConnection.BeginSend: Unable to enqueue function" throw new Exception(DASResource.Strings.CDCUSBConnection_BeginSend_Err1); } return rar; } public int EndSend(IAsyncResult ar) { var rar = ar as UsbRecAsyncResult; if (null == rar) { return 0; } _comPort.Write(rar.buffer, 0, rar.Size); ((ManualResetEvent)rar.AsyncWaitHandle).Set(); return rar.Size; } public Task SendAsync(byte[] sendBuffer, int bufferStartOffset, int bufferSizeToSend) { return Task.Factory.FromAsync( (callback, state) => BeginSend(sendBuffer, bufferStartOffset, bufferSizeToSend, callback, state), EndSend, state: null ); } #endregion #region Receive public IAsyncResult BeginReceive(byte[] buffer, int offset, int size, AsyncCallback cb, object state) { var rar = new UsbRecAsyncResult { AsyncState = state, AsyncWaitHandle = new ManualResetEvent(false), CompletedSynchronously = false, IsCompleted = false, buffer = buffer, Offset = offset, Size = size }; if (!ThreadPool.QueueUserWorkItem(NetCallbackFix, new AUSBRecFix(cb, rar))) { // "WINUSBConnection.BeginReceive: Unable to enqueue function" throw new Exception(DASResource.Strings.CDCUSBConnection_BeginReceive_Err1); } return rar; } //private DateTime _lastHeartbeat; public int EndReceive(IAsyncResult ar) { var rar = ar as UsbRecAsyncResult; int bytesRead; do { bytesRead = _comPort.Read(rar.buffer, 0, rar.Size); if (0 == bytesRead) { Thread.Sleep(1); } } while (0 == bytesRead); ((ManualResetEvent)rar.AsyncWaitHandle).Set(); return bytesRead; } #endregion } }