aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Utilities/Tango.RemoteRunner.UI/MainWindowVM.cs
blob: b25f794524c13e8977cb36d435c78c089c143d8d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
<mahapps:MetroWindow x:Class="Tango.MachineEM.UI.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:mahapps="http://metro.mahapps.com/winfx/xaml/controls"
        xmlns:converters="clr-namespace:Tango.SharedUI.Converters;assembly=Tango.SharedUI"
        xmlns:fa="http://schemas.fontawesome.io/icons/"
        xmlns:local="clr-namespace:Tango.MachineEM.UI"
        xmlns:views="clr-namespace:Tango.MachineEM.UI.Views"
        mc:Ignorable="d"
        Title="Tango Machine Emulator" Height="720" Width="1200" TitleCaps="False" BorderBrush="Gray" BorderThickness="1" WindowStartupLocation="CenterScreen" Background="#202020" Foreground="Gainsboro" DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid>
        <views:MainView DataContext="{StaticResource MainViewVM}"></views:MainView>
    </Grid>
</mahapps:MetroWindow>
'#n230'>230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Tango.Logging;
using Tango.PMR;
using Tango.PMR.Common;
using Tango.PMR.Discovery;
using Tango.PMR.IO;
using Tango.SharedUI;
using Tango.Transport;
using Tango.Transport.Adapters;
using Tango.Transport.Discovery;
using Tango.Transport.Servers;
using Tango.Transport.Transporters;

namespace Tango.RemoteRunner.UI
{
    public class MainWindowVM : ViewModel
    {
        private TcpServer _server;
        private ITransporter _transporter;
        private UdpDiscoveryService<BasicDiscoveryMessage> _discoveryService;
        private const int PORT = 9595;
        private List<FileUpload> _uploads;
        private const long MAX_CHUNK_LENGTH = 500000; //500kb

        private Action<String> _notificationAction;
        private ObservableCollection<RunningProcess> _runningProcesses;
        public ObservableCollection<RunningProcess> RunningProcesses
        {
            get { return _runningProcesses; }
            set { _runningProcesses = value; RaisePropertyChangedAuto(); }
        }

        private String _log;
        public String Log
        {
            get { return _log; }
            set { _log = value; RaisePropertyChangedAuto(); }
        }

        public MainWindowVM()
        {
            RunningProcesses = new ObservableCollection<RunningProcess>();
            _uploads = new List<FileUpload>();

            var logger = new SimpleStringLogger() { Enabled = true };
            logger.LogReceived += (_, log) =>
            {
                Log += String.Format("{0}: {1}", log.TimeStamp.ToShortTimeString(), log.Message) + Environment.NewLine;
            };
            LogManager.RegisterLogger(logger);
        }

        public void ViewLoaded()
        {
            LogManager.Log("Application Started!");
            LogManager.Log("Initializing TCP listener on port " + PORT + "...");
            _server = new TcpServer(PORT);
            _server.ClientConnected += _server_ClientConnected;
            _server.Start();

            LogManager.Log("Initializing basic transporter...");
            _transporter = new BasicTransporter();
            _transporter.FailsWithAdapter = false;
            _transporter.RequestReceived += _transporter_RequestReceived;

            _discoveryService = new UdpDiscoveryService<BasicDiscoveryMessage>(2018, new BasicDiscoveryMessage()
            {
                ServiceName = "Tango Remote Runner",
                Port = PORT,
            });

            _discoveryService.Interval = TimeSpan.FromSeconds(1);
            _discoveryService.Start();
        }

        private void _transporter_RequestReceived(object sender, MessageContainer container)
        {
            try
            {
                switch (container.Type)
                {
                    case MessageType.FileUploadRequest:
                        HandleFileUploadRequest(MessageFactory.ParseTangoMessageFromContainer<FileUploadRequest>(container));
                        break;
                    case MessageType.FileChunkUploadRequest:
                        HandleFileChunkUploadRequest(MessageFactory.ParseTangoMessageFromContainer<FileChunkUploadRequest>(container));
                        break;
                    case MessageType.ExecuteProcessRequest:
                        HandleExecuteProcessRequest(MessageFactory.ParseTangoMessageFromContainer<ExecuteProcessRequest>(container));
                        break;
                    case MessageType.KillProcessRequest:
                        HandleKillProcessRequest(MessageFactory.ParseTangoMessageFromContainer<KillProcessRequest>(container));
                        break;
                }
            }
            catch (Exception ex)
            {
                LogManager.Log(ex);
                _transporter.SendErrorResponse(ex, container.Token);
            }
        }

        #region Request Handlers

        private void HandleFileUploadRequest(TangoMessage<FileUploadRequest> request)
        {
            LogManager.Log("File upload request received " + request.Message.FileName);

            Notify("Upload request " + request.Message.FileName);

            var tempFolder = TemporaryManager.CreateFolder();
            LogManager.Log("Created temporary upload folder " + tempFolder.Path);

            String uploadID = Guid.NewGuid().ToString();

            _uploads.Add(new FileUpload()
            {
                Folder = tempFolder,
                UploadID = uploadID,
                FileName = request.Message.FileName,
                Length = request.Message.Length,
            });

            _transporter.SendResponse<FileUploadResponse>(new FileUploadResponse()
            {
                UploadID = uploadID,
                MaxChunkLength = MAX_CHUNK_LENGTH,
            }, request.Container.Token);
        }

        private void HandleFileChunkUploadRequest(TangoMessage<FileChunkUploadRequest> request)
        {
            FileUpload upload = _uploads.SingleOrDefault(x => x.UploadID == request.Message.UploadID);
            if (upload == null)
            {
                _transporter.SendResponse<FileChunkUploadResponse>(new FileChunkUploadResponse() { IsCanceled = true }, request.Container.Token, true, ErrorCode.InvalidUploadId);
                return;
            }

            if (upload.CurrentLength + request.Message.Buffer.Length > upload.Length)
            {
                _transporter.SendResponse<FileChunkUploadResponse>(new FileChunkUploadResponse() { IsCanceled = true }, request.Container.Token, true, ErrorCode.FileLengthOutOfRange);
                return;
            }

            using (FileStream fs = new FileStream(upload.FullPath, FileMode.Append))
            {
                byte[] buffer = request.Message.Buffer.ToByteArray();
                fs.Write(buffer, 0, buffer.Length);
                upload.CurrentLength = fs.Length;

                if (fs.Length == upload.Length)
                {
                    upload.Completed = true;
                    LogManager.Log("File upload completed " + upload.FileName);
                }
            }

            _transporter.SendResponse<FileChunkUploadResponse>(new FileChunkUploadResponse(), request.Container.Token);
        }

        private void HandleExecuteProcessRequest(TangoMessage<ExecuteProcessRequest> request)
        {
            LogManager.Log("Execute process request received " + request.Message.FileName);

            Notify("Execute request " + request.Message.FileName);

            FileUpload upload = _uploads.SingleOrDefault(x => x.UploadID == request.Message.UploadID);
            if (upload == null || !upload.Completed)
            {
                _transporter.SendResponse<ExecuteProcessResponse>(new ExecuteProcessResponse(), request.Container.Token, true, ErrorCode.InvalidUploadId);
                return;
            }

            if (Path.GetExtension(upload.FileName).ToLower() == ".zip" && !upload.Expanded)
            {
                LogManager.Log("Extracting zip file to " + upload.Folder.Path);
                ZipFile.ExtractToDirectory(upload.FullPath, upload.Folder.Path);
            }

            upload.Expanded = true;

            RunningProcess process = new RunningProcess();
            process.Process = Process.Start(Path.Combine(upload.Folder.Path, Path.GetFileName(request.Message.FileName)));

            process.Process.Exited += (_, __) => 
            {
                RunningProcesses.Remove(process);
            };

            LogManager.Log("Process started " + process.ProcessName + ", " + process.ProcessID);

            InvokeUINow(() =>
            {
                RunningProcesses.Add(process);
            });

            _transporter.SendResponse<ExecuteProcessResponse>(new ExecuteProcessResponse() { ProcessID = process.ProcessID }, request.Container.Token);
        }

        private void HandleKillProcessRequest(TangoMessage<KillProcessRequest> request)
        {
            LogManager.Log("Kill process request received " + request.Message.ProcessID);
            Notify("Kill request " + request.Message.ProcessID);

            var process = RunningProcesses.SingleOrDefault(x => x.ProcessID == request.Message.ProcessID);

            if (process == null)
            {
                _transporter.SendResponse<KillProcessResponse>(new KillProcessResponse(), request.Container.Token, true, ErrorCode.InvalidProcessId);
                return;
            }

            process.Process.Kill();
            LogManager.Log("Process killed " + process.ProcessName + ", " + process.ProcessID);
            _transporter.SendResponse<KillProcessResponse>(new KillProcessResponse(), request.Container.Token);
        }

        #endregion

        private void _server_ClientConnected(object sender, ClientConnectedEventArgs e)
        {
            Task.Factory.StartNew(async () =>
            {
                try
                {
                    Stopwatch watch = new Stopwatch();
                    watch.Start();

                    LogManager.Log("Remote client connection from " + e.Socket.GetIPAddress());
                    LogManager.Log("Initializing transporter with a new TCP adapter...");

                    Notify("Client connected from " + e.Socket.GetIPAddress());

                    await _transporter.Disconnect();
                    TcpTransportAdapter adapter = new TcpTransportAdapter(e.Socket);
                    _transporter.Adapter = adapter;
                    await _transporter.Connect();

                    LogManager.Log(watch.Elapsed.Milliseconds + " milliseconds");
                }
                catch (Exception ex)
                {
                    LogManager.Log(ex);
                }
            });
        }

        public void SetNotificationAction(Action<String> notificationAction)
        {
            _notificationAction = notificationAction;
        }

        private void Notify(String text)
        {
            _notificationAction?.Invoke(text);
        }
    }
}