Commit cfd71a83 authored by Greg Pettyjohn's avatar Greg Pettyjohn
Browse files

Latest polish

parent d5439590
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -96,6 +96,8 @@
                                        </CheckBox>
                                        <TextBlock Margin="0,0,4,0" Text="Device Name:" />
                                        <TextBlock Text="{Binding DeviceName}"/>
                                        <TextBlock Margin="50,0,4,0" Text="Device Address:" />
                                        <TextBlock Text="{Binding Address}"/>
                                        <Button
                                            DockPanel.Dock="Right"
                                            Height="16" Width="16"
@@ -135,9 +137,11 @@
                            <TextBox Width="100" Margin="0,0,4,0" Text="{Binding DeviceNameEntry, UpdateSourceTrigger=PropertyChanged}" />
                            <Button Padding="2,1,2,1" Command="{Binding RenameCommand}" Content="Rename" />
                        </StackPanel>
                        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Height="20" Margin="0,5,0,0">
                            <Button Content="Reboot" Width="60" Command="{Binding RebootCommand}" />
                            <Button Content="Reestablish Connection" Width="140" Margin="2,0,0,0" Command="{Binding ReestablishConnectionCommand}" />
                            <TextBlock Text="Retry Attempts:" Width="86" Margin="15,0,-5,0" />
                            <local:NumberEntryBox Value="{Binding ConnectionRetryAttempts}" />
                        </StackPanel>
                    </StackPanel>
                </TabItem>
+7 −0
Original line number Diff line number Diff line
@@ -68,6 +68,9 @@
      <DependentUpon>DeviceSignInView.xaml</DependentUpon>
    </Compile>
    <Compile Include="MainViewModel.cs" />
    <Compile Include="NumberEntryBox.xaml.cs">
      <DependentUpon>NumberEntryBox.xaml</DependentUpon>
    </Compile>
    <Compile Include="SelectionListBox.cs" />
    <Compile Include="XboxDevicePortalConnection.cs" />
    <Page Include="BindablePassword.xaml">
@@ -99,6 +102,10 @@
      <DependentUpon>MainWindow.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Compile>
    <Page Include="NumberEntryBox.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="Themes\CustomStyles.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
+36 −4
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ namespace DeviceLab
        #region Constructors
        public DevicePortalViewModel(DevicePortal portal, IDiagnosticSink diags)
        {
            this.ConnectionRetryAttempts = 5;
            this.diagnostics = diags;
            this.portal = portal;
            this.Ready = true;
@@ -160,7 +161,7 @@ namespace DeviceLab
        }
        #endregion // PlatformName

        #region // CPULoad
        #region CPULoad
        private int cpuLoad;
        public string CPULoad
        {
@@ -170,6 +171,22 @@ namespace DeviceLab
            }
        }
        #endregion // CPULoad

        #region ConnectionRetryAttempts
        private int connectionRetryAttempts;
        public int ConnectionRetryAttempts
        {
            get
            {
                return this.connectionRetryAttempts;
            }

            set
            {
                SetProperty(ref this.connectionRetryAttempts, value);
            }
        }
        #endregion // ConnectionRetryAttempts
        #endregion // Properties

        //-------------------------------------------------------------------
@@ -286,6 +303,7 @@ namespace DeviceLab

        private async Task ExecuteRebootAsync()
        {
            await ExecuteStopListeningForSystemPerfAsync();
            this.Ready = false;
            try
            {
@@ -301,6 +319,7 @@ namespace DeviceLab
                ReportException("Reboot", exn);
            }
            await ExecuteReestablishConnectionAsync();
            await ExecuteStartListeningForSystemPerfAsync();
        }
        #endregion // Reboot Command

@@ -349,7 +368,7 @@ namespace DeviceLab
                {
                    await this.portal.Connect();
                    ++numTries;
                } while (this.portal.ConnectionHttpStatusCode != HttpStatusCode.OK);
                } while (this.portal.ConnectionHttpStatusCode != HttpStatusCode.OK && numTries < this.ConnectionRetryAttempts);
                this.Ready = true;
            }
            catch (Exception exn)
@@ -384,9 +403,15 @@ namespace DeviceLab
        {
            this.Ready = false;
            this.portal.SystemPerfMessageReceived += OnSystemPerfReceived;
            await this.portal.StartListeningForSystemPerf();
            await Task.Run(StartListeningHelper);
            this.Ready = true;
        }

        private async Task StartListeningHelper()
        {
            await this.portal.StartListeningForSystemPerf().ConfigureAwait(false);
        }

        #endregion // StartListeningForSystemPerfCommand

        #region StopListeningForSystemPerfCommand
@@ -413,9 +438,16 @@ namespace DeviceLab
        {
            this.Ready = false;
            this.portal.SystemPerfMessageReceived -= OnSystemPerfReceived;
            await this.portal.StopListeningForSystemPerf();
            await StopListeningHelper();
            //await Task.Run(StopListeningHelper);
            await StopListeningHelper();
            this.Ready = true;
        }

        private async Task StopListeningHelper()
        {
            await this.portal.StopListeningForSystemPerf();
        }
        #endregion // StopListeningForSystemPerfCommand
        #endregion // Commands
             
+50 −41
Original line number Diff line number Diff line
@@ -51,15 +51,14 @@ namespace DeviceLab

    public class DeviceSignInEventArgs : System.EventArgs
    {
        internal DeviceSignInEventArgs(HttpStatusCode statusCode, DevicePortal portal)
        internal DeviceSignInEventArgs(DevicePortal portal, IDevicePortalConnection conn)
        {
            this.StatusCode = statusCode;
            this.Portal = portal;
            this.Connection = conn;
        }

        public HttpStatusCode StatusCode { get; private set; }

        public DevicePortal Portal { get; private set; }
        public IDevicePortalConnection Connection { get; private set; }
    }

    public class DeviceSignInViewModel : BindableBase
@@ -175,7 +174,9 @@ namespace DeviceLab
            {
                if (connectCommand == null)
                {
                    connectCommand = DelegateCommand.FromAsyncHandler(ExecuteConnectAsync, CanExecuteConnect);
                    // TODO
                    //connectCommand = DelegateCommand.FromAsyncHandler(ExecuteConnect, CanExecuteConnect);
                    connectCommand = new DelegateCommand(ExecuteConnect, CanExecuteConnect);
                    connectCommand.ObservesProperty(() => this.DeviceIP);
                    connectCommand.ObservesProperty(() => this.UserName);
                    connectCommand.ObservesProperty(() => this.Password);
@@ -186,46 +187,54 @@ namespace DeviceLab
            }
        }

        private async Task ExecuteConnectAsync()
        private void ExecuteConnect()
        {
            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);
            this.SignInAttempts?.Invoke(this, new DeviceSignInEventArgs(portal, conn));
        }
            };
            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;
        }
        // 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()
        {
+102 −75
Original line number Diff line number Diff line
using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Collections.Specialized;
using System.Collections;
using System.ComponentModel;
using System;
using Microsoft.Tools.WindowsDevicePortal;

namespace DeviceLab
{
    public static class CommandExtensions
    {
        public class ChainedCommand
    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;

            private ICommand executeMe;
            private object arg;
        public NewDeviceHelper(DevicePortalViewModel dpvm)
        {
            this.dpvm = dpvm;
            
            private ChainedCommand nextChainedCommand;
            this.commandQueue = new Queue<ICommand>();
            this.executionStarted = false;
        }

            public ChainedCommand(ICommand executeMe, object arg, bool start = true)
        public void Enqueue(ICommand command)
        {
                this.executeMe = executeMe;
                this.arg = arg;
                this.nextChainedCommand = null;
                this.Status = State.NotStarted;
                if(start)
            if (this.executionStarted)
            {
                    Start();
                throw new InvalidOperationException("Execution already started");
            }

            this.commandQueue.Enqueue(command);
        }

            public enum State
        private void Dpvm_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
                NotStarted,
                Waiting,
                Canceled,
                Completed
            };

            public State Status { get; private set; }

            public void Start()
            if(e.PropertyName == "Ready")
            {
                if(this.Status != State.NotStarted)
                if(this.dpvm.Ready)
                {
                    throw new InvalidOperationException("Must be in the NotStarted state");
                    DoNextCommand();
                }
                this.Status = State.Waiting;
                this.executeMe.CanExecuteChanged += ExecuteMe_CanExecuteChanged;
                TryExecute();
            }

            public State Cancel()
            {
                this.executeMe.CanExecuteChanged -= ExecuteMe_CanExecuteChanged;
                State oldState = this.Status;
                this.Status = State.Canceled;
                return oldState;
        }

            public ChainedCommand ExecuteNext(ICommand next, object arg)
        private void DoNextCommand()
        {
                this.nextChainedCommand = new ChainedCommand(next, arg, false);
                TryExecuteNext();
                return this.nextChainedCommand;
            if(this.commandQueue == null)
            {
                throw new InvalidOperationException("Attempting to execute commands without a command queue");
            }

            private void ExecuteMe_CanExecuteChanged(object sender, EventArgs e)
            if(this.commandQueue.Count == 0)
            {
                TryExecute();
                throw new InvalidOperationException("Attempting to execute command on an empty command queue");
            }

            private void TryExecute()
            if(this.commandQueue.Peek().CanExecute(null))
            {
                if (this.executeMe.CanExecute(arg))
                ICommand nextCommand = this.commandQueue.Dequeue();

                if (this.commandQueue.Count == 0)
                {
                    this.executeMe.Execute(arg);
                    this.executeMe.CanExecuteChanged -= ExecuteMe_CanExecuteChanged;
                    this.Status = State.Completed;
                    TryExecuteNext();
                    // 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);
            }
        }

            private void TryExecuteNext()
        public bool CanExecute(object parameter)
        {
                if(this.Status == State.Completed && this.nextChainedCommand != null)
            if(this.executionStarted)
            {
                    this.nextChainedCommand.Start();
                return false;
            }

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

            return false;
        }

        public static ChainedCommand ExecuteWhenReady(this ICommand executeMe, object arg)
        public void Execute(object parameter)
        {
            return new ChainedCommand(executeMe, arg);
            if(this.executionStarted)
            {
                throw new InvalidOperationException("Execution already started");
            }

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

@@ -124,14 +129,21 @@ namespace DeviceLab
            this.ConnectedDevices.CollectionChanged += OnConnectedDevicesChanged;
        }


        private void OnSignInAttemptCompleted(DeviceSignInViewModel sender, DeviceSignInEventArgs args)
        {
            if(args.StatusCode == System.Net.HttpStatusCode.OK)
            DevicePortal portal = args.Portal;
            // See if the device is already in the list
            foreach (DevicePortalViewModel dpvm in this.ConnectedDevices)
            {
                this.ConnectedDevices.Add(new DevicePortalViewModel(args.Portal, Diagnostics));
                if(dpvm.Address == portal.Address)
                {
                    this.Diagnostics.OutputDiagnosticString("[!!] The device with address {0} is already in the list\n", portal.Address);
                    return;
                }
            }

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

        //-------------------------------------------------------------------
@@ -325,39 +337,44 @@ namespace DeviceLab
        #endregion // RebootSelectedDevicesCommand

        #region RemoveDeviceCommand
        private DelegateCommand<DevicePortalViewModel> removeDeviceCommand;
        // NOTE: Ideally, the template parameter would be DevicePortalViewMOdel, rather than object.
        // Unfortunately, this would sporadically throw an invalid cast exception from within the
        // DelegateCommand<> constructor in the PRISM library. Apparently, the object passed to the
        // constructor cannot always be cast to something that would rightfully belong to the 
        // collection. 
        private DelegateCommand<object> removeDeviceCommand;
        public ICommand RemoveDeviceCommand
        {
            get
            {
                if(this.removeDeviceCommand == null)
                {
                    this.removeDeviceCommand = new DelegateCommand<DevicePortalViewModel>(ExecuteRemoveDeviceCommand, CanExecuteRemoveDeviceCommand);
                    this.removeDeviceCommand = new DelegateCommand<object>(ExecuteRemoveDeviceCommand, CanExecuteRemoveDeviceCommand);
                    this.removeDeviceCommand.ObservesProperty(() => SomeDevicesReady);
                }
                return this.removeDeviceCommand;
            }
        }

        private bool CanExecuteRemoveDeviceCommand(DevicePortalViewModel arg)
        private bool CanExecuteRemoveDeviceCommand(object arg)
        {
            if (arg != null)
            DevicePortalViewModel dpvm = arg as DevicePortalViewModel;
            if (dpvm != null)
            {
                return arg.Ready;
                return dpvm.Ready;
            }
            return false;
        }

        private void ExecuteRemoveDeviceCommand(DevicePortalViewModel obj)
        private void ExecuteRemoveDeviceCommand(object obj)
        {
            if (obj != null)
            DevicePortalViewModel dpvm = obj as DevicePortalViewModel;
            if (dpvm != null)
            {
                OnDeviceRemoved(obj);
                this.ConnectedDevices.Remove(obj);
                this.ConnectedDevices.Remove(dpvm);
            }
        }
        #endregion // RemoveDeviceCommand

        #endregion // Commands

        private void OnConnectedDevicesChanged(object sender, NotifyCollectionChangedEventArgs e)
@@ -378,7 +395,7 @@ namespace DeviceLab
                {
                    foreach(DevicePortalViewModel dpvm in e.OldItems)
                    {
                        //OnDeviceRemoved(dpvm);
                        OnDeviceRemoved(dpvm);
                    }
                }
            }
@@ -387,7 +404,17 @@ namespace DeviceLab
        private void OnDeviceAdded(DevicePortalViewModel dpvm)
        {
            dpvm.PropertyChanged += DevicePropertyChanged;
            dpvm.RefreshDeviceNameCommand.ExecuteWhenReady(this).ExecuteNext(dpvm.StartListeningForSystemPerfCommand, this);
            // 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);
        }

        private void OnDeviceRemoved(DevicePortalViewModel dpvm)
Loading