using Microsoft.AspNet.SignalR.Client; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.IO.Ports; using System.Linq; using System.Management; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; using Tango.BL.Entities; using Tango.Core; using Tango.Core.Helpers; using Tango.Integration.ExternalBridge.Web; using Tango.Logging; using Tango.PMR; using Tango.PMR.Common; using Tango.PMR.Integration; using Tango.Settings; using Tango.Transport.Adapters; namespace Tango.Integration.ExternalBridge { /// /// Scans for available machines on the local area network and physically connected via USB. /// /// public class ExternalBridgeScanner : ExtendedObject { private Thread _tcpDiscoveryThread; private Thread _usbDiscoveryThread; private Thread _signalRDiscoveryThread; private UdpClient _server; private IntegrationSettings _settings; private HubConnection _connection; private IHubProxy _proxy; public event EventHandler MachineDiscovered; public event EventHandler MachineLost; private SynchronizedObservableCollection _availableMachines; /// /// Gets the available machines. /// public SynchronizedObservableCollection AvailableMachines { get { return _availableMachines; } private set { _availableMachines = value; RaisePropertyChangedAuto(); } } /// /// Gets or sets the SignalR configuration. /// public ExternalBridgeSignalRConfiguration SignalRConfiguration { get; set; } /// /// Gets or sets the known machines. /// public List KnownMachines { get; set; } private bool _isStarted; /// /// Gets or sets a value indicating whether this instance is started. /// public bool IsStarted { get { return _isStarted; } private set { _isStarted = value; RaisePropertyChangedAuto(); } } /// /// Initializes a new instance of the class. /// public ExternalBridgeScanner() { _settings = SettingsManager.Default.GetOrCreate(); AvailableMachines = new SynchronizedObservableCollection(); KnownMachines = new List(); SignalRConfiguration = new ExternalBridgeSignalRConfiguration(); } public ExternalBridgeScanner(List knownMachines) : this() { KnownMachines = knownMachines; } /// /// Start scanning. (Results will be available through ). /// public void Start() { if (!IsStarted) { LogManager.Log("External bridge scanner started..."); if (_server == null) { _server = new UdpClient(); _server.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); _server.Client.Bind(new IPEndPoint(IPAddress.Any, _settings.ExternalBridgeServiceDiscoveryPort)); } IsStarted = true; foreach (var machine in AvailableMachines.OfType().ToList()) { AvailableMachines.Remove(machine); } foreach (var machine in AvailableMachines.OfType().ToList()) { AvailableMachines.Remove(machine); } _tcpDiscoveryThread = new Thread(TcpDiscoveryThreadMethod); _tcpDiscoveryThread.IsBackground = true; _tcpDiscoveryThread.Start(); _usbDiscoveryThread = new Thread(UsbDiscoveryThreadMethod); _usbDiscoveryThread.IsBackground = true; _usbDiscoveryThread.Start(); if (SignalRConfiguration.Enabled) { try { _connection = new HubConnection(SignalRConfiguration.Address); _proxy = _connection.CreateHubProxy(SignalRConfiguration.Hub); bool signalRStarted = false; _connection.StateChanged += (x) => { if (x.NewState == ConnectionState.Connected) { if (!signalRStarted) { signalRStarted = true; _signalRDiscoveryThread = new Thread(SignalRDiscoveryThreadMethod); _signalRDiscoveryThread.IsBackground = true; _signalRDiscoveryThread.Start(); } } }; _connection.Start(); } catch (Exception ex) { Debug.WriteLine(ex); } } } } /// /// Stops this instance. /// public void Stop() { if (IsStarted) { IsStarted = false; LogManager.Log("External bridge scanner stopped."); } } /// /// USB discovery thread method. /// private void UsbDiscoveryThreadMethod() { while (IsStarted) { foreach (var device in Transport.Components.ComPortEnumerator.EnumerateComPorts()) { if (device.Description.Contains(_settings.EmbeddedDeviceName) || !_settings.FilterExternalBridgeUsbMachines) { if (!AvailableMachines.OfType().ToList().Exists(x => x.ComPort == device.Port)) { LogManager.Log("Found a new machine via USB " + device.Description); ThreadsHelper.InvokeUINow(() => { var machine = new ExternalBridgeUsbClient(device.Port, device.Description, _settings.EmbeddedSerialBaudRate); AvailableMachines.Add(machine); MachineDiscovered?.Invoke(this, machine); }); } } } Thread.Sleep(2000); } } /// /// TCP discovery thread method. /// //[DebuggerStepThrough] private void TcpDiscoveryThreadMethod() { while (IsStarted) { try { var ClientEp = new IPEndPoint(IPAddress.Any, _settings.ExternalBridgeServiceDiscoveryPort); _server.EnableBroadcast = true; var ClientRequestData = _server.Receive(ref ClientEp); ExternalBridgeUdpDiscoveryPacket packet = ExternalBridgeUdpDiscoveryPacket.Parser.ParseFrom(ClientRequestData); String address = ClientEp.Address.ToString(); //validate service existence using TCP connection. try { TcpClient client = new TcpClient(); client.Connect(address, _settings.ExternalBridgeServiceDiscoveryPort); client.Dispose(); } catch { var disconnected_machine = AvailableMachines.OfType().ToList().FirstOrDefault(x => x.SerialNumber == packet.SerialNumber && x.IPAddress == address); if (disconnected_machine != null) { LogManager.Log("Disconnected machine detected via TCP: " + disconnected_machine.SerialNumber); AvailableMachines.Remove(disconnected_machine); MachineLost?.Invoke(this, disconnected_machine); } continue; } if (!AvailableMachines.OfType().ToList().Exists(x => x.SerialNumber == packet.SerialNumber && x.IPAddress == address)) { ExternalBridgeTcpClient newMachine = null; var knownMachine = KnownMachines.FirstOrDefault(x => x.SerialNumber == packet.SerialNumber); if (knownMachine == null && KnownMachines.Count == 0) { newMachine = new ExternalBridgeTcpClient(packet.SerialNumber, address); } else if (knownMachine != null) { newMachine = new ExternalBridgeTcpClient(knownMachine, address); } if (newMachine != null) { LogManager.Log("Found a new machine via TCP " + newMachine.SerialNumber); ThreadsHelper.InvokeUINow(() => { if (AvailableMachines.Count > 0) { AvailableMachines.Insert(1, newMachine); } else { AvailableMachines.Add(newMachine); } MachineDiscovered?.Invoke(this, newMachine); }); } } } catch (Exception ex) { LogManager.Log(ex); } } } private void SignalRDiscoveryThreadMethod() { while (IsStarted && SignalRConfiguration.Enabled) { if (_connection.State == ConnectionState.Connected) { try { var machines = _proxy.Invoke>("GetAvailableMachines").Result; foreach (var machine in AvailableMachines.OfType().ToList().Where(x => !machines.Exists(y => y.SerialNumber == x.SerialNumber))) { AvailableMachines.Remove(machine); MachineLost?.Invoke(this, machine); } foreach (var machine in machines.Where(x => !AvailableMachines.OfType().ToList().Exists(y => y.SerialNumber == x.SerialNumber))) { ExternalBridgeSignalRClient newMachine = null; var knownMachine = KnownMachines.FirstOrDefault(x => x.SerialNumber == machine.SerialNumber); if (knownMachine == null && KnownMachines.Count == 0) { newMachine = new ExternalBridgeSignalRClient(SignalRConfiguration.Address, SignalRConfiguration.Hub, machine); } else if (knownMachine != null) { newMachine = new ExternalBridgeSignalRClient(SignalRConfiguration.Address, SignalRConfiguration.Hub, knownMachine, machine); } if (newMachine != null) { LogManager.Log("Found a new machine via SignalR " + newMachine.SerialNumber); ThreadsHelper.InvokeUINow(() => { if (AvailableMachines.Count > 0) { AvailableMachines.Insert(1, newMachine); } else { AvailableMachines.Add(newMachine); } MachineDiscovered?.Invoke(this, newMachine); }); } } } catch (Exception ex) { Debug.WriteLine(ex); } } Thread.Sleep(5000); } } private DateTime ParseDateTime(String dateTime) { try { return DateTime.ParseExact(dateTime, "dd/MM/yyyy HH:mm:ss", CultureInfo.InvariantCulture); } catch (Exception) { return DateTime.Parse(dateTime); } } /// /// Handles the available machines component state changed event. /// /// The sender. /// The e. private void Client_StateChanged(object sender, Transport.TransportComponentState e) { if (e == Transport.TransportComponentState.Failed || e == Transport.TransportComponentState.Disposed) { LogManager.Log("External bridge client failed or disposed. Removing from available machines..."); AvailableMachines.Remove(sender as IExternalBridgeClient); MachineLost?.Invoke(this, sender as IExternalBridgeClient); } } } }