aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Updater/MainWindow.xaml
blob: 8733d4e0c181bb0c5553e561c66a9565428d975a (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
<Window x:Class="Tango.MachineStudio.Updater.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Tango.MachineStudio.Updater"
        mc:Ignorable="d"
        Title="Machine Studio Update" Height="400" Width="700" ShowInTaskbar="False" WindowStartupLocation="CenterScreen" WindowStyle="None" ResizeMode="NoResize" Foreground="#202020">

    <Border BorderThickness="1" BorderBrush="#0288D1">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="60"/>
                <RowDefinition Height="156*"/>
            </Grid.RowDefinitions>

            <Grid Background="#0288D1">
                <Grid.Effect>
                    <DropShadowEffect BlurRadius="10" ShadowDepth="5" Direction="270" Opacity="0.5" />
                </Grid.Effect>

                <StackPanel Orientation="Horizontal" Margin="8" HorizontalAlignment="Center">
                    <Image Source="/Images/machine-trans.png" RenderOptions.BitmapScalingMode="Fant"></Image>
                    <TextBlock Foreground="White" VerticalAlignment="Center" Margin="10 0 0 0" FontSize="23">MACHINE STUDIO</TextBlock>
                </StackPanel>
            </Grid>

            <Grid Grid.Row="1">
                <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
                    <Image Source="/Images/update.png" Width="100" />
                    <TextBlock x:Name="txtStatus" Margin="0 20 0 0" FontSize="12" Text="Updating Machine Studio..." HorizontalAlignment="Center"></TextBlock>
                    <ProgressBar x:Name="prog" Margin="0 30 0 0" Width="500" Height="10" Maximum="100" Value="0" Foreground="#0288D1"></ProgressBar>
                </StackPanel>
            </Grid>
        </Grid>
    </Border>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Security.Authentication;
using System.Text;
using System.Threading.Tasks;
using Tango.BL;
using Tango.BL.Entities;
using Tango.Integration.Operation;
using Tango.Logging;
using Tango.PMR;
using Tango.PMR.Common;
using Tango.PMR.Connection;
using Tango.PMR.Integration;
using Tango.PMR.MachineStatus;
using Tango.Settings;
using Tango.Transport;
using Tango.Transport.Adapters;
using Tango.Transport.Transporters;

namespace Tango.Integration.ExternalBridge
{
    /// <summary>
    /// Represents a secure external bridge TCP client.
    /// </summary>
    /// <seealso cref="Tango.Transport.Transporters.BasicTransporter" />
    /// <seealso cref="Tango.Integration.ExternalBridge.IExternalBridgeSecureClient" />
    public class ExternalBridgeTcpClient : MachineOperator, IExternalBridgeSecureClient
    {
        private bool _logs_sent;

        public event EventHandler<LogItemBase> ApplicationLogAvailable;

        #region Properties

        private String _serialNumber;
        /// <summary>
        /// Gets the machine serial number.
        /// </summary>
        public String SerialNumber
        {
            get { return _serialNumber; }
            set
            {
                _serialNumber = value;
                RaisePropertyChangedAuto();
            }
        }

        private String _ipAddress;
        /// <summary>
        /// Gets or sets the machine IP address.
        /// </summary>
        public String IPAddress
        {
            get { return _ipAddress; }
            set { _ipAddress = value; RaisePropertyChangedAuto(); }
        }

        private bool _enableApplicationLogs;
        /// <summary>
        /// Gets or sets a value indicating whether to enable receiving application logs.
        /// </summary>
        public bool EnableApplicationLogs
        {
            get { return _enableApplicationLogs; }
            set
            {
                _enableApplicationLogs = value;
                RaisePropertyChangedAuto();
                OnEnableApplicationLogsChanged(value);
            }
        }

        public bool InjectApplicationLogsToDefaultLogManager { get; set; } = true;

        /// <summary>
        /// Gets a value indicating whether this client requires authentication.
        /// </summary>
        public bool RequiresAuthentication => true;

        /// <summary>
        /// Gets or sets the login request message when using <see cref="Connect"/>.
        /// </summary>
        public ExternalBridgeLoginRequest LoginRequest { get; set; }

        /// <summary>
        /// Gets or sets the configure protocol request message when using <see cref="Connect"/>.
        /// </summary>
        public ConfigureProtocolRequest ConfigureProtocolRequest { get; set; }

        /// <summary>
        /// Gets or sets a value indicating to apply the <see cref="ConfigureProtocolRequest"/> even if there is an error with the request.
        /// </summary>
        public bool ForceProtocolConfiguration { get; set; }

        private ApplicationInformation _applicationInformation;
        /// <summary>
        /// Gets or sets the remote application information (PPC).
        /// </summary>
        public ApplicationInformation ApplicationInformation
        {
            get { return _applicationInformation; }
            protected set { _applicationInformation = value; RaisePropertyChangedAuto(); }
        }

        #endregion

        /// <summary>
        /// Connects to a remote external bridge service using the specified <see cref="LoginRequest"/>.
        /// </summary>
        /// <param name="login">The login.</param>
        /// <returns></returns>
        /// <exception cref="AuthenticationException">The machine password is invalid.</exception>
        public override Task Connect()
        {
            if (LoginRequest == null)
            {
                throw new InvalidOperationException("No LoginRequest was not specified.");
            }
            return Connect(LoginRequest, ConfigureProtocolRequest);
        }

        /// <summary>
        /// Connects to a remote external bridge service using the specified login.
        /// </summary>
        /// <param name="login">The login request.</param>
        /// <param name="protocol">Optional protocol configuration.</param>
        /// <returns></returns>
        /// <exception cref="AuthenticationException"></exception>
        public virtual async Task Connect(ExternalBridgeLoginRequest login, ConfigureProtocolRequest protocol = null)
        {
            if (State != TransportComponentState.Connected)
            {
                try
                {
                    Adapter.EnableCompression = false;
                    GenericProtocol = GenericMessageProtocol.Json;

                    await Adapter.Connect();

                    State = TransportComponentState.Connected;
                    StartThreads();

                    LogManager.Log($"{ComponentName}: External Bridge TCP Client Connected...");

                    TimeSpan? timeout = null;

                    if (login.RequireSafetyLevelOperations)
                    {
                        timeout = TimeSpan.FromSeconds(35);
                    }

                    var response = await SendRequest<ExternalBridgeLoginRequest, ExternalBridgeLoginResponse>(login, new TransportRequestConfig() { ShouldLog = true, Timeout = timeout });

                    if (protocol != null)
                    {
                        try
                        {
                            var configureResponse = await SendRequest<ConfigureProtocolRequest, ConfigureProtocolResponse>(protocol, new TransportRequestConfig() { ShouldLog = true });
                            if (configureResponse.Message.Confirmed)
                            {
                                await Task.Delay(500);
                                Adapter.EnableCompression = protocol.EnableCompression;
                                GenericProtocol = protocol.GenericProtocol;
                            }
                        }
                        catch (Exception ex)
                        {
                            if (ForceProtocolConfiguration)
                            {
                                LogManager.Log(ex, $"{ComponentName}: Could not configure remote machine protocol. Could be an old PPC version. (forcing protocol configuration)");
                                Adapter.EnableCompression = protocol.EnableCompression;
                                GenericProtocol = protocol.GenericProtocol;
                            }
                            else
                            {
                                LogManager.Log(ex, $"{ComponentName}: Could not configure remote machine protocol. Could be an old PPC version.");
                            }
                        }
                    }

                    ApplicationInformation = response.Message.ApplicationInformation;

                    SessionLogger.CreateSession();

                    DeviceInformation = response.Message.DeviceInformation;
                    if (!response.Message.Authenticated)
                    {
                        await Adapter.Disconnect();
                        throw new AuthenticationException(response.Container.ErrorMessage);
                    }
                }
                catch (Exception ex)
                {
                    try
                    {
                        await Adapter.Disconnect();
                    }
                    catch { }

                    throw ex;
                }

                ApplyContinuousChannelsConfiguration();
            }
        }

        protected virtual void ApplyContinuousChannelsConfiguration()
        {
            OnEnableDiagnosticsChanged(EnableDiagnostics);
            OnEnableEmbeddedDebuggingChanged(EnableEmbeddedDebugging);
            OnEnableEventsNotification(EnableEventsNotification);
            OnEnableApplicationLogsChanged(EnableApplicationLogs);
            OnEnableMachineStatusUpdatesChanged(EnableMachineStatusUpdates);
            OnEnableInkFillingStatus(EnableInkFillingStatus);
            //TODO: Uncomment this only when Machine Studio enables automatic thread loading (ExternalBridgeTCPClient).
            //OnEnableAutomaticThreadLoadingChanged(EnableAutomaticThreadLoading);
        }

        protected async void OnEnableApplicationLogsChanged(bool value)
        {
            if (value && State == TransportComponentState.Connected && !_logs_sent)
            {
                var request = new StartApplicationLogsRequest();

                bool responseLogged = false;
                _logs_sent = true;

                SendContinuousRequest<StartApplicationLogsRequest, StartApplicationLogsResponse>(request, new TransportContinuousRequestConfig() { ShouldLog = true }).ObserveOn(new NewThreadScheduler())
                    .Subscribe
                    (
                    (response) =>
                    {
                        if (!responseLogged)
                        {
                            responseLogged = true;
                        }

                        OnApplicationLogAvailable(response);
                    },
                    (ex) =>
                    {
                        _logs_sent = false;
                    },
                    () =>
                    {
                        _logs_sent = false;
                    });
            }
            else if (_logs_sent)
            {
                _logs_sent = false;

                if (State == TransportComponentState.Connected)
                {
                    var req = new StopApplicationLogsRequest();

                    try
                    {
                        var res = await SendRequest<StopApplicationLogsRequest, StopApplicationLogsResponse>(req, new TransportRequestConfig() { ShouldLog = true });
                    }
                    catch { }
                }
            }
        }

        private void OnApplicationLogAvailable(TangoMessage<StartApplicationLogsResponse> response)
        {
            try
            {
                if (response.Message.LogItem.Count() > 0)
                {
                    LogItemBase log = LogItemBase.Deserialize(response.Message.LogItem.ToArray());
                    log.LogObject = "External Bridge";

                    if (InjectApplicationLogsToDefaultLogManager)
                    {
                        LogManager.Log(log);
                    }

                    ApplicationLogAvailable?.Invoke(this, log);
                }
            }
            catch (Exception ex)
            {
                LogManager.Log(ex, "Error deserializing incoming application log item!");
            }
        }

        public async override Task Disconnect()
        {
            if (State == TransportComponentState.Connected)
            {
                ExternalBridgeLogoutRequest request = new ExternalBridgeLogoutRequest();

                try
                {
                    var response = await SendRequest<ExternalBridgeLogoutRequest, ExternalBridgeLogoutResponse>(request, new TransportRequestConfig() { ShouldLog = true });
                }
                catch { }

                Status = MachineStatuses.Standby;
            }

            State = TransportComponentState.Disconnected;

            NotifyContinuousRequestMessagesDisconnection();

            SessionLogger.EndSession();

            if (Adapter != null)
            {
                await Adapter.Disconnect();
            }
            LogManager.Log($"{ComponentName} disconnected.");
        }

        internal ExternalBridgeTcpClient()
        {

        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ExternalBridgeTcpClient"/> class.
        /// </summary>
        /// <param name="serialNumber">The machine serial number.</param>
        /// <param name="ipAddress">The machine IP address.</param>
        public ExternalBridgeTcpClient(String serialNumber, String ipAddress)
        {
            ComponentName = $"External Bridge TCP Client {_component_counter++}";
            SerialNumber = serialNumber;

            if (ObservablesStaticCollections.Instance.IsInitialized)
            {
                Machine = ObservablesStaticCollections.Instance.Machines.SingleOrDefault(x => x.SerialNumber == serialNumber);
            }
            IPAddress = ipAddress;
            KeepAliveTimeout = TimeSpan.FromSeconds(5);
            KeepAliveRetries = 2;
            UseKeepAlive = false;
            EnableDiagnostics = true;

            Adapter = new TcpTransportAdapter(IPAddress, SettingsManager.Default.GetOrCreate<IntegrationSettings>().ExternalBridgeServicePort);
        }

        public ExternalBridgeTcpClient(Machine machine, String ipAddress)
        {
            ComponentName = $"External Bridge TCP Client {_component_counter++}";
            Machine = machine;
            SerialNumber = Machine.SerialNumber;
            IPAddress = ipAddress;
            KeepAliveTimeout = TimeSpan.FromSeconds(5);
            KeepAliveRetries = 2;
            UseKeepAlive = false;
            EnableDiagnostics = true;

            Adapter = new TcpTransportAdapter(IPAddress, SettingsManager.Default.GetOrCreate<IntegrationSettings>().ExternalBridgeServicePort);
        }

        /// <summary>
        /// Returns a <see cref="System.String" /> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String" /> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return SerialNumber;
        }

        /// <summary>
        /// Called when a new request has been received.
        /// </summary>
        /// <param name="request">The request.</param>
        protected async override void OnRequestReceived(RequestReceivedEventArgs e)
        {
            base.OnRequestReceived(e);

            var container = e.Container;

            if (container.Type == MessageType.ExternalBridgeLogoutRequest)
            {
                try
                {
                    await SendResponse<ExternalBridgeLogoutResponse>(new ExternalBridgeLogoutResponse(), container.Token);
                }
                catch { }

                await Task.Delay(2000);

                try
                {
                    State = TransportComponentState.Disconnected;
                    if (Adapter != null)
                    {
                        await Adapter.Disconnect();
                    }
                    LogManager.Log("External Bridge TCP client disconnected by the remote host.");
                }
                catch { }

                SessionLogger.EndSession();
                SessionClosed?.Invoke(this, new EventArgs());

                NotifyContinuousRequestMessagesDisconnection();
            }
        }

        protected override void OnMachineStateChanged(MachineState state)
        {
            //Do Nothing...
        }

        /// <summary>
        /// Occurs when the remote host has closed the session.
        /// </summary>
        public event EventHandler SessionClosed;

        /// <summary>
        /// Gets the database machine associated with this client.
        /// </summary>
        public Machine Machine { get; protected set; }

        /// <summary>
        /// Sets the database machine.
        /// </summary>
        /// <param name="machine">The machine.</param>
        public void SetMachine(Machine machine)
        {
            Machine = machine;
        }
    }
}