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.BL.Enumerations;
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;
using Tango.Transport.Adapters;
using Tango.Transport.Discovery;
using Tango.Transport.Helpers;
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 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...");
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;
try
{
_usbDiscoveryThread.Abort();
_tcpDiscoveryThread.Abort();
_signalRDiscoveryThread.Abort();
AvailableMachines.Clear();
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
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
{
ExternalBridgeUdpDiscoveryPacket discoveryPacket = null;
String address = null;
if (_settings.ExternalBridgeDiscoveryMethod == DiscoveryMethod.Multicast)
{
using (UdpClient udpClient = new UdpClient())
{
udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, _settings.ExternalBridgeServiceDiscoveryPort));
udpClient.JoinMulticastGroup(IPAddress.Parse(_settings.ExternalBridgeMulticastGroup), IPAddress.Parse(IPAddressHelper.GetLocalIpAddress()));
var endPoint = new IPEndPoint(IPAddress.Any, 0);
var discoveryData = udpClient.Receive(ref endPoint);
discoveryPacket = ExternalBridgeUdpDiscoveryPacket.Parser.ParseFrom(discoveryData);
address = endPoint.Address.ToString();
}
}
else
{
using (UdpClient udpClient = new UdpClient())
{
udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, _settings.ExternalBridgeServiceDiscoveryPort));
var endPoint = new IPEndPoint(IPAddress.Any, _settings.ExternalBridgeServiceDiscoveryPort);
udpClient.EnableBroadcast = true;
var discoveryData = udpClient.Receive(ref endPoint);
discoveryPacket = ExternalBridgeUdpDiscoveryPacket.Parser.ParseFrom(discoveryData);
address = endPoint.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 == discoveryPacket.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 == discoveryPacket.SerialNumber && x.IPAddress == address))
{
ExternalBridgeTcpClient newMachine = null;
var knownMachine = KnownMachines.FirstOrDefault(x => x.SerialNumber == discoveryPacket.SerialNumber);
if (knownMachine == null && KnownMachines.Count == 0)
{
newMachine = new ExternalBridgeTcpClient(discoveryPacket.SerialNumber, address);
}
else if (knownMachine != null)
{
newMachine = new ExternalBridgeTcpClient(knownMachine, address);
}
if (newMachine != null)
{
newMachine.MachineType = (MachineTypes)discoveryPacket.MachineType;
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);
}
Thread.Sleep(100);
}
}
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)
{
newMachine.MachineType = (MachineTypes)machine.MachineType;
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);
}
}
public void TryGetMachineByIP(String ip)
{
IPAddress _IP;
if (IPAddress.TryParse(ip, out _IP))
{
try
{
TcpClient client = new TcpClient();
client.Connect(ip, _settings.ExternalBridgeServiceDiscoveryPort);
Thread.Sleep(100);
byte[] data = new byte[client.Available];
client.GetStream().Read(data, 0, data.Length);
client.Dispose();
TcpValidationInfo info = GenericMessageSerializer.Deserialize(data, GenericMessageProtocol.Bson);
ExternalBridgeTcpClient newMachine = null;
var knownMachine = KnownMachines.FirstOrDefault(x => x.SerialNumber == info.SerialNumber);
if (knownMachine == null && KnownMachines.Count == 0)
{
newMachine = new ExternalBridgeTcpClient(info.SerialNumber, ip);
}
else if (knownMachine != null)
{
newMachine = new ExternalBridgeTcpClient(knownMachine, ip);
}
if (newMachine != null)
{
newMachine.MachineType = (MachineTypes)info.MachineType;
LogManager.Log("Found a new machine via IP Address" + 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, "Error occurred when trying to find a machine by IP.");
}
}
}
}
}