Commit 078c278d authored by Greg Pettyjohn's avatar Greg Pettyjohn
Browse files

Added a nifty CommandSequence class

parent cfd71a83
Loading
Loading
Loading
Loading
+134 −0
Original line number Diff line number Diff line
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace DeviceLab
{
    public class CommandSequence : ICommand
    {
        private List<ICommand> registeredCommands;
        private Queue<ICommand> commandQueue;
        private object sharedParameter;

        //-------------------------------------------------------------------
        // Constructor
        //-------------------------------------------------------------------
        #region Constructor
        public CommandSequence(Queue<ICommand> commandQueue = null)
        {
            this.registeredCommands = new List<ICommand>();
            this.commandQueue = commandQueue == null ? new Queue<ICommand>() : commandQueue;
        }
        #endregion // Constructor

        //-------------------------------------------------------------------
        // Command Registration
        //-------------------------------------------------------------------
        public void RegisterCommand(ICommand cmd)
        {
            if (cmd == null)
            {
                throw new ArgumentException(nameof(cmd));
            }

            if (cmd == this)
            {
                throw new ArgumentException("Cannot register a CommandSequence with itself");
            }

            lock (this.registeredCommands)
            {
                CommandSequence seq = cmd as CommandSequence;
                if (seq == null)
                {
                    this.registeredCommands.Add(cmd);
                }
                else
                {
                    foreach (ICommand subcmd in seq.registeredCommands)
                    {
                        this.registeredCommands.Add(subcmd);
                    }
                }
            }
            OnCanExecuteChanged();
        }

        //-------------------------------------------------------------------
        // ICommand Implementation
        //-------------------------------------------------------------------
        public event EventHandler CanExecuteChanged;
        private void OnCanExecuteChanged()
        {
            this.CanExecuteChanged?.Invoke(this, new EventArgs());
        }

        public bool CanExecute(object parameter)
        {
            if (this.registeredCommands.Count == 0)
            {
                return false;
            }

            if (this.commandQueue.Count > 0)
            {
                return false;
            }

            return this.registeredCommands[0].CanExecute(parameter);
        }

        public void Execute(object parameter)
        {
            lock (this.commandQueue)
            {
                if (this.registeredCommands.Count > 0 && this.commandQueue.Count == 0)
                {
                    foreach (ICommand cmd in this.registeredCommands)
                    {
                        this.commandQueue.Enqueue(cmd);
                        this.sharedParameter = parameter;
                    }
                }
                else
                {
                    return;
                }
            }

            OnCanExecuteChanged();
            ExecuteNext();
        }

        private void CurrentCommand_CanExecuteChanged(object sender, EventArgs e)
        {
            ICommand cmd = sender as ICommand;
            cmd.CanExecuteChanged -= CurrentCommand_CanExecuteChanged;
            ExecuteNext();
        }

        private void ExecuteNext()
        {
            lock (this.commandQueue)
            {
                while (this.commandQueue.Count > 0 && this.commandQueue.Peek().CanExecute(this.sharedParameter))
                {
                    ICommand cmd = this.commandQueue.Dequeue();
                    cmd.Execute(this.sharedParameter);
                }

                if (this.commandQueue.Count > 0)
                {
                    this.commandQueue.Peek().CanExecuteChanged += CurrentCommand_CanExecuteChanged;
                }
                else
                {
                    OnCanExecuteChanged();
                }
            }
        }
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@
      <SubType>Designer</SubType>
    </ApplicationDefinition>
    <Compile Include="AutoScrollTextBox.cs" />
    <Compile Include="CommandSequence.cs" />
    <Compile Include="DeviceCollectionView.xaml.cs">
      <DependentUpon>DeviceCollectionView.xaml</DependentUpon>
    </Compile>
+2 −0
Original line number Diff line number Diff line
@@ -12,6 +12,8 @@ using static Microsoft.Tools.WindowsDevicePortal.DevicePortal;

namespace DeviceLab
{


    public class DevicePortalViewModel : BindableBase
    {
        //-------------------------------------------------------------------
+1 −58
Original line number Diff line number Diff line
@@ -80,7 +80,6 @@ namespace DeviceLab
        /// <remarks>Diagnostic output will be lost</remarks>
        public DeviceSignInViewModel()
        {
            this.ready = true;
            this.diagnostics = new NullDiagnosticSink();

        }
@@ -91,7 +90,6 @@ namespace DeviceLab
        /// <param name="diags">Diagnostic sink that will receive all the diagnostic output</param>
        public DeviceSignInViewModel(IDiagnosticSink diags)
        {
            this.ready = true;
            this.diagnostics = diags;
        }
        #endregion // Constructors
@@ -127,15 +125,6 @@ namespace DeviceLab
        }
        #endregion // Password

        #region Ready
        private bool ready;
        public bool Ready
        {
            get { return this.ready; }
            private set { SetProperty(ref this.ready, value); }
        }
        #endregion // Ready

        #region Connection Types
        private ObservableCollection<IDevicePortalConnectionFactory> connectionTypes;
        public ObservableCollection<IDevicePortalConnectionFactory> ConnectionTypes
@@ -174,13 +163,10 @@ namespace DeviceLab
            {
                if (connectCommand == null)
                {
                    // TODO
                    //connectCommand = DelegateCommand.FromAsyncHandler(ExecuteConnect, CanExecuteConnect);
                    connectCommand = new DelegateCommand(ExecuteConnect, CanExecuteConnect);
                    connectCommand.ObservesProperty(() => this.DeviceIP);
                    connectCommand.ObservesProperty(() => this.UserName);
                    connectCommand.ObservesProperty(() => this.Password);
                    connectCommand.ObservesProperty(() => this.Ready);
                    connectCommand.ObservesProperty(() => this.ConnectionTypeSelection);
                }
                return connectCommand;
@@ -194,52 +180,9 @@ namespace DeviceLab
            this.SignInAttempts?.Invoke(this, new DeviceSignInEventArgs(portal, conn));
        }
        
        // TODO
        //private async Task OldExecuteConnectAsync()
        //{
        //    this.diagnostics.OutputDiagnosticString("[{0}] Attempting to connect.\n", this.deviceIP);

        //    IDevicePortalConnection conn = this.ConnectionTypeSelection.CreateConnection(this.DeviceIP, this.UserName, this.Password);

        //    DevicePortal portal = new DevicePortal(conn);
        //    DeviceConnectionStatusEventHandler handler = (DevicePortal sender, DeviceConnectionStatusEventArgs args) =>
        //    {
        //        this.diagnostics.OutputDiagnosticString("[{0}] Connection status update: Status: {1}, Phase: {2}\n", portal.Address, args.Status, args.Phase);
        //        if (args.Status == DeviceConnectionStatus.Connected)
        //        {
        //            this.diagnostics.OutputDiagnosticString("[{0}] Language: {1}\n", portal.Address, conn.OsInfo.Language);
        //            this.diagnostics.OutputDiagnosticString("[{0}] Name: {1}\n", portal.Address, conn.OsInfo.Name);
        //            this.diagnostics.OutputDiagnosticString("[{0}] OsEdition: {1}\n", portal.Address, conn.OsInfo.OsEdition);
        //            this.diagnostics.OutputDiagnosticString("[{0}] OsEditionId: {1}\n", portal.Address, conn.OsInfo.OsEditionId);
        //            this.diagnostics.OutputDiagnosticString("[{0}] OsVersionString: {1}\n", portal.Address, conn.OsInfo.OsVersionString);
        //            this.diagnostics.OutputDiagnosticString("[{0}] Platform: {1}\n", portal.Address, conn.OsInfo.Platform);
        //            this.diagnostics.OutputDiagnosticString("[{0}] PlatformName: {1}\n", portal.Address, conn.OsInfo.PlatformName);
        //        }
        //        else if (args.Status == DeviceConnectionStatus.Failed)
        //        {
        //            this.diagnostics.OutputDiagnosticString("[{0}] Bummer.\n", portal.Address);
        //        }
        //    };
        //    portal.ConnectionStatus += handler;

        //    try
        //    {
        //        this.Ready = false;
        //        await portal.Connect();
        //    }
        //    catch (Exception exn)
        //    {
        //        this.diagnostics.OutputDiagnosticString("[{0}] Exception when trying to connect:\n[{0}] {1}\nStackTrace: \n[{0}] {2}\n", portal.Address, exn.Message, exn.StackTrace);
        //    }
        //    this.SignInAttempts?.Invoke(this, new DeviceSignInEventArgs(portal.ConnectionHttpStatusCode, portal));
        //    portal.ConnectionStatus -= handler;
        //    this.Ready = true;
        //}

        private bool CanExecuteConnect()
        {
            return
                Ready &&
                !string.IsNullOrWhiteSpace(this.DeviceIP) &&
                !string.IsNullOrWhiteSpace(this.UserName) &&
                this.Password != null &&
+8 −108
Original line number Diff line number Diff line
@@ -11,102 +11,6 @@ using Microsoft.Tools.WindowsDevicePortal;

namespace DeviceLab
{
    public class NewDeviceHelper : ICommand
    {
        // TODO: Don't close over a DevicePortalViewModel
        // Instead, should just hook up the CanExecuteChanged events on
        // the commands
        private DevicePortalViewModel dpvm;
        private Queue<ICommand> commandQueue;
        private bool executionStarted;

        // TODO:
        public event EventHandler CanExecuteChanged;

        public NewDeviceHelper(DevicePortalViewModel dpvm)
        {
            this.dpvm = dpvm;
            
            this.commandQueue = new Queue<ICommand>();
            this.executionStarted = false;
        }

        public void Enqueue(ICommand command)
        {
            if (this.executionStarted)
            {
                throw new InvalidOperationException("Execution already started");
            }

            this.commandQueue.Enqueue(command);
        }

        private void Dpvm_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if(e.PropertyName == "Ready")
            {
                if(this.dpvm.Ready)
                {
                    DoNextCommand();
                }
            }
        }

        private void DoNextCommand()
        {
            if(this.commandQueue == null)
            {
                throw new InvalidOperationException("Attempting to execute commands without a command queue");
            }

            if(this.commandQueue.Count == 0)
            {
                throw new InvalidOperationException("Attempting to execute command on an empty command queue");
            }

            if(this.commandQueue.Peek().CanExecute(null))
            {
                ICommand nextCommand = this.commandQueue.Dequeue();

                if (this.commandQueue.Count == 0)
                {
                    // Executing the next command will probably trigger property changed
                    // events and since this is the last command we want to disconnect from 
                    // those events
                    this.dpvm.PropertyChanged -= Dpvm_PropertyChanged;
                    this.executionStarted = false;
                }
                nextCommand.Execute(null);
            }
        }

        public bool CanExecute(object parameter)
        {
            if(this.executionStarted)
            {
                return false;
            }

            if(this.commandQueue.Count > 0)
            {
                return this.commandQueue.Peek().CanExecute(null);
            }

            return false;
        }

        public void Execute(object parameter)
        {
            if(this.executionStarted)
            {
                throw new InvalidOperationException("Execution already started");
            }

            dpvm.PropertyChanged += Dpvm_PropertyChanged;
            DoNextCommand();
        }
    }

    public class MainViewModel : BindableBase
    {
        //-------------------------------------------------------------------
@@ -128,6 +32,8 @@ namespace DeviceLab

            this.ConnectedDevices.CollectionChanged += OnConnectedDevicesChanged;
        }
        #endregion // Constructors


        private void OnSignInAttemptCompleted(DeviceSignInViewModel sender, DeviceSignInEventArgs args)
        {
@@ -144,7 +50,6 @@ namespace DeviceLab

            this.ConnectedDevices.Add(new DevicePortalViewModel(args.Portal, Diagnostics));
        }
        #endregion // Constructors

        //-------------------------------------------------------------------
        //  Properties
@@ -404,17 +309,12 @@ namespace DeviceLab
        private void OnDeviceAdded(DevicePortalViewModel dpvm)
        {
            dpvm.PropertyChanged += DevicePropertyChanged;
            // TODO:
            //CompositeCommand command = new CompositeCommand();
            //command.RegisterCommand(dpvm.RefreshDeviceNameCommand);
            //command.RegisterCommand(dpvm.StartListeningForSystemPerfCommand);
            //command.Execute(null);

            NewDeviceHelper ndh = new NewDeviceHelper(dpvm);
            ndh.Enqueue(dpvm.ReestablishConnectionCommand);
            ndh.Enqueue(dpvm.RefreshDeviceNameCommand);
            ndh.Enqueue(dpvm.StartListeningForSystemPerfCommand);
            ndh.Execute(null);
            
            CommandSequence cmdSeq = new CommandSequence();
            cmdSeq.RegisterCommand(dpvm.ReestablishConnectionCommand);
            cmdSeq.RegisterCommand(dpvm.RefreshDeviceNameCommand);
            cmdSeq.RegisterCommand(dpvm.StartListeningForSystemPerfCommand);
            cmdSeq.Execute(null);
        }

        private void OnDeviceRemoved(DevicePortalViewModel dpvm)