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