aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/FSE/Modules/Tango.FSE.MachineConfiguration/Messages
ModeNameSize
-rw-r--r--EditingCompositionLoadedMessage.cs427logstatsplain
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
{
    /// <summary>
    /// Scans for available machines on the local area network and physically connected via USB.
    /// </summary>
    /// <seealso cref="Tango.Core.ExtendedObject" />
    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<IExternalBridgeClient> MachineDiscovered;

        public event EventHandler<IExternalBridgeClient> MachineLost;

        private SynchronizedObservableCollection<IExternalBridgeClient> _availableMachines;
        /// <summary>
        /// Gets the available machines.
        /// </summary>
        public SynchronizedObservableCollection<IExternalBridgeClient> AvailableMachines
        {
            get { return _availableMachines; }
            private set { _availableMachines = value; RaisePropertyChangedAuto(); }
        }

        /// <summary>
        /// Gets or sets the SignalR configuration.
        /// </summary>
        public ExternalBridgeSignalRConfiguration SignalRConfiguration { get; set; }

        /// <summary>
        /// Gets or sets the known machines.
        /// </summary>
        public List<Machine> KnownMachines { get; set; }

        private bool _isStarted;
        /// <summary>
        /// Gets or sets a value indicating whether this instance is started.
        /// </summary>
        public bool IsStarted
        {
            get { return _isStarted; }
            private set { _isStarted = value; RaisePropertyChangedAuto(); }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ExternalBridgeScanner"/> class.
        /// </summary>
        public ExternalBridgeScanner()
        {
            _settings = SettingsManager.Default.GetOrCreate<IntegrationSettings>();
            AvailableMachines = new SynchronizedObservableCollection<IExternalBridgeClient>();
            KnownMachines = new List<Machine>();
            SignalRConfiguration = new ExternalBridgeSignalRConfiguration();
        }

        public ExternalBridgeScanner(List<Machine> knownMachines) : this()
        {
            KnownMachines = knownMachines;
        }

        /// <summary>
        /// Start scanning. (Results will be available through <see cref="AvailableMachines"/>).
        /// </summary>
        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<ExternalBridgeTcpClient>().ToList())
                {
                    AvailableMachines.Remove(machine);
                }

                foreach (var machine in AvailableMachines.OfType<ExternalBridgeSignalRClient>().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);
                    }
                }
            }
        }

        /// <summary>
        /// Stops this instance.
        /// </summary>
        public void Stop()
        {
            if (IsStarted)
            {
                IsStarted = false;
                LogManager.Log("External bridge scanner stopped.");
            }
        }

        /// <summary>
        /// USB discovery thread method.
        /// </summary>
        private void UsbDiscoveryThreadMethod()
        {
            while (IsStarted)
            {
                foreach (var device in Transport.Components.ComPortEnumerator.EnumerateComPorts())
                {
                    if (device.Description.Contains(_settings.EmbeddedDeviceName) || !_settings.FilterExternalBridgeUsbMachines)
                    {
                        if (!AvailableMachines.OfType<ExternalBridgeUsbClient>().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);
            }
        }

        /// <summary>
        /// TCP discovery thread method.
        /// </summary>
        //[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<ExternalBridgeTcpClient>().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<ExternalBridgeTcpClient>().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<List<MachineInfo>>("GetAvailableMachines").Result;

                        foreach (var machine in AvailableMachines.OfType<ExternalBridgeSignalRClient>().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<ExternalBridgeSignalRClient>().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);
            }
        }

        /// <summary>
        /// Handles the available machines component state changed event.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        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);
            }
        }
    }
}