using Google.Protobuf; using Microsoft.Win32.SafeHandles; using System; using System.Collections.Generic; using System.IO.Ports; using System.Linq; using System.Management; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using Tango.Logging; using Tango.Transport.Adapters; using Tango.Transport.Components; using Tango.Transport.Transporters; namespace Tango.Transport.Discovery { internal static class UsbComPortWin32 { internal static int dwFlagsAndAttributes = 0x40000000; [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] internal static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, IntPtr securityAttrs, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile); } /// /// Represents a USB port scanner which will scan all known ports on PC with the specified request and response types. /// /// The type of the request. /// The type of the response. /// public class UsbCommunicationScanner : ICommunicationScanner where TRequest : IMessage where TResponse : IMessage { public event Action ScanningPort; /// /// Gets the baud rate. /// public UsbSerialBaudRates BaudRate { get; private set; } /// /// Initializes a new instance of the class. /// /// The baud rate. public UsbCommunicationScanner(UsbSerialBaudRates baudRate) { BaudRate = baudRate; } /// /// Scans the environment with the specified request while expecting the type of response. /// /// The request. /// The timeout. /// public Task> Scan(TRequest request, TimeSpan timeout) { return Scan(request, null, timeout); } /// /// Scans the environment with the specified request while expecting the type of response. /// /// The request. /// The expected device name. /// The timeout. /// public Task> Scan(TRequest request, String hint, TimeSpan timeout) { var logManager = LogManager.Default; logManager.Log("Starting com ports scanning using baud rate " + BaudRate.ToInt32() + "..."); DateTime startTime = DateTime.Now; bool found = false; TaskCompletionSource> source = new TaskCompletionSource>(); Task.Factory.StartNew(async () => { CommunicationScannerResult result = new CommunicationScannerResult(); logManager.Log("Enumerating com ports..."); var comPorts = ComPortEnumerator.EnumerateComPorts(); if (!String.IsNullOrWhiteSpace(hint)) { logManager.Log($"Device hint is '{hint}'."); var expected_port = comPorts.FirstOrDefault(x => x.Description.Contains(hint)); if (expected_port != null) { logManager.Log($"Device found by hint on port {expected_port.Port}. Sorting ports..."); comPorts.Remove(expected_port); comPorts.Insert(0, expected_port); } else { logManager.Log("Device could not be found by hint."); } } while (!found && DateTime.Now < startTime.Add(timeout)) { foreach (var port in comPorts) { if (DateTime.Now > startTime.Add(timeout)) { break; } logManager.Log("Scanning " + port.ToString() + "..."); ScanningPort?.Invoke(port.Port); //SafeFileHandle hFile = UsbComPortWin32.CreateFile(@"\\.\" + port.Port, -1073741824, 0, IntPtr.Zero, 3, UsbComPortWin32.dwFlagsAndAttributes, IntPtr.Zero); //if (hFile.IsInvalid) //{ // logManager.Log(String.Format("Port {0} is already open or invalid.", port.Port)); // hFile.Close(); // continue; //} //hFile.Close(); SerialPort serialPort = new SerialPort(port.Port); try { serialPort.Open(); serialPort.Close(); } catch (Exception ex) { logManager.Log(ex, "Could not open port " + port.Port); continue; } BasicTransporter transporter = new BasicTransporter(new UsbTransportAdapter(port.Port, BaudRate)); try { logManager.Log("Connecting transporter..."); await transporter.Connect(); logManager.Log("Sending scanning request..."); var response = await transporter.SendRequest(request, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(2) }); if (response is TResponse) { logManager.Log("Response received from " + port.ToString()); ITransportAdapter adapter = null; try { adapter = transporter.Adapter; logManager.Log("Disconnecting transporter... (keeping adapter)"); transporter.Adapter = null; await transporter.Disconnect(); } catch { } found = true; logManager.Log("Setting scan task result."); source.SetResult(new CommunicationScannerResult() { Adapter = adapter as UsbTransportAdapter, Response = (TResponse)response, }); break; } logManager.Log("Disconnecting transporter..."); await transporter.Disconnect(); } catch (Exception ex) { try { logManager.Log(ex, "Disconnecting transporter..."); await transporter.Disconnect(); } catch { } } } } if (!found) { source.SetException(new TimeoutException(logManager.Log("The com port scanning operation had timed out after " + timeout.TotalSeconds + " seconds."))); } }); return source.Task; } } }