aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Tango.Transport/Discovery/UsbCommunicationScanner.cs
blob: 7606de4fc7a373859918bc9e83dfcb9a9879fb8d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
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);
    }

    /// <summary>
    /// Represents a USB port scanner which will scan all known ports on PC with the specified request and response types. 
    /// </summary>
    /// <typeparam name="TRequest">The type of the request.</typeparam>
    /// <typeparam name="TResponse">The type of the response.</typeparam>
    /// <seealso cref="Tango.Transport.Discovery.ICommunicationScanner{Tango.Transport.Adapters.UsbTransportAdapter, TRequest, TResponse}" />
    public class UsbCommunicationScanner<TRequest, TResponse> : ICommunicationScanner<UsbTransportAdapter, TRequest, TResponse> where TRequest : IMessage where TResponse : IMessage
    {
        public event Action<String> ScanningPort;

        /// <summary>
        /// Gets the baud rate.
        /// </summary>
        public UsbSerialBaudRates BaudRate { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="UsbCommunicationScanner{TRequest, TResponse}"/> class.
        /// </summary>
        /// <param name="baudRate">The baud rate.</param>
        public UsbCommunicationScanner(UsbSerialBaudRates baudRate)
        {
            BaudRate = baudRate;
        }

        /// <summary>
        /// Scans the environment with the specified request while expecting the type of response.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="timeout">The timeout.</param>
        /// <returns></returns>
        public Task<CommunicationScannerResult<UsbTransportAdapter, TResponse>> Scan(TRequest request, TimeSpan timeout)
        {
            return Scan(request, null, timeout);
        }

        /// <summary>
        /// Scans the environment with the specified request while expecting the type of response.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="hint">The expected device name.</param>
        /// <param name="timeout">The timeout.</param>
        /// <returns></returns>
        public Task<CommunicationScannerResult<UsbTransportAdapter, TResponse>> 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<CommunicationScannerResult<UsbTransportAdapter, TResponse>> source = new TaskCompletionSource<CommunicationScannerResult<UsbTransportAdapter, TResponse>>();

            Task.Factory.StartNew(async () =>
            {
                CommunicationScannerResult<UsbTransportAdapter, TResponse> result = new CommunicationScannerResult<UsbTransportAdapter, TResponse>();

                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<UsbTransportAdapter, TResponse>()
                                {
                                    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;
        }
    }
}