diff options
| author | Shlomo Hecht <shlomo@twine-s.com> | 2018-05-02 17:36:54 +0300 |
|---|---|---|
| committer | Shlomo Hecht <shlomo@twine-s.com> | 2018-05-02 17:36:54 +0300 |
| commit | ee697f7a3350d0a97bddee4de3a2ae4f9d285052 (patch) | |
| tree | 2dc2e3bb811b0d89a3c4c51801c1572966fcee7c /Software | |
| parent | 73c4b814f1f28170ae72723568189096413c3564 (diff) | |
| download | Tango-ee697f7a3350d0a97bddee4de3a2ae4f9d285052.tar.gz Tango-ee697f7a3350d0a97bddee4de3a2ae4f9d285052.zip | |
merge
Diffstat (limited to 'Software')
60 files changed, 2258 insertions, 220 deletions
diff --git a/Software/DB/Tango.mdf b/Software/DB/Tango.mdf Binary files differindex ebd7dc607..7eccbdff2 100644 --- a/Software/DB/Tango.mdf +++ b/Software/DB/Tango.mdf diff --git a/Software/DB/Tango_log.ldf b/Software/DB/Tango_log.ldf Binary files differindex 9f1c3dcfe..9e5f0e2af 100644 --- a/Software/DB/Tango_log.ldf +++ b/Software/DB/Tango_log.ldf diff --git a/Software/PMR/Messages/Connection/ConnectResponse.proto b/Software/PMR/Messages/Connection/ConnectResponse.proto index 8f3be5c20..e6c178083 100644 --- a/Software/PMR/Messages/Connection/ConnectResponse.proto +++ b/Software/PMR/Messages/Connection/ConnectResponse.proto @@ -1,9 +1,11 @@ syntax = "proto3"; +import "DeviceInformation.proto"; + package Tango.PMR.Connection; option java_package = "com.twine.tango.pmr.connection"; message ConnectResponse { - + DeviceInformation DeviceInformation = 1; }
\ No newline at end of file diff --git a/Software/PMR/Messages/Connection/DeviceInformation.proto b/Software/PMR/Messages/Connection/DeviceInformation.proto new file mode 100644 index 000000000..bc217c799 --- /dev/null +++ b/Software/PMR/Messages/Connection/DeviceInformation.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +package Tango.PMR.Connection; +option java_package = "com.twine.tango.pmr.connection"; + +message DeviceInformation +{ + string Name = 1; + string Version = 2; + string BuildDate = 3; +}
\ No newline at end of file diff --git a/Software/PMR/Messages/Diagnostics/PushDiagnosticsResponse.proto b/Software/PMR/Messages/Diagnostics/PushDiagnosticsResponse.proto index 3fdeaf40f..6c18586a5 100644 --- a/Software/PMR/Messages/Diagnostics/PushDiagnosticsResponse.proto +++ b/Software/PMR/Messages/Diagnostics/PushDiagnosticsResponse.proto @@ -21,9 +21,4 @@ message PushDiagnosticsResponse //Hardware Errors & Warnings repeated Event Events = 4; - - //Software Information - string Version = 5; - string VersionName = 6; - string VersionBuildDate = 7; }
\ No newline at end of file diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Views/MachineTechView.xaml b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Views/MachineTechView.xaml index 3d4ecdac1..53fcafb8f 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Views/MachineTechView.xaml +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Views/MachineTechView.xaml @@ -220,9 +220,9 @@ <Border HorizontalAlignment="Right" Width="220" Margin="10 0 5 8" BorderBrush="Gainsboro" BorderThickness="1 0 1 1" Padding="5"> <Grid> <StackPanel TextElement.FontSize="10" VerticalAlignment="Center" TextElement.Foreground="Gray"> - <TextBlock HorizontalAlignment="Center"><Run FontWeight="SemiBold" FontStyle="Italic">Version Name:</Run> <Run Text="{Binding CurrentDiagnosticsResponse.VersionName,FallbackValue='N/A'}"></Run></TextBlock> - <TextBlock HorizontalAlignment="Center" Margin="0 2 0 0"><Run FontWeight="SemiBold" FontStyle="Italic">Version Number:</Run> <Run Text="{Binding CurrentDiagnosticsResponse.Version,FallbackValue='N/A'}"></Run></TextBlock> - <TextBlock HorizontalAlignment="Center" Margin="0 2 0 0"><Run FontWeight="SemiBold" FontStyle="Italic">Build Date:</Run> <Run Text="{Binding CurrentDiagnosticsResponse.VersionBuildDate,FallbackValue='N/A'}"></Run></TextBlock> + <TextBlock HorizontalAlignment="Center"><Run FontWeight="SemiBold" FontStyle="Italic">Version Name:</Run> <Run Text="{Binding MachineOperator.DeviceInformation.Name,FallbackValue='N/A'}"></Run></TextBlock> + <TextBlock HorizontalAlignment="Center" Margin="0 2 0 0"><Run FontWeight="SemiBold" FontStyle="Italic">Version Number:</Run> <Run Text="{Binding MachineOperator.DeviceInformation.Version,FallbackValue='N/A'}"></Run></TextBlock> + <TextBlock HorizontalAlignment="Center" Margin="0 2 0 0"><Run FontWeight="SemiBold" FontStyle="Italic">Build Date:</Run> <Run Text="{Binding MachineOperator.DeviceInformation.BuildDate,FallbackValue='N/A'}"></Run></TextBlock> <TextBlock HorizontalAlignment="Center" Margin="0 2 0 0"><Run FontWeight="SemiBold" FontStyle="Italic">Diagnostics Frame Size:</Run> <Run Text="{Binding CurrentDiagnosticsResponseSize,Converter={StaticResource NumberToFileSizeConverter},FallbackValue='N/A'}"></Run></TextBlock> </StackPanel> </Grid> diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Converters/PermissionToVisibilityConverter.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Converters/PermissionToVisibilityConverter.cs new file mode 100644 index 000000000..08e7d1c12 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Converters/PermissionToVisibilityConverter.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Data; +using Tango.BL.Entities; +using Tango.BL.Enumerations; +using Tango.Core.DI; +using Tango.MachineStudio.Common.Authentication; + +namespace Tango.MachineStudio.Common.Converters +{ + public class PermissionToVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + User user = value as User; + + if (user != null) + { + if (user.HasPermission((Permissions)parameter)) + { + return Visibility.Visible; + } + } + + return Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/IStudioViewModel.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/IStudioViewModel.cs index 35ed50cd9..4203a1e8b 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/IStudioViewModel.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/IStudioViewModel.cs @@ -38,5 +38,10 @@ namespace Tango.MachineStudio.Common /// </summary> /// <param name="args">The arguments.</param> void OnModuleRequest(params object[] args); + + /// <summary> + /// Called when the application has been started + /// </summary> + void OnApplicationStarted(); } } diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/StudioApplication/IStudioApplicationManager.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/StudioApplication/IStudioApplicationManager.cs index be793ac81..1de18ac94 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/StudioApplication/IStudioApplicationManager.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/StudioApplication/IStudioApplicationManager.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Windows; using Tango.Integration.Services; namespace Tango.MachineStudio.Common.StudioApplication @@ -53,5 +54,12 @@ namespace Tango.MachineStudio.Common.StudioApplication /// Gets the machine studio application version. /// </summary> String Version { get; } + + /// <summary> + /// Notify the application manager about an external opened window. + /// When application exists. All registered windows will be closed. + /// </summary> + /// <param name="window">The window.</param> + void RegisterOpenedWindow(Window window); } } diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/StudioViewModel.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/StudioViewModel.cs index 9c7e52d23..77fad1fc6 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/StudioViewModel.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/StudioViewModel.cs @@ -13,6 +13,79 @@ namespace Tango.MachineStudio.Common /// <typeparam name="Module">The type of the module.</typeparam> /// <seealso cref="Tango.SharedUI.ViewModel" /> /// <seealso cref="Tango.MachineStudio.Common.IStudioViewModel" /> + public abstract class StudioViewModelInternal : ViewModel, IStudioViewModel + { + /// <summary> + /// Gets or sets a value indicating whether this view model studio module is currently loaded. + /// </summary> + public bool IsModuleLoaded { get; private set; } + + /// <summary> + /// Initializes a new instance of the <see cref="StudioViewModel{Module}"/> class. + /// </summary> + public StudioViewModelInternal() : base() + { + + } + + /// <summary> + /// Called when another module has wants to navigate to this module with some arguments. + /// </summary> + /// <param name="args">The arguments.</param> + public virtual void OnModuleRequest(params object[] args) + { + + } + + /// <summary> + /// Called when the user has navigated out of the module. + /// </summary> + public virtual void OnNavigatedFrom() + { + IsModuleLoaded = false; + } + + /// <summary> + /// Called when the user has navigated in to the module. + /// </summary> + public virtual void OnNavigatedTo() + { + IsModuleLoaded = true; + } + + /// <summary> + /// Called before the application is shutting down. + /// Return false to cancel the shutdown in case an important process is in progress. + /// </summary> + /// <returns></returns> + public virtual Task<bool> OnShutdownRequest() + { + return Task.FromResult(true); + } + + /// <summary> + /// Called when application is shutting down. + /// </summary> + public virtual void OnShuttingDown() + { + + } + + /// <summary> + /// Called when the application has been started + /// </summary> + public virtual void OnApplicationStarted() + { + + } + } + + /// <summary> + /// Represents a Machine Studio view model + /// </summary> + /// <typeparam name="Module">The type of the module.</typeparam> + /// <seealso cref="Tango.SharedUI.ViewModel" /> + /// <seealso cref="Tango.MachineStudio.Common.IStudioViewModel" /> public abstract class StudioViewModel<Module> : ViewModel, IStudioViewModel where Module : IStudioModule { /// <summary> @@ -70,6 +143,14 @@ namespace Tango.MachineStudio.Common { } + + /// <summary> + /// Called when the application has been started + /// </summary> + public virtual void OnApplicationStarted() + { + + } } /// <summary> @@ -91,6 +172,14 @@ namespace Tango.MachineStudio.Common } /// <summary> + /// Called when the application has been started + /// </summary> + public virtual void OnApplicationStarted() + { + + } + + /// <summary> /// Called when another module has wants to navigate to this module with some arguments. /// </summary> /// <param name="args">The arguments.</param> diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Tango.MachineStudio.Common.csproj b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Tango.MachineStudio.Common.csproj index e4a2720b9..ac6f84618 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Tango.MachineStudio.Common.csproj +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.Common/Tango.MachineStudio.Common.csproj @@ -89,6 +89,7 @@ <DependentUpon>RealTimeGraphControl.xaml</DependentUpon> </Compile> <Compile Include="Controls\TableGrid.cs" /> + <Compile Include="Converters\PermissionToVisibilityConverter.cs" /> <Compile Include="Converters\SecondsToGraphPointsConverter.cs" /> <Compile Include="Diagnostics\DefaultDiagnosticsFrameProvider.cs" /> <Compile Include="Diagnostics\IDiagnosticsFrameProvider.cs" /> @@ -287,5 +288,8 @@ <EmbedInteropTypes>True</EmbedInteropTypes> </COMReference> </ItemGroup> + <ItemGroup> + <Folder Include="MarkupExtensions\" /> + </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> </Project>
\ No newline at end of file diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/App.xaml.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/App.xaml.cs index d9c64b9e3..f124ebb54 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/App.xaml.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/App.xaml.cs @@ -15,6 +15,11 @@ using Tango.Settings; using Tango.MachineStudio.Common.EventLogging; using Tango.BL.Enumerations; using Tango.Core.DI; +using Tango.MachineStudio.UI.TFS; +using Tango.TFS; +using Tango.MachineStudio.Common.Notifications; +using Tango.MachineStudio.UI.ViewModels; +using Tango.MachineStudio.UI.Views; namespace Tango.MachineStudio.UI { @@ -82,7 +87,26 @@ namespace Tango.MachineStudio.UI Application.Current.Dispatcher.Invoke(() => { - ExceptionWindow exWin = new ExceptionWindow(e.Exception); + WorkItem bug = null; + TeamFoundationServiceExtendedClient tfsClient = null; + INotificationProvider notification = null; + + try + { + tfsClient = TangoIOC.Default.GetInstance<TeamFoundationServiceExtendedClient>(); + notification = TangoIOC.Default.GetInstance<INotificationProvider>(); + + if (tfsClient != null && tfsClient.IsInitialized) + { + bug = tfsClient.CreateBug(); + } + } + catch (Exception ex) + { + Debug.WriteLine(ex.ToString()); + } + + ExceptionWindow exWin = new ExceptionWindow(e.Exception, bug != null); exWin.ShowDialog(); switch (exWin.Resolution) @@ -101,6 +125,31 @@ namespace Tango.MachineStudio.UI LogManager.Log("User selection was to shutdown the application. Restarting..."); Environment.Exit(0); break; + case ExceptionResolutions.Report: + e.TryRecover = true; + LogManager.Log("User selection was to report the issue."); + + if (bug != null) + { + notification.ShowModalDialog<ReportIssueViewVM, ReportIssueView>(new ReportIssueViewVM(tfsClient.Project, bug), async (vm) => + { + using (notification.PushTaskItem("Uploading bug report...")) + { + try + { + tfsClient.FinalizeBug(vm.WorkItem); + await tfsClient.UploadWorkItem(vm.WorkItem); + } + catch (Exception ex) + { + notification.ShowError("An error occurred while trying to create the issue." + Environment.NewLine + ex.Message); + } + } + + }, null); + } + + break; } }); } diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Console/CodeTemplate.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Console/CodeTemplate.cs new file mode 100644 index 000000000..4b307051f --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Console/CodeTemplate.cs @@ -0,0 +1,23 @@ +using System; +using System.Text; +using System.Linq; +using System.Drawing; +using System.Diagnostics; +using System.Windows.Forms; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; +using Tango.MachineStudio.Common.Notifications; +using Tango.MachineStudio.UI.Console; + +public void OnExecute(ConsoleManager manager) +{ + manager.InvokeUI(() => + { + + var notification = manager.TangoIOC.GetInstance<INotificationProvider>(); + notification.ShowInfo("Hello world!"); + + }); +} + diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Console/ConsoleManager.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Console/ConsoleManager.cs new file mode 100644 index 000000000..e2d525d95 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Console/ConsoleManager.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.Core.DI; +using Tango.MachineStudio.Common.Modules; + +namespace Tango.MachineStudio.UI.Console +{ + public class ConsoleManager + { + public TangoIOC TangoIOC { get; set; } + private Action<String> _writeLine; + + public ConsoleManager(Action<String> writeLine) + { + _writeLine = writeLine; + TangoIOC = TangoIOC.Default; + } + + public void WriteLine(String text) + { + _writeLine(text); + } + + public void InvokeUI(Action action) + { + Core.Helpers.ThreadsHelper.InvokeUI(action); + } + + public void StartModule(String name) + { + IStudioModuleLoader loader = TangoIOC.Default.GetInstance<IStudioModuleLoader>(); + var module = loader.AllModules.SingleOrDefault(x => x.Name == name); + TangoIOC.Default.GetInstance<ViewModels.MainViewVM>().StartModule(module); + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Console/ConsoleOnExecuteParameters.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Console/ConsoleOnExecuteParameters.cs new file mode 100644 index 000000000..b06637ccd --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Console/ConsoleOnExecuteParameters.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.Scripting; + +namespace Tango.MachineStudio.UI.Console +{ + public class ConsoleOnExecuteParameters : OnExecuteParameters + { + /// <summary> + /// Provides access to the script stub manager. + /// </summary> + public ConsoleManager manager; + + /// <summary> + /// Initializes a new instance of the <see cref="StubOnExecuteParameters"/> class. + /// </summary> + /// <param name="manager">The manager.</param> + public ConsoleOnExecuteParameters(ConsoleManager manager) + { + this.manager = manager; + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Console/ConsoleWindow.xaml b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Console/ConsoleWindow.xaml new file mode 100644 index 000000000..22338fea7 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Console/ConsoleWindow.xaml @@ -0,0 +1,223 @@ +<mahapps:MetroWindow x:Class="Tango.MachineStudio.UI.Console.ConsoleWindow" + 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:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" + xmlns:converters="clr-namespace:Tango.SharedUI.Converters;assembly=Tango.SharedUI" + xmlns:mahapps="http://metro.mahapps.com/winfx/xaml/controls" + xmlns:controls="clr-namespace:Tango.SharedUI.Controls;assembly=Tango.SharedUI" + xmlns:views="clr-namespace:Tango.MachineStudio.UI.Views" + xmlns:fa="http://schemas.fontawesome.io/icons/" + xmlns:sharedControls="clr-namespace:Tango.SharedUI.Controls;assembly=Tango.SharedUI" + xmlns:console="clr-namespace:Tango.MachineStudio.UI.Console" + xmlns:local="clr-namespace:Tango.MachineStudio.UI.Console" + mc:Ignorable="d" + Title="Developer Console" Height="800" Width="1280" BorderThickness="1" BorderBrush="#ADADAD" d:DataContext="{d:DesignInstance Type=console:ConsoleWindowVM, IsDesignTimeCreatable=False}" DataContext="{Binding ConsoleWindowVM, Source={StaticResource Locator}}" Foreground="Gainsboro"> + + <Window.Resources> + <ResourceDictionary> + <ResourceDictionary.MergedDictionaries> + <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseDark.xaml" /> + <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/FlatButton.xaml" /> + <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/VS/Colors.xaml" /> + <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/VS/Styles.xaml" /> + <ResourceDictionary> + <converters:StringEllipsisConverter x:Key="StringEllipsisConverter" /> + </ResourceDictionary> + </ResourceDictionary.MergedDictionaries> + </ResourceDictionary> + </Window.Resources> + + <Grid> + <Grid.RowDefinitions> + <RowDefinition Height="Auto"/> + <RowDefinition Height="742*"/> + </Grid.RowDefinitions> + + <Menu IsMainMenu="True" BorderThickness="0"> + <MenuItem Header="File"> + <MenuItem Header="New" Command="{Binding NewCommand}"> + <MenuItem.Icon> + <fa:ImageAwesome Icon="FileOutline" Width="12" Foreground="Gainsboro" Margin="2" /> + </MenuItem.Icon> + </MenuItem> + <Separator/> + <MenuItem Header="Open" MinWidth="150" Command="{Binding OpenCommand}"> + <MenuItem.Icon> + <fa:ImageAwesome Icon="FolderOutline" Width="12" Foreground="Gainsboro" Margin="2" /> + </MenuItem.Icon> + </MenuItem> + <Separator/> + <MenuItem Header="Save" Command="{Binding SaveCommand}"> + <MenuItem.Icon> + <fa:ImageAwesome Icon="Save" Width="12" Foreground="Gainsboro" Margin="2" /> + </MenuItem.Icon> + </MenuItem> + <MenuItem Header="Save as" Command="{Binding SaveAsCommand}"> + <MenuItem.Icon> + <Grid> + <fa:ImageAwesome Icon="Save" Width="10" Foreground="Gainsboro" Margin="2" /> + <fa:ImageAwesome Icon="Save" Width="10" Foreground="Gainsboro" Margin="2 -5 -5 2" /> + </Grid> + </MenuItem.Icon> + </MenuItem> + </MenuItem> + <MenuItem Header="Edit"> + <MenuItem Header="Cut" MinWidth="150" Command="Cut"> + <MenuItem.Icon> + <fa:ImageAwesome Icon="Cut" Width="12" Foreground="Gainsboro" Margin="2" /> + </MenuItem.Icon> + </MenuItem> + <Separator/> + <MenuItem Header="Copy" Command="Copy"> + <MenuItem.Icon> + <fa:ImageAwesome Icon="Copy" Width="12" Foreground="Gainsboro" Margin="2" /> + </MenuItem.Icon> + </MenuItem> + <MenuItem Header="Paste" Command="Paste"> + <MenuItem.Icon> + <fa:ImageAwesome Icon="Paste" Width="12" Foreground="Gainsboro" Margin="2" /> + </MenuItem.Icon> + </MenuItem> + <Separator/> + <MenuItem Header="Undo" Command="Undo"> + <MenuItem.Icon> + <fa:ImageAwesome Icon="Undo" Width="12" Foreground="Gainsboro" Margin="2" /> + </MenuItem.Icon> + </MenuItem> + <MenuItem Header="Redo" Command="Redo"> + <MenuItem.Icon> + <fa:ImageAwesome Icon="Repeat" Width="12" Foreground="Gainsboro" Margin="2" /> + </MenuItem.Icon> + </MenuItem> + </MenuItem> + <MenuItem Header="Debug"> + <MenuItem Header="Run (F5)" MinWidth="150" Command="{Binding RunCommand}" > + <MenuItem.Icon> + <fa:ImageAwesome Icon="Play" Width="12" Margin="2"> + <fa:ImageAwesome.Style> + <Style TargetType="fa:ImageAwesome"> + <Setter Property="Foreground" Value="#8DD28A"></Setter> + <Style.Triggers> + <Trigger Property="IsEnabled" Value="False"> + <Setter Property="Foreground" Value="Gray"></Setter> + </Trigger> + </Style.Triggers> + </Style> + </fa:ImageAwesome.Style> + </fa:ImageAwesome> + </MenuItem.Icon> + </MenuItem> + <MenuItem Header="Stop" Command="{Binding StopCommand}" IsEnabled="False"> + <MenuItem.Icon> + <fa:ImageAwesome Icon="Stop" Width="12" Margin="2"> + <fa:ImageAwesome.Style> + <Style TargetType="fa:ImageAwesome"> + <Setter Property="Foreground" Value="#F38B76"></Setter> + <Style.Triggers> + <Trigger Property="IsEnabled" Value="False"> + <Setter Property="Foreground" Value="Gray"></Setter> + </Trigger> + </Style.Triggers> + </Style> + </fa:ImageAwesome.Style> + </fa:ImageAwesome> + </MenuItem.Icon> + </MenuItem> + </MenuItem> + </Menu> + + <DockPanel Grid.Row="1"> + <materialDesign:ColorZone Background="#2E2E2E" Padding="16" materialDesign:ShadowAssist.ShadowDepth="Depth2" + Mode="PrimaryMid" DockPanel.Dock="Top"> + <DockPanel> + <Grid> + <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Height="60" HorizontalAlignment="Center"> + <Image Source="/Images/machine-trans.png" RenderOptions.BitmapScalingMode="Fant"></Image> + <TextBlock Text="Machine Studio" VerticalAlignment="Center" Margin="20 0 0 0" FontSize="36"/> + </StackPanel> + </Grid> + </DockPanel> + </materialDesign:ColorZone> + + <Grid> + <Grid> + <Grid.RowDefinitions> + <RowDefinition Height="Auto"/> + <RowDefinition Height="1*"/> + </Grid.RowDefinitions> + + <Grid Grid.Row="1" x:Name="grid"> + <Grid> + <Grid.RowDefinitions> + <RowDefinition Height="467*"/> + <RowDefinition Height="5"/> + <RowDefinition Height="200*"/> + </Grid.RowDefinitions> + + <controls:ScriptEditorControl BorderThickness="0 1 0 0" BorderBrush="#616161" Text="{Binding Code,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" RunCommand="{Binding RunCommand}" StopCommand="{Binding StopCommand}" HighlightTypes="{Binding HighlightTypes}" IntellisenseTypes="{Binding IntellisenseTypes}"></controls:ScriptEditorControl> + + <GridSplitter Grid.Row="1" Background="#101010" Foreground="#202020" BorderBrush="#202020" Height="5" HorizontalAlignment="Stretch" VerticalAlignment="Center" /> + + + <Grid Grid.Row="2" Background="#1B1B1B"> + <Grid.RowDefinitions> + <RowDefinition Height="20"/> + <RowDefinition Height="57*"/> + </Grid.RowDefinitions> + + <Border BorderThickness="0 0 0 1" BorderBrush="#595959"> + <TextBlock Margin="2" HorizontalAlignment="Center">LOG</TextBlock> + </Border> + + <TextBox x:Name="txtLog" TextChanged="TextBox_TextChanged" FontFamily="Lucida Console" Background="#202020" BorderThickness="0" AcceptsReturn="True" VerticalScrollBarVisibility="Visible" Padding="5" IsReadOnly="True" TextWrapping="Wrap" FontSize="11" Foreground="Gainsboro" Grid.Row="2"></TextBox> + </Grid> + </Grid> + </Grid> + </Grid> + + <Border HorizontalAlignment="Right" Margin="0 -1 10 0" VerticalAlignment="Top" Width="300" Height="40" Padding="5" CornerRadius="0 0 30 30" BorderThickness="1 0 1 1" BorderBrush="DimGray"> + <Border.Style> + <Style TargetType="Border"> + <Setter Property="RenderTransform"> + <Setter.Value> + <ScaleTransform ScaleX="1" ScaleY="0"></ScaleTransform> + </Setter.Value> + </Setter> + <Style.Triggers> + <DataTrigger Binding="{Binding NotificationProvider.HasTaskItems}" Value="True"> + <DataTrigger.EnterActions> + <BeginStoryboard HandoffBehavior="Compose"> + <Storyboard> + <DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" FillBehavior="HoldEnd" To="1" Duration="00:00:0.2"></DoubleAnimation> + </Storyboard> + </BeginStoryboard> + </DataTrigger.EnterActions> + <DataTrigger.ExitActions> + <BeginStoryboard HandoffBehavior="Compose"> + <Storyboard> + <DoubleAnimation BeginTime="00:00:02" FillBehavior="HoldEnd" Storyboard.TargetProperty="RenderTransform.ScaleY" To="0" From="1" Duration="00:00:0.5"></DoubleAnimation> + </Storyboard> + </BeginStoryboard> + </DataTrigger.ExitActions> + </DataTrigger> + </Style.Triggers> + </Style> + </Border.Style> + <Border.Background> + <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1"> + <GradientStop Color="#03A9F4"/> + <GradientStop Color="#0081BB" Offset="1"/> + </LinearGradientBrush> + </Border.Background> + + <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="20 0 0 0"> + <mahapps:ProgressRing Width="24" Height="24" Foreground="White"></mahapps:ProgressRing> + <TextBlock Text="{Binding NotificationProvider.CurrentTaskItem.Message,Converter={StaticResource StringEllipsisConverter},ConverterParameter=35}" Foreground="White" VerticalAlignment="Center" Margin="10 0 0 0" TextWrapping="Wrap"></TextBlock> + </StackPanel> + </Border> + </Grid> + </DockPanel> + </Grid> +</mahapps:MetroWindow> diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Console/ConsoleWindow.xaml.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Console/ConsoleWindow.xaml.cs new file mode 100644 index 000000000..de2c728b0 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Console/ConsoleWindow.xaml.cs @@ -0,0 +1,43 @@ +using MahApps.Metro.Controls; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace Tango.MachineStudio.UI.Console +{ + /// <summary> + /// Interaction logic for ConsoleWindow.xaml + /// </summary> + public partial class ConsoleWindow : MetroWindow + { + private ConsoleWindowVM _vm; + + public ConsoleWindow() + { + InitializeComponent(); + + this.Loaded += (_, __) => + { + _vm = this.DataContext as ConsoleWindowVM; + _vm.SetLogTextBox(txtLog); + }; + } + + //Auto scroll to bottom of response log each time it is changed. + private void TextBox_TextChanged(object sender, TextChangedEventArgs e) + { + txtLog.SelectionStart = txtLog.Text.Length; + txtLog.ScrollToEnd(); + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Console/ConsoleWindowVM.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Console/ConsoleWindowVM.cs new file mode 100644 index 000000000..a3a0a734e --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Console/ConsoleWindowVM.cs @@ -0,0 +1,257 @@ +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Controls; +using System.Windows.Threading; +using Tango.Core.Commands; +using Tango.Core.Helpers; +using Tango.MachineStudio.Common.Modules; +using Tango.MachineStudio.Common.Notifications; +using Tango.Scripting; +using Tango.SharedUI; + +namespace Tango.MachineStudio.UI.Console +{ + public class ConsoleWindowVM : ViewModel + { + private IStudioModuleLoader _moduleLoader; + private INotificationProvider _notificatrion; + private TextBox _txtLog; + private String _currentFile; + + + /// <summary> + /// Gets or sets the additional highlight C# types. + /// </summary> + public ObservableCollection<KeyValuePair<String, Type>> HighlightTypes { get; set; } + + internal void SetLogTextBox(TextBox txtLog) + { + _txtLog = txtLog; + } + + /// <summary> + /// Gets or sets the intellisense types. + /// </summary> + public ObservableCollection<KeyValuePair<String, Type>> IntellisenseTypes { get; set; } + + private String _code; + /// <summary> + /// Gets or sets the code. + /// </summary> + public String Code + { + get { return _code; } + set { _code = value; RaisePropertyChangedAuto(); } + } + + private bool _isRunning; + /// <summary> + /// Gets or sets a value indicating whether a stub is currently running. + /// </summary> + public bool IsRunning + { + get { return _isRunning; } + set { _isRunning = value; RaisePropertyChanged(nameof(IsRunning)); InvalidateRelayCommands(); } + } + + /// <summary> + /// Gets or sets the run command. + /// </summary> + public RelayCommand RunCommand { get; set; } + + /// <summary> + /// Gets or sets the stop command. + /// </summary> + public RelayCommand StopCommand { get; set; } + + /// <summary> + /// Gets or sets the clear command. + /// </summary> + public RelayCommand ClearCommand { get; set; } + + /// <summary> + /// Gets or sets the new command. + /// </summary> + public RelayCommand NewCommand { get; set; } + + /// <summary> + /// Gets or sets the open command. + /// </summary> + public RelayCommand OpenCommand { get; set; } + + /// <summary> + /// Gets or sets the save command. + /// </summary> + public RelayCommand SaveCommand { get; set; } + + /// <summary> + /// Gets or sets the save as command. + /// </summary> + public RelayCommand SaveAsCommand { get; set; } + + public ConsoleWindowVM(IStudioModuleLoader moduleLoader, INotificationProvider notification) + { + _moduleLoader = moduleLoader; + _notificatrion = notification; + + RunCommand = new RelayCommand(Run); + StopCommand = new RelayCommand(Stop); + + HighlightTypes = new ObservableCollection<KeyValuePair<string, Type>>(); + IntellisenseTypes = new ObservableCollection<KeyValuePair<string, Type>>(); + + IntellisenseTypes.Add(new KeyValuePair<string, Type>("manager", typeof(ConsoleManager))); + + foreach (var moduleType in moduleLoader.UserModules.SelectMany(x => x.MainViewType.Assembly.GetTypes())) + { + if (!moduleType.FullName.Contains("<") && !moduleType.FullName.Contains(">")) + { + HighlightTypes.Add(new KeyValuePair<string, Type>(moduleType.FullName, moduleType)); + } + } + + foreach (var type in this.GetType().Assembly.GetTypes()) + { + if (!type.FullName.Contains("<") && !type.FullName.Contains(">")) + { + HighlightTypes.Add(new KeyValuePair<string, Type>(type.Name, type)); + } + } + + foreach (var type in typeof(INotificationProvider).Assembly.GetTypes()) + { + if (!type.FullName.Contains("<") && !type.FullName.Contains(">")) + { + HighlightTypes.Add(new KeyValuePair<string, Type>(type.Name, type)); + } + } + + HighlightTypes.Add(new KeyValuePair<string, Type>("Thread", typeof(Thread))); + HighlightTypes.Add(new KeyValuePair<string, Type>("DateTime", typeof(DateTime))); + HighlightTypes.Add(new KeyValuePair<string, Type>("TimeSpan", typeof(TimeSpan))); + HighlightTypes.Add(new KeyValuePair<string, Type>("Dispatcher", typeof(Dispatcher))); + HighlightTypes.Add(new KeyValuePair<string, Type>("Task", typeof(Task))); + HighlightTypes.Add(new KeyValuePair<string, Type>("List", typeof(IList<Object>))); + HighlightTypes.Add(new KeyValuePair<string, Type>("int", typeof(Int32))); + HighlightTypes.Add(new KeyValuePair<string, Type>("double", typeof(Double))); + HighlightTypes.Add(new KeyValuePair<string, Type>("String", typeof(String))); + HighlightTypes.Add(new KeyValuePair<string, Type>("string", typeof(String))); + + foreach (var item in HighlightTypes) + { + IntellisenseTypes.Add(item); + } + + Code = EmbeddedResourceHelper.GetEmbeddedResourceText("Tango.MachineStudio.UI.Console.CodeTemplate.cs"); + + NewCommand = new RelayCommand(CreateNew); + OpenCommand = new RelayCommand(OpenFile); + SaveCommand = new RelayCommand(SaveFile); + SaveAsCommand = new RelayCommand(SaveAsFile); + ClearCommand = new RelayCommand(ClearLog); + } + + private void Stop() + { + + } + + private async void Run() + { + ScriptEngine engine = new ScriptEngine(new ConsoleOnExecuteParameters(new ConsoleManager(WriteLine))); + engine.Stop(); + engine.ReferencedAssemblies.Add(this.GetType()); + engine.ReferencedAssemblies.Add(typeof(INotificationProvider)); + + foreach (var module in _moduleLoader.AllModules) + { + engine.ReferencedAssemblies.Add(module.GetType()); + } + + await engine.Run(Code); + } + + private void WriteLine(String text) + { + InvokeUI(() => + { + _txtLog.AppendText(text); + }); + } + + /// <summary> + /// Clears the log. + /// </summary> + private void ClearLog() + { + _txtLog.Clear(); + } + + /// <summary> + /// Saves the selected script file. + /// </summary> + private void SaveFile() + { + if (_currentFile == null) + { + SaveAsFile(); + } + else + { + File.WriteAllText(_currentFile, Code); + } + } + + /// <summary> + /// Saves the selected script file. + /// </summary> + private void SaveAsFile() + { + SaveFileDialog dlg = new SaveFileDialog(); + dlg.Filter = "C# Script Files|*.cs"; + dlg.DefaultExt = ".cs"; + if (dlg.ShowDialog().Value) + { + File.WriteAllText(dlg.FileName, Code); + _currentFile = dlg.FileName; + } + } + + /// <summary> + /// Opens a script from HD. + /// </summary> + private void OpenFile() + { + OpenFileDialog dlg = new OpenFileDialog(); + dlg.Filter = "C# Script Files|*.cs"; + if (dlg.ShowDialog().Value) + { + OpenFile(dlg.FileName); + } + } + + /// <summary> + /// Opens the file. + /// </summary> + /// <param name="file">The file.</param> + private void OpenFile(String file) + { + Code = File.ReadAllText(file); + _currentFile = file; + } + + private void CreateNew() + { + _txtLog.Clear(); + _currentFile = null; + Code = String.Empty; + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Images/bug-resolved.png b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Images/bug-resolved.png Binary files differnew file mode 100644 index 000000000..e49f62923 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Images/bug-resolved.png diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/StudioApplication/DefaultStudioApplicationManager.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/StudioApplication/DefaultStudioApplicationManager.cs index 0bbcfd313..4c02be2b2 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/StudioApplication/DefaultStudioApplicationManager.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/StudioApplication/DefaultStudioApplicationManager.cs @@ -42,6 +42,22 @@ namespace Tango.MachineStudio.UI.StudioApplication _moduleLoader = moduleLoader; _navigationManager = navigationManager; _openedWindows = new List<Window>(); + + Task.Factory.StartNew(() => + { + while (MainWindow.Instance == null) + { + Thread.Sleep(100); + } + + InvokeUI(() => + { + MainWindow.Instance.ContentRendered += (_, __) => + { + TangoIOC.Default.GetAllInstancesByBase<IStudioViewModel>().ToList().ForEach(x => x.OnApplicationStarted()); + }; + }); + }); } /// <summary> @@ -229,6 +245,11 @@ namespace Tango.MachineStudio.UI.StudioApplication } } + /// <summary> + /// Notify the application manager about an external opened window. + /// When application exists. All registered windows will be closed. + /// </summary> + /// <param name="window">The window.</param> public void RegisterOpenedWindow(Window window) { _openedWindows.Add(window); diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/SystemInformationModel.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/SystemInformationModel.cs index eb9ef8012..5dde73744 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/SystemInformationModel.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/SystemInformationModel.cs @@ -15,5 +15,7 @@ namespace Tango.MachineStudio.UI.TFS public String HostName { get; set; } public Machine Machine { get; set; } public String ConfigurationString { get; set; } + public String LoadedHardwareConfigurationString { get; set; } + public String LoadedProcessParametersString { get; set; } } } diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/SystemInformationTemplate.cshtml b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/SystemInformationTemplate.cshtml index 7b0c4a896..54db28e32 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/SystemInformationTemplate.cshtml +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/SystemInformationTemplate.cshtml @@ -60,11 +60,32 @@ </tbody> </table> - <div style="font-size:20pt;text-decoration:underline;margin-top:10px">Configuration</div> + <div style="font-size:20pt;text-decoration:underline;margin-top:10px">Machine Configuration</div> <div style="white-space:pre;margin-top:10px;font-size:8pt">@vm.ConfigurationString</div> + + <div style="font-size:20pt;text-decoration:underline;margin-top:10px">Hardware Configuration</div> + if (vm.LoadedHardwareConfigurationString != null) + { + <div style="white-space:pre;margin-top:10px;font-size:8pt">@vm.LoadedHardwareConfigurationString</div> + } + else + { + <div style="color:Red;margin-top:10px">NOT SET</div> + } + + <div style="font-size:20pt;text-decoration:underline;margin-top:10px">Process Parameters</div> + + if (vm.LoadedProcessParametersString != null) + { + <div style="white-space:pre;margin-top:10px;font-size:8pt">@vm.LoadedProcessParametersString</div> + } + else + { + <div style="color:Red;margin-top:10px">NOT SET</div> + } } else { - <div style="color:Red;margin-top:10px">Not Connected</div> + <div style="color:Red;margin-top:10px">NOT CONNECTED</div> } </div>
\ No newline at end of file diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/TeamFoundationServiceExtendedClient.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/TeamFoundationServiceExtendedClient.cs index 27b257e61..79572eb71 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/TeamFoundationServiceExtendedClient.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/TeamFoundationServiceExtendedClient.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using System.Windows; using Tango.BL.Entities; @@ -21,6 +23,13 @@ namespace Tango.MachineStudio.UI.TFS { public Project Project { get; private set; } + private ObservableCollection<WorkItem> _resolvedWorkItems; + public ObservableCollection<WorkItem> ResolvedWorkItems + { + get { return _resolvedWorkItems; } + set { _resolvedWorkItems = value; RaisePropertyChangedAuto(); } + } + private bool _isInitialized; public bool IsInitialized { @@ -30,7 +39,20 @@ namespace Tango.MachineStudio.UI.TFS public TeamFoundationServiceExtendedClient(string collectionURL, string userName, string personalToken) : base(collectionURL, userName, personalToken) { + ResolvedWorkItems = new ObservableCollection<WorkItem>(); + TangoIOC.Default.GetInstance<IAuthenticationProvider>().CurrentUserChanged += TeamFoundationServiceExtendedClient_CurrentUserChanged; + } + private void TeamFoundationServiceExtendedClient_CurrentUserChanged(object sender, User user) + { + if (user != null) + { + Task.Factory.StartNew(async () => + { + Thread.Sleep(5000); + await UpdateCurrentUserResolvedWorkItems(); + }); + } } public Task<WorkItem> UploadWorkItem(WorkItem workItem) @@ -38,14 +60,53 @@ namespace Tango.MachineStudio.UI.TFS return UploadWorkItem(Project, workItem); } + public async Task UpdateCurrentUserResolvedWorkItems() + { + try + { + IStudioApplicationManager app = TangoIOC.Default.GetInstance<IStudioApplicationManager>(); + var items = await GetWorkItemsCreatedBy(Project, GetUserTeamMember()); + items.Where(x => x.StepsToReproduce != null).ToList().ForEach(x => x.StepsToReproduce = x.StepsToReproduce.Replace("<div style=\"white-space:pre;\">", "").Replace("</div>", "")); + ResolvedWorkItems = items.Where(x => x.State == State.Resolved && x.ResolvedReason == ResolvedReason.Fixed && x.IsBuildVersionValid && x.FoundInBuildVersion < Version.Parse(app.Version)).ToObservableCollection(); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error getting the current user resolved work items."); + } + } + + public async Task<WorkItem> CloseWorkItem(WorkItem workItem) + { + await SetWorkItemState(Project, workItem, State.Closed); + var updated = await AddWorkItemComment(Project, workItem, GetUserTeamMember(), "Bug has been verified and closed by " + GetUserTeamMember().DisplayName + " (via Tango Software)."); + ResolvedWorkItems.Remove(workItem); + return updated; + } + + public async Task<WorkItem> ReactivateWorkItem(WorkItem workItem) + { + await SetWorkItemState(Project, workItem, State.New); + var updated = await AddWorkItemComment(Project, workItem, GetUserTeamMember(), "Bug has been reactivated by " + GetUserTeamMember().DisplayName + " (via Tango Software)."); + updated = await SetWorkItemAssignment(Project, updated, workItem.ResolvedBy); + ResolvedWorkItems.Remove(workItem); + return updated; + } + public void Initialize() { Task.Factory.StartNew(() => { if (!IsInitialized) { - Project = GetProject("Tango").Result; - IsInitialized = true; + try + { + Project = GetProject("Tango").Result; + IsInitialized = true; + } + catch (Exception ex) + { + LogManager.Log(ex, "Error initializing the Team Foundation Service client."); + } } }); } @@ -60,7 +121,7 @@ namespace Tango.MachineStudio.UI.TFS item.Area = Project.Areas.FirstOrDefault(); item.Iteration = Project.Iterations.FirstOrDefault(); - TeamMember currentUser = Project.Members.SingleOrDefault(x => x.UniqueName.ToLower().Contains(auth.CurrentUser.Email.ToLower())); + TeamMember currentUser = GetUserTeamMember(); item.CreatedBy = currentUser; item.ChangedBy = currentUser; item.AuthorizedAs = currentUser; @@ -82,6 +143,14 @@ namespace Tango.MachineStudio.UI.TFS Name = "Screen Capture.jpg", }); + return item; + } + + public void FinalizeBug(WorkItem item) + { + IAuthenticationProvider auth = TangoIOC.Default.GetInstance<IAuthenticationProvider>(); + IStudioApplicationManager app = TangoIOC.Default.GetInstance<IStudioApplicationManager>(); + FileLogger appFileLogger = LogManager.Default.RegisteredLoggers.FirstOrDefault(x => x.GetType() == typeof(FileLogger)) as FileLogger; FileLogger embeddedFileLogger = MachineOperator.EmbeddedLogManager.RegisteredLoggers.FirstOrDefault(x => x.GetType() == typeof(FileLogger)) as FileLogger; @@ -113,7 +182,7 @@ namespace Tango.MachineStudio.UI.TFS SystemInformationModel sysModel = new SystemInformationModel(); sysModel.ApplicationVersion = app.Version; - sysModel.EmbeddedVersion = "Fake Version"; + sysModel.EmbeddedVersion = "N/A"; sysModel.HostName = Environment.MachineName; sysModel.UserName = auth.CurrentUser.Contact.FullName; @@ -122,17 +191,18 @@ namespace Tango.MachineStudio.UI.TFS Machine machine = app.ConnectedMachine.Machine; sysModel.Machine = machine; + sysModel.EmbeddedVersion = app.ConnectedMachine.DeviceInformation.Version; MachineDesigner.Views.MainView machineView = new MachineDesigner.Views.MainView(); machineView.Width = 1280; machineView.Height = 1100; - machineView.PanelColumnDefinition.Width = new System.Windows.GridLength(0); + machineView.PanelColumnDefinition.Width = new GridLength(0); machineView.stackHeader.Visibility = Visibility.Collapsed; machineView.Background = System.Windows.Media.Brushes.White; machineView.DataContext = new MachineDesigner.ViewModels.MainViewVM() { SelectedMachine = machine, Configuration = machine.Configuration }; String configImageFile = PathHelper.GetTempFilePath(); - machineView.RenderToFile(configImageFile, System.Drawing.Imaging.ImageFormat.Jpeg, new System.Windows.Size(machineView.Width, machineView.Height), 100); + machineView.RenderToFile(configImageFile, System.Drawing.Imaging.ImageFormat.Jpeg, new Size(machineView.Width, machineView.Height), 100); item.Attachments.Add(new Attachment() { @@ -141,7 +211,17 @@ namespace Tango.MachineStudio.UI.TFS Name = "Machine Configuration.jpg" }); - sysModel.ConfigurationString = machine.Configuration.CloneConfiguration().ToJsonString(); + sysModel.ConfigurationString = machine.Configuration.CloneConfiguration().ToJsonString(nameof(Configuration.MachinesConfigurations), nameof(Configuration.MachineVersions)); + + if (app.ConnectedMachine.CurrentProcessParameters != null) + { + sysModel.LoadedProcessParametersString = app.ConnectedMachine.CurrentProcessParameters.ToJsonString(nameof(ProcessParametersTable.ProcessParametersTablesGroup)); + } + + if (app.ConnectedMachine.CurrentHardwareConfiguration != null) + { + sysModel.LoadedHardwareConfigurationString = app.ConnectedMachine.CurrentHardwareConfiguration.ToJsonString(); + } } String html = String.Empty; @@ -154,7 +234,14 @@ namespace Tango.MachineStudio.UI.TFS item.SystemInformation = CodeGeneration.Helper.Parse(html, sysModel); - return item; + item.StepsToReproduce = String.Format("<div style=\"white-space:pre\">{0}</div>", item.StepsToReproduce); + } + + private TeamMember GetUserTeamMember() + { + IAuthenticationProvider auth = TangoIOC.Default.GetInstance<IAuthenticationProvider>(); + TeamMember currentUser = Project.Members.SingleOrDefault(x => x.UniqueName.ToLower().Contains(auth.CurrentUser.Email.ToLower())); + return currentUser; } } } diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/WorkItemValidationAttribute.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/WorkItemValidationAttribute.cs index 1418d0576..3e85f91b4 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/WorkItemValidationAttribute.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/TFS/WorkItemValidationAttribute.cs @@ -16,13 +16,19 @@ namespace Tango.MachineStudio.UI.TFS if (String.IsNullOrWhiteSpace(item.Title)) { - ErrorMessage = "Title is empty"; + ErrorMessage = "Title is required."; return false; } if (item.AssignedTo == null) { - ErrorMessage = "Assigned To is empty"; + ErrorMessage = "Assigned To field is required."; + return false; + } + + if (String.IsNullOrWhiteSpace(item.StepsToReproduce)) + { + ErrorMessage = "Please enter steps to reproduce."; return false; } diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Tango.MachineStudio.UI.csproj b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Tango.MachineStudio.UI.csproj index 8ab6bb188..5d685c112 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Tango.MachineStudio.UI.csproj +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Tango.MachineStudio.UI.csproj @@ -131,6 +131,13 @@ <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> </ApplicationDefinition> + <EmbeddedResource Include="Console\CodeTemplate.cs" /> + <Compile Include="Console\ConsoleManager.cs" /> + <Compile Include="Console\ConsoleOnExecuteParameters.cs" /> + <Compile Include="Console\ConsoleWindow.xaml.cs"> + <DependentUpon>ConsoleWindow.xaml</DependentUpon> + </Compile> + <Compile Include="Console\ConsoleWindowVM.cs" /> <Compile Include="Html\DefaultHtmlPresenter.cs" /> <Compile Include="Html\HtmlWindow.xaml.cs"> <DependentUpon>HtmlWindow.xaml</DependentUpon> @@ -161,6 +168,7 @@ <Compile Include="ViewModelLocator.cs" /> <Compile Include="ViewModels\ModuleWindowVM.cs" /> <Compile Include="ViewModels\ReportIssueViewVM.cs" /> + <Compile Include="ViewModels\ResolvedIssuesViewVM.cs" /> <Compile Include="ViewModels\ShutdownViewVM.cs" /> <Compile Include="ViewModels\UpdateViewVM.cs" /> <Compile Include="Views\ConnectedMachineView.xaml.cs"> @@ -190,6 +198,9 @@ <Compile Include="Views\ReportIssueView.xaml.cs"> <DependentUpon>ReportIssueView.xaml</DependentUpon> </Compile> + <Compile Include="Views\ResolvedIssuesView.xaml.cs"> + <DependentUpon>ResolvedIssuesView.xaml</DependentUpon> + </Compile> <Compile Include="Views\ShutdownView.xaml.cs"> <DependentUpon>ShutdownView.xaml</DependentUpon> </Compile> @@ -203,6 +214,10 @@ <Compile Include="Windows\ModuleWindow.xaml.cs"> <DependentUpon>ModuleWindow.xaml</DependentUpon> </Compile> + <Page Include="Console\ConsoleWindow.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> <Page Include="Html\HtmlWindow.xaml"> <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> @@ -270,6 +285,10 @@ <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> + <Page Include="Views\ResolvedIssuesView.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> <Page Include="Views\ShutdownView.xaml"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> @@ -355,6 +374,10 @@ <Project>{e4927038-348d-4295-aaf4-861c58cb3943}</Project> <Name>Tango.PMR</Name> </ProjectReference> + <ProjectReference Include="..\..\Tango.Scripting\Tango.Scripting.csproj"> + <Project>{401989E7-AE1E-4002-B0EE-9A9F63740B97}</Project> + <Name>Tango.Scripting</Name> + </ProjectReference> <ProjectReference Include="..\..\Tango.Settings\Tango.Settings.csproj"> <Project>{d8f1ad85-526a-4f50-b6dc-d437af63d8d8}</Project> <Name>Tango.Settings</Name> @@ -460,6 +483,7 @@ </ItemGroup> <ItemGroup> <Content Include="Html\HTML Templates\Thread Break.html" /> + <Resource Include="Images\bug-resolved.png" /> <EmbeddedResource Include="TFS\SystemInformationTemplate.cshtml" /> <Resource Include="Images\bug.png" /> </ItemGroup> diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModelLocator.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModelLocator.cs index 8e6f11452..931130a05 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModelLocator.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModelLocator.cs @@ -13,6 +13,7 @@ using Tango.MachineStudio.Common.Speech; using Tango.MachineStudio.Common.StudioApplication; using Tango.MachineStudio.Common.Video; using Tango.MachineStudio.UI.Authentication; +using Tango.MachineStudio.UI.Console; using Tango.MachineStudio.UI.Html; using Tango.MachineStudio.UI.Modules; using Tango.MachineStudio.UI.Navigation; @@ -64,7 +65,6 @@ namespace Tango.MachineStudio.UI TangoIOC.Default.Unregister<IHtmlPresenter>(); TangoIOC.Default.Unregister<ITeamFoundationServiceClient>(); - TangoIOC.Default.Register<TeamFoundationServiceExtendedClient>(new TeamFoundationServiceExtendedClient("https://twinetfs.visualstudio.com/DefaultCollection", "Roy", "szzfokrceo4rhd4eqi5qpmxn3pa5iwl3q7tlqd36l2m7smz2ynoa")); TangoIOC.Default.Register<INotificationProvider, DefaultNotificationProvider>(); TangoIOC.Default.Register<IAuthenticationProvider, DefaultAuthenticationProvider>(); TangoIOC.Default.Register<INavigationManager, DefaultNavigationManager>(); @@ -76,6 +76,7 @@ namespace Tango.MachineStudio.UI TangoIOC.Default.Register<IEventLogger, DefaultEventLogger>(); TangoIOC.Default.Register<ISpeechProvider, DefaultSpeechProvider>(); TangoIOC.Default.Register<IHtmlPresenter, DefaultHtmlPresenter>(); + TangoIOC.Default.Register<TeamFoundationServiceExtendedClient>(new TeamFoundationServiceExtendedClient("https://twinetfs.visualstudio.com", String.Empty, "szzfokrceo4rhd4eqi5qpmxn3pa5iwl3q7tlqd36l2m7smz2ynoa")); TangoIOC.Default.Register<MainViewVM>(); TangoIOC.Default.Register<LoadingViewVM>(); @@ -85,6 +86,7 @@ namespace Tango.MachineStudio.UI TangoIOC.Default.Register<ConnectedMachineViewVM>(); TangoIOC.Default.Register<MachineLoginViewVM>(); TangoIOC.Default.Register<UpdateViewVM>(); + TangoIOC.Default.Register<ConsoleWindowVM>(); //Register View (Supervising Controller Pattern). //if (!ViewModelBase.IsInDesignModeStatic) @@ -157,5 +159,13 @@ namespace Tango.MachineStudio.UI return TangoIOC.Default.GetInstance<UpdateViewVM>(); } } + + public ConsoleWindowVM ConsoleWindowVM + { + get + { + return TangoIOC.Default.GetInstance<ConsoleWindowVM>(); + } + } } }
\ No newline at end of file diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/LoadingViewVM.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/LoadingViewVM.cs index 2b2b442f7..29f9102ac 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/LoadingViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/LoadingViewVM.cs @@ -16,6 +16,7 @@ using Tango.BL; using Tango.MachineStudio.Common.EventLogging; using Tango.BL.Enumerations; using Tango.MachineStudio.UI.TFS; +using Tango.MachineStudio.Common; namespace Tango.MachineStudio.UI.ViewModels { @@ -23,7 +24,7 @@ namespace Tango.MachineStudio.UI.ViewModels /// Represents the Machine Studio loading view, view model. /// </summary> /// <seealso cref="Tango.SharedUI.ViewModel" /> - public class LoadingViewVM : ViewModel + public class LoadingViewVM : StudioViewModelInternal { private INotificationProvider _notificationProvider; private TeamFoundationServiceExtendedClient _tfs; @@ -55,6 +56,11 @@ namespace Tango.MachineStudio.UI.ViewModels _navigationManager = navigationManager; _studioModuleLoader = studioModuleLoader; _notificationProvider = notificationProvider; + } + + public override void OnApplicationStarted() + { + base.OnApplicationStarted(); Load(); } diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/MainViewVM.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/MainViewVM.cs index 0882267e8..04b973f23 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/MainViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/MainViewVM.cs @@ -148,6 +148,16 @@ namespace Tango.MachineStudio.UI.ViewModels /// </summary> public RelayCommand ReportIssueCommand { get; set; } + /// <summary> + /// Gets or sets the open resolved bugs. + /// </summary> + public RelayCommand OpenResolvedBugsCommand { get; set; } + + /// <summary> + /// Gets or sets the open developer console command. + /// </summary> + public RelayCommand OpenDeveloperConsoleCommand { get; set; } + private IAuthenticationProvider _authenticationProvider; /// <summary> /// Gets or sets the authentication provider. @@ -302,6 +312,8 @@ namespace Tango.MachineStudio.UI.ViewModels ResolveMachineEventCommand = new RelayCommand<MachinesEvent>(ResolveMachineEvent); ReportIssueCommand = new RelayCommand(ReportIssue); + OpenResolvedBugsCommand = new RelayCommand(OpenResolvedBugs); + OpenDeveloperConsoleCommand = new RelayCommand(OpenDeveloperConsole); } private void MachineEventsStateProvider_EventsResolved(object sender, IEnumerable<MachinesEvent> e) @@ -321,7 +333,7 @@ namespace Tango.MachineStudio.UI.ViewModels { while (!DisableCheckForUpdates) { - Thread.Sleep(TimeSpan.FromMinutes(1)); + Thread.Sleep(TimeSpan.FromMinutes(0.2)); try { @@ -649,10 +661,78 @@ namespace Tango.MachineStudio.UI.ViewModels { using (_notificationProvider.PushTaskItem("Uploading bug report...")) { - await TFSClient.UploadWorkItem(vm.WorkItem); + try + { + TFSClient.FinalizeBug(vm.WorkItem); + await TFSClient.UploadWorkItem(vm.WorkItem); + } + catch (Exception ex) + { + _notificationProvider.ShowError("An error occurred while trying to create the issue." + Environment.NewLine + ex.Message); + } } }, null); } + + private void OpenResolvedBugs() + { + ResolvedIssuesViewVM vm = null; + + vm = new ResolvedIssuesViewVM(TFSClient, async (item) => + { + //Approve + using (_notificationProvider.PushTaskItem("Approving issue...")) + { + vm.IsAvailable = false; + try + { + await TFSClient.CloseWorkItem(item); + } + catch (Exception ex) + { + _notificationProvider.ShowError("An error occurred while trying to update the issue." + Environment.NewLine + ex.Message); + } + vm.IsAvailable = true; + + if (TFSClient.ResolvedWorkItems.Count == 0) + { + vm.Close(); + } + } + }, async (item) => + { + //Decline + using (_notificationProvider.PushTaskItem("Reactivating issue...")) + { + vm.IsAvailable = false; + try + { + await TFSClient.ReactivateWorkItem(item); + } + catch (Exception ex) + { + _notificationProvider.ShowError("An error occurred while trying to update the issue." + Environment.NewLine + ex.Message); + } + + vm.IsAvailable = true; + + if (TFSClient.ResolvedWorkItems.Count == 0) + { + vm.Close(); + } + } + }); + + _notificationProvider.ShowModalDialog<ResolvedIssuesViewVM, ResolvedIssuesView>(vm, (_) => { }, null); + } + + private void OpenDeveloperConsole() + { + Console.ConsoleWindow console = new Console.ConsoleWindow(); + ApplicationManager.RegisterOpenedWindow(console); + console.Owner = MainWindow.Instance; + console.Show(); + } } } diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/ResolvedIssuesViewVM.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/ResolvedIssuesViewVM.cs new file mode 100644 index 000000000..552880792 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/ViewModels/ResolvedIssuesViewVM.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.Core.Commands; +using Tango.MachineStudio.Common.Notifications; +using Tango.MachineStudio.UI.TFS; +using Tango.TFS; + +namespace Tango.MachineStudio.UI.ViewModels +{ + public class ResolvedIssuesViewVM : DialogViewVM + { + private Action<WorkItem> _onApprove; + private Action<WorkItem> _onDecline; + + public TeamFoundationServiceExtendedClient TFSClient { get; set; } + + public RelayCommand<WorkItem> ApproveCommand { get; set; } + + public RelayCommand<WorkItem> DeclineCommand { get; set; } + + private bool _isAvailable; + + public bool IsAvailable + { + get { return _isAvailable; } + set { _isAvailable = value; RaisePropertyChangedAuto(); } + } + + public ResolvedIssuesViewVM() : base() + { + IsAvailable = true; + ApproveCommand = new RelayCommand<WorkItem>(ApproveIssue); + DeclineCommand = new RelayCommand<WorkItem>(DeclineIssue); + } + + public ResolvedIssuesViewVM(TeamFoundationServiceExtendedClient tfsClient, Action<WorkItem> onApprove, Action<WorkItem> onDecline) : this() + { + TFSClient = tfsClient; + _onApprove = onApprove; + _onDecline = onDecline; + } + + private void DeclineIssue(WorkItem workItem) + { + _onDecline(workItem); + } + + private void ApproveIssue(WorkItem workItem) + { + _onApprove(workItem); + } + + public void Close() + { + Accept(); + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/MainView.xaml b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/MainView.xaml index 86723479b..8417c70c8 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/MainView.xaml +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/MainView.xaml @@ -10,9 +10,11 @@ xmlns:controls="clr-namespace:Tango.SharedUI.Controls;assembly=Tango.SharedUI" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:entities="clr-namespace:Tango.BL.Entities;assembly=Tango.BL" + xmlns:enumerations="clr-namespace:Tango.BL.Enumerations;assembly=Tango.BL" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:db="clr-namespace:Tango.MachineStudio.DB.Views;assembly=Tango.MachineStudio.DB" xmlns:local="clr-namespace:Tango.MachineStudio.UI.Views" + xmlns:commonConverters="clr-namespace:Tango.MachineStudio.Common.Converters;assembly=Tango.MachineStudio.Common" mc:Ignorable="d" d:DesignHeight="720" d:DesignWidth="1270" Background="Transparent" DataContext="{Binding MainViewVM, Source={StaticResource Locator}}"> @@ -22,6 +24,7 @@ <converters:NullObjectToBooleanConverter x:Key="NullObjectToBooleanConverter" /> <converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" /> <converters:StringToWordsConverter x:Key="StringToWordsConverter" /> + <commonConverters:PermissionToVisibilityConverter x:Key="PermissionToVisibilityConverter" /> </UserControl.Resources> <Grid> @@ -189,7 +192,16 @@ <TextBlock Margin="5 0 0 0" VerticalAlignment="Center">Speech</TextBlock> </StackPanel> </Button> - <Separator/> + <StackPanel Visibility="{Binding AuthenticationProvider.CurrentUser,Converter={StaticResource PermissionToVisibilityConverter},ConverterParameter={x:Static enumerations:Permissions.RunDeveloperConsole}}"> + <Separator/> + <Button Command="{Binding OpenDeveloperConsoleCommand}"> + <StackPanel Orientation="Horizontal"> + <materialDesign:PackIcon Kind="Console" Width="24" Height="24" /> + <TextBlock Margin="5 0 0 0" VerticalAlignment="Center">Developer Console</TextBlock> + </StackPanel> + </Button> + <Separator/> + </StackPanel> <Button Command="{Binding ExitCommand}"> <StackPanel Orientation="Horizontal"> <materialDesign:PackIcon Kind="CloseCircleOutline" Width="24" Height="24" /> @@ -320,7 +332,7 @@ <Button Cursor="Hand" Command="{Binding ReportIssueCommand}" IsEnabled="{Binding TFSClient.IsInitialized}"> <Button.Style> <Style TargetType="Button" BasedOn="{StaticResource emptyButton}"> - <Setter Property="ToolTip" Value="Report and issue"></Setter> + <Setter Property="ToolTip" Value="Report issue"></Setter> <Setter Property="Opacity" Value="1"></Setter> <Style.Triggers> <Trigger Property="IsEnabled" Value="False"> @@ -332,6 +344,38 @@ </Button.Style> <Image Margin="10 0 0 0" Source="/Images/bug.png" Width="24" RenderOptions.BitmapScalingMode="Fant" /> </Button> + <Button Cursor="Hand" Command="{Binding OpenResolvedBugsCommand}"> + <Button.Style> + <Style TargetType="Button" BasedOn="{StaticResource emptyButton}"> + <Setter Property="Visibility" Value="Visible"></Setter> + <Style.Triggers> + <EventTrigger RoutedEvent="Loaded"> + <EventTrigger.Actions> + <BeginStoryboard> + <Storyboard> + <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Duration="00:00:01" RepeatBehavior="Forever"> + <DiscreteDoubleKeyFrame KeyTime="00:00:00" Value="1" /> + <DiscreteDoubleKeyFrame KeyTime="00:00:0.5" Value="0" /> + </DoubleAnimationUsingKeyFrames> + </Storyboard> + </BeginStoryboard> + </EventTrigger.Actions> + </EventTrigger> + <DataTrigger Binding="{Binding TFSClient.ResolvedWorkItems.Count}" Value="0"> + <Setter Property="Visibility" Value="Collapsed"></Setter> + </DataTrigger> + </Style.Triggers> + </Style> + </Button.Style> + <Button.ToolTip> + <TextBlock> + <Run>There are</Run> + <Run Text="{Binding TFSClient.ResolvedWorkItems.Count,Mode=OneWay}"></Run> + <Run>issues waiting for your approval</Run> + </TextBlock> + </Button.ToolTip> + <Image Margin="10 0 0 0" Source="/Images/bug-resolved.png" Width="24" RenderOptions.BitmapScalingMode="Fant" /> + </Button> </StackPanel> </Grid> </Grid> diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ReportIssueView.xaml b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ReportIssueView.xaml index f2e3038b9..37cd0b793 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ReportIssueView.xaml +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ReportIssueView.xaml @@ -12,7 +12,7 @@ xmlns:tfs="clr-namespace:Tango.TFS;assembly=Tango.TFS" xmlns:tfss="clr-namespace:Tango.MachineStudio.UI.TFS" xmlns:local="clr-namespace:Tango.MachineStudio.UI.Views" - mc:Ignorable="d" Width="530" Height="650" Background="White" d:DataContext="{d:DesignInstance Type=vm:ReportIssueViewVM, IsDesignTimeCreatable=False}"> + mc:Ignorable="d" Width="530" Height="680" Background="White" d:DataContext="{d:DesignInstance Type=vm:ReportIssueViewVM, IsDesignTimeCreatable=False}"> <UserControl.Resources> <converters:EnumToItemsSourceConverter x:Key="EnumToItemsSourceConverter" /> @@ -36,53 +36,85 @@ </Grid> <Grid Grid.Row="1"> - <StackPanel> - <TextBox materialDesign:HintAssist.Hint="Title" materialDesign:HintAssist.IsFloating="True" FontSize="16" Text="{Binding WorkItem.Title}"></TextBox> + <DockPanel> + <StackPanel DockPanel.Dock="Top"> + <DockPanel> + <materialDesign:PackIcon Kind="Pencil" Width="24" Height="24" VerticalAlignment="Center" Margin="0 0 10 0" /> + <TextBox materialDesign:HintAssist.Hint="Title" materialDesign:HintAssist.IsFloating="True" FontSize="16" Text="{Binding WorkItem.Title}"></TextBox> + </DockPanel> - <autoComplete:AutoCompleteTextBox Provider="{StaticResource TeamMembersProvider}" SelectedItem="{Binding WorkItem.AssignedTo,Mode=TwoWay}" DisplayMember="DisplayName" Margin="0 10 0 0" materialDesign:HintAssist.Hint="Assigned To" materialDesign:HintAssist.IsFloating="True"></autoComplete:AutoCompleteTextBox> - <ComboBox Margin="0 10 0 0" materialDesign:HintAssist.Hint="Area" materialDesign:HintAssist.IsFloating="True" ItemsSource="{Binding Project.Areas}" SelectedItem="{Binding WorkItem.Area}" DisplayMemberPath="Name"></ComboBox> - <ComboBox Margin="0 10 0 0" materialDesign:HintAssist.Hint="Severity" materialDesign:HintAssist.IsFloating="True" ItemsSource="{Binding Source={x:Type tfs:Severity},Converter={StaticResource EnumToItemsSourceConverter}}" SelectedValue="{Binding WorkItem.Severity}" SelectedValuePath="Value" DisplayMemberPath="DisplayName"></ComboBox> + <DockPanel Margin="0 10 0 0"> + <materialDesign:PackIcon Kind="Account" Width="24" Height="24" VerticalAlignment="Center" Margin="0 0 10 0" /> + <autoComplete:AutoCompleteTextBox Provider="{StaticResource TeamMembersProvider}" SelectedItem="{Binding WorkItem.AssignedTo,Mode=TwoWay}" DisplayMember="DisplayName" materialDesign:HintAssist.Hint="Assigned To" materialDesign:HintAssist.IsFloating="True"></autoComplete:AutoCompleteTextBox> + </DockPanel> - <TextBlock Margin="0 10 0 0">Tags</TextBlock> - <ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" Height="120"> - <ItemsControl ItemsSource="{Binding SelectedTags}"> - <ItemsControl.ItemsPanel> - <ItemsPanelTemplate> - <WrapPanel IsItemsHost="True" Orientation="Horizontal" /> - </ItemsPanelTemplate> - </ItemsControl.ItemsPanel> - <ItemsControl.ItemTemplate> - <DataTemplate> - <ToggleButton Style="{StaticResource emptyToggleButton}" IsChecked="{Binding IsSelected}" Cursor="Hand"> - <Border CornerRadius="5" Margin="0 5 5 5" Padding="5" BorderThickness="1" BorderBrush="DimGray"> - <Border.Style> - <Style TargetType="Border"> - <Setter Property="Background" Value="Silver"></Setter> - <Style.Triggers> - <DataTrigger Binding="{Binding IsSelected}" Value="True"> - <Setter Property="Background" Value="{StaticResource AccentColorBrush}"></Setter> - <Setter Property="TextElement.Foreground" Value="White"></Setter> - </DataTrigger> - </Style.Triggers> - </Style> - </Border.Style> - <TextBlock Text="{Binding Data.Name}" FontSize="11"></TextBlock> - </Border> - </ToggleButton> - </DataTemplate> - </ItemsControl.ItemTemplate> - </ItemsControl> - </ScrollViewer> - </StackPanel> + <DockPanel Margin="0 10 0 0"> + <materialDesign:PackIcon Kind="Star" Width="24" Height="24" VerticalAlignment="Center" Margin="0 0 10 0" /> + <ComboBox materialDesign:HintAssist.Hint="Area" materialDesign:HintAssist.IsFloating="True" ItemsSource="{Binding Project.Areas}" SelectedItem="{Binding WorkItem.Area}" DisplayMemberPath="Name"></ComboBox> + </DockPanel> + + <DockPanel Margin="0 10 0 0"> + <materialDesign:PackIcon Kind="CommentAlertOutline" Width="24" Height="24" VerticalAlignment="Center" Margin="0 0 10 0" /> + <ComboBox materialDesign:HintAssist.Hint="Severity" materialDesign:HintAssist.IsFloating="True" ItemsSource="{Binding Source={x:Type tfs:Severity},Converter={StaticResource EnumToItemsSourceConverter}}" SelectedValue="{Binding WorkItem.Severity}" SelectedValuePath="Value" DisplayMemberPath="DisplayName"></ComboBox> + </DockPanel> + + <DockPanel Margin="0 20 0 0"> + <materialDesign:PackIcon Kind="Tag" Width="24" Height="24" VerticalAlignment="Center" Margin="0 0 10 0" /> + <TextBlock VerticalAlignment="Center"><Run>Tags</Run> <Run FontSize="10" Foreground="Gray">(highlight selected tags)</Run></TextBlock> + </DockPanel> + <ScrollViewer Margin="30 0 0 0" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" Height="120"> + <ItemsControl ItemsSource="{Binding SelectedTags}"> + <ItemsControl.ItemsPanel> + <ItemsPanelTemplate> + <WrapPanel IsItemsHost="True" Orientation="Horizontal" /> + </ItemsPanelTemplate> + </ItemsControl.ItemsPanel> + <ItemsControl.ItemTemplate> + <DataTemplate> + <ToggleButton Style="{StaticResource emptyToggleButton}" IsChecked="{Binding IsSelected}" Cursor="Hand"> + <Border CornerRadius="5" Margin="0 5 5 5" Padding="5" BorderThickness="1" BorderBrush="DimGray"> + <Border.Style> + <Style TargetType="Border"> + <Setter Property="Background" Value="Silver"></Setter> + <Style.Triggers> + <DataTrigger Binding="{Binding IsSelected}" Value="True"> + <Setter Property="Background" Value="{StaticResource AccentColorBrush}"></Setter> + <Setter Property="TextElement.Foreground" Value="White"></Setter> + </DataTrigger> + </Style.Triggers> + </Style> + </Border.Style> + <TextBlock Text="{Binding Data.Name}" FontSize="11"></TextBlock> + </Border> + </ToggleButton> + </DataTemplate> + </ItemsControl.ItemTemplate> + </ItemsControl> + </ScrollViewer> + </StackPanel> + + <Grid> + <DockPanel> + <DockPanel Margin="0 10 0 0" DockPanel.Dock="Top"> + <materialDesign:PackIcon Kind="ChartTimeline" Width="24" Height="24" VerticalAlignment="Center" Margin="0 0 10 0" /> + <TextBlock VerticalAlignment="Center">Steps To Reproduce</TextBlock> + </DockPanel> + + <Grid Margin="30 0 0 0"> + <TextBox Margin="0 5 0 0" Style="{x:Null}" BorderBrush="Silver" AcceptsReturn="True" TextWrapping="Wrap" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" Text="{Binding WorkItem.StepsToReproduce}"></TextBox> + </Grid> + </DockPanel> + </Grid> + </DockPanel> </Grid> <Grid Grid.Row="2"> - <Button HorizontalAlignment="Right" Width="140" Command="{Binding OKCommand}">SUBMIT</Button> + <Button HorizontalAlignment="Right" Height="40" Width="140" Command="{Binding OKCommand}" Background="#FF6262" BorderBrush="#FF6262" IsDefault="True">SUBMIT</Button> <ItemsControl ItemsSource="{Binding ValidationErrors}" HorizontalAlignment="Left" Margin="5" VerticalAlignment="Center" Visibility="Visible"> <ItemsControl.ItemTemplate> <DataTemplate> - <TextBlock Foreground="#FF4C4C" Margin="0 2 0 0"><Run>*</Run> <Run Text="{Binding}"></Run></TextBlock> + <TextBlock Foreground="#FF4C4C" Margin="0 2 0 0"><Run>*</Run> <Run Text="{Binding Path=*,Mode=OneWay}"></Run></TextBlock> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ResolvedIssuesView.xaml b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ResolvedIssuesView.xaml new file mode 100644 index 000000000..4fb457f87 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ResolvedIssuesView.xaml @@ -0,0 +1,89 @@ +<UserControl x:Class="Tango.MachineStudio.UI.Views.ResolvedIssuesView" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:mahapps="http://metro.mahapps.com/winfx/xaml/controls" + xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:autoComplete="clr-namespace:Tango.AutoComplete.Editors;assembly=Tango.AutoComplete" + xmlns:integration="clr-namespace:Tango.Integration.Services;assembly=Tango.Integration" + xmlns:converters="clr-namespace:Tango.SharedUI.Converters;assembly=Tango.SharedUI" + xmlns:vm="clr-namespace:Tango.MachineStudio.UI.ViewModels" + xmlns:tfs="clr-namespace:Tango.TFS;assembly=Tango.TFS" + xmlns:tfss="clr-namespace:Tango.MachineStudio.UI.TFS" + xmlns:local="clr-namespace:Tango.MachineStudio.UI.Views" + mc:Ignorable="d" Width="530" Height="580" Background="White" d:DataContext="{d:DesignInstance Type=vm:ResolvedIssuesViewVM, IsDesignTimeCreatable=False}"> + <Grid> + <Grid IsEnabled="{Binding IsAvailable}"> + <Grid.RowDefinitions> + <RowDefinition Height="130"/> + <RowDefinition Height="1*"/> + <RowDefinition Height="200"/> + </Grid.RowDefinitions> + + <Grid> + <StackPanel Margin="10"> + <StackPanel Orientation="Horizontal"> + <Image Source="/Images/bug-resolved.png" Width="48" RenderOptions.BitmapScalingMode="Fant"></Image> + <TextBlock VerticalAlignment="Center" Margin="10 0 0 0" FontSize="20">Resolved Issues</TextBlock> + </StackPanel> + + <TextBlock HorizontalAlignment="Left" Margin="10" TextWrapping="Wrap"> + Below you can find issues reported by you that have been flagged as resolved by the development team. + Please verify each one and report back by pressing 'FIXED' or 'NOT FIXED'. + </TextBlock> + </StackPanel> + </Grid> + + <Grid Grid.Row="1"> + <ListBox x:Name="list" ItemsSource="{Binding TFSClient.ResolvedWorkItems}" HorizontalContentAlignment="Stretch"> + <ListBox.ItemTemplate> + <DataTemplate> + <Border BorderBrush="#E6E6E6" BorderThickness="0 0 0 1" Padding="5"> + <DockPanel> + <StackPanel Orientation="Horizontal"> + <Image Source="/Images/bug.png" Width="24" RenderOptions.BitmapScalingMode="Fant"></Image> + <StackPanel Margin="5 0 0 0"> + <TextBlock Text="{Binding Title}" FontWeight="SemiBold" TextTrimming="CharacterEllipsis"></TextBlock> + <TextBlock Text="{Binding Comment,TargetNullValue='Comment',FallbackValue='Comment'}" Foreground="Gray" FontSize="10"></TextBlock> + </StackPanel> + </StackPanel> + + <StackPanel HorizontalAlignment="Right" Orientation="Horizontal"> + <Button Background="#FF8282" BorderBrush="#FF8282" Padding="0" Width="90" Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.DeclineCommand}" CommandParameter="{Binding}"> + <StackPanel Orientation="Horizontal"> + <materialDesign:PackIcon VerticalAlignment="Center" Kind="Close" /> + <TextBlock Margin="10 0 0 0" FontSize="11" VerticalAlignment="Center">NOT FIXED</TextBlock> + </StackPanel> + </Button> + <Button Background="#91D66D" BorderBrush="#91D66D" Padding="0" Width="90" Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.ApproveCommand}" CommandParameter="{Binding}"> + <StackPanel Orientation="Horizontal"> + <materialDesign:PackIcon VerticalAlignment="Center" Kind="Check" /> + <TextBlock Margin="10 0 0 0" FontSize="11" VerticalAlignment="Center">FIXED</TextBlock> + </StackPanel> + </Button> + </StackPanel> + </DockPanel> + </Border> + </DataTemplate> + </ListBox.ItemTemplate> + </ListBox> + </Grid> + + <Grid Grid.Row="2"> + <Border BorderBrush="#FFA5A5" BorderThickness="0 1 0 0" Margin="5"> + <DockPanel> + <DockPanel Margin="0 10 0 0" DockPanel.Dock="Top"> + <materialDesign:PackIcon Kind="ChartTimeline" Width="24" Height="24" VerticalAlignment="Center" Margin="0 0 10 0" /> + <TextBlock VerticalAlignment="Center" FontSize="16">Steps To Reproduce</TextBlock> + </DockPanel> + + <Grid> + <TextBox Margin="5 10 10 10" Style="{x:Null}" BorderThickness="0" AcceptsReturn="True" TextWrapping="Wrap" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" Text="{Binding ElementName=list,Path=SelectedItem.StepsToReproduce}" IsReadOnly="True"></TextBox> + </Grid> + </DockPanel> + </Border> + </Grid> + </Grid> + </Grid> +</UserControl> diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ResolvedIssuesView.xaml.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ResolvedIssuesView.xaml.cs new file mode 100644 index 000000000..7825bf587 --- /dev/null +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Views/ResolvedIssuesView.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Tango.MachineStudio.UI.Views +{ + /// <summary> + /// Interaction logic for ResolvedBugsView.xaml + /// </summary> + public partial class ResolvedIssuesView : UserControl + { + public ResolvedIssuesView() + { + InitializeComponent(); + } + } +} diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Windows/ExceptionResolutions.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Windows/ExceptionResolutions.cs index 2e7327559..e27a84c3b 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Windows/ExceptionResolutions.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Windows/ExceptionResolutions.cs @@ -10,6 +10,7 @@ namespace Tango.MachineStudio.UI.Windows { Shutdown, Restart, - Ignore + Ignore, + Report } } diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Windows/ExceptionWindow.xaml b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Windows/ExceptionWindow.xaml index 892e4944f..147b40892 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Windows/ExceptionWindow.xaml +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Windows/ExceptionWindow.xaml @@ -5,7 +5,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Tango.MachineStudio.UI.Windows" mc:Ignorable="d" - WindowStyle="None" ResizeMode="NoResize" Topmost="True" AllowsTransparency="True" WindowStartupLocation="CenterScreen" d:DesignHeight="300" d:DesignWidth="300" Width="610" Height="410" Background="Transparent" d:DataContext="{d:DesignInstance Type=local:ExceptionWindow, IsDesignTimeCreatable=False}"> + WindowStyle="None" ResizeMode="NoResize" Topmost="True" AllowsTransparency="True" WindowStartupLocation="CenterScreen" Width="800" Height="600" Background="Transparent" d:DataContext="{d:DesignInstance Type=local:ExceptionWindow, IsDesignTimeCreatable=False}"> <Grid> <Border BorderThickness="1" BorderBrush="DodgerBlue" Background="White" Margin="10" CornerRadius="10"> <Border.Effect> @@ -48,8 +48,16 @@ </Grid> <Grid Grid.Row="1"> - <Button HorizontalAlignment="Left" Width="140" Command="{Binding ResolveCommand}" CommandParameter="{x:Static local:ExceptionResolutions.Ignore}">Ignore</Button> + <StackPanel HorizontalAlignment="Left" Orientation="Horizontal"> + <Button Width="140" Command="{Binding ResolveCommand}" CommandParameter="{x:Static local:ExceptionResolutions.Ignore}">Ignore</Button> + </StackPanel> <StackPanel Orientation="Horizontal" HorizontalAlignment="Right"> + <Button x:Name="btnReport" Style="{StaticResource MaterialDesignFlatButton}" Width="150" Command="{Binding ResolveCommand}" CommandParameter="{x:Static local:ExceptionResolutions.Report}"> + <StackPanel Orientation="Horizontal"> + <Image Source="/Images/bug.png" RenderOptions.BitmapScalingMode="Fant"></Image> + <TextBlock Foreground="#FF5C5C" Margin="10 0 0 0" VerticalAlignment="Center">Report Issue</TextBlock> + </StackPanel> + </Button> <Button Margin="5 0 0 0" Width="140" Command="{Binding ResolveCommand}" CommandParameter="{x:Static local:ExceptionResolutions.Shutdown}">Shutdown</Button> <Button Margin="5 0 0 0" Width="140" Command="{Binding ResolveCommand}" CommandParameter="{x:Static local:ExceptionResolutions.Restart}">Restart</Button> </StackPanel> diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Windows/ExceptionWindow.xaml.cs b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Windows/ExceptionWindow.xaml.cs index 0f74fee17..0a6f2de39 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Windows/ExceptionWindow.xaml.cs +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Windows/ExceptionWindow.xaml.cs @@ -33,10 +33,15 @@ namespace Tango.MachineStudio.UI.Windows DataContext = this; } - public ExceptionWindow(Exception ex) : this() + public ExceptionWindow(Exception ex, bool canReport) : this() { Exception = ex.FlattenException(); ResolveCommand = new RelayCommand<ExceptionResolutions>(Resolve); + + if (!canReport) + { + btnReport.Visibility = Visibility.Collapsed; + } } private void Resolve(ExceptionResolutions resolution) diff --git a/Software/Visual_Studio/Tango.BL/Enumerations/Permissions.cs b/Software/Visual_Studio/Tango.BL/Enumerations/Permissions.cs index ee77c6f0f..a727bf5e8 100644 --- a/Software/Visual_Studio/Tango.BL/Enumerations/Permissions.cs +++ b/Software/Visual_Studio/Tango.BL/Enumerations/Permissions.cs @@ -75,5 +75,11 @@ namespace Tango.BL.Enumerations [Description("Allows loading the Users & Roles module in machine studio")] RunUsersAndRolesModule = 10, + /// <summary> + /// (Allows openning the machine studio developer console) + /// </summary> + [Description("Allows openning the machine studio developer console")] + RunDeveloperConsole = 11, + } } diff --git a/Software/Visual_Studio/Tango.CodeGeneration/CustomResolver.cs b/Software/Visual_Studio/Tango.CodeGeneration/CustomResolver.cs new file mode 100644 index 000000000..ee1acccae --- /dev/null +++ b/Software/Visual_Studio/Tango.CodeGeneration/CustomResolver.cs @@ -0,0 +1,20 @@ +using RazorEngine.Compilation; +using RazorEngine.Compilation.ReferenceResolver; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.CodeGeneration +{ + internal class CustomResolver : IReferenceResolver + { + public IEnumerable<CompilerReference> GetReferences(TypeContext context, IEnumerable<CompilerReference> includeAssemblies) + { + return new UseCurrentAssembliesReferenceResolver() + .GetReferences(context, includeAssemblies) + .Where(f => !f.GetFile().EndsWith(".winmd")); + } + } +} diff --git a/Software/Visual_Studio/Tango.CodeGeneration/Helper.cs b/Software/Visual_Studio/Tango.CodeGeneration/Helper.cs index dfbc893ee..af617fe3e 100644 --- a/Software/Visual_Studio/Tango.CodeGeneration/Helper.cs +++ b/Software/Visual_Studio/Tango.CodeGeneration/Helper.cs @@ -1,4 +1,5 @@ using RazorEngine; +using RazorEngine.Configuration; using RazorEngine.Templating; using System; using System.Collections.Generic; @@ -15,6 +16,12 @@ namespace Tango.CodeGeneration /// </summary> public static class Helper { + static Helper() + { + var config = new TemplateServiceConfiguration { Debug = true, ReferenceResolver = new CustomResolver() }; + Engine.Razor = RazorEngineService.Create(config); + } + /// <summary> /// Gets a code template by the code object type. /// </summary> diff --git a/Software/Visual_Studio/Tango.CodeGeneration/Tango.CodeGeneration.csproj b/Software/Visual_Studio/Tango.CodeGeneration/Tango.CodeGeneration.csproj index db77d9f20..ff83f8ce4 100644 --- a/Software/Visual_Studio/Tango.CodeGeneration/Tango.CodeGeneration.csproj +++ b/Software/Visual_Studio/Tango.CodeGeneration/Tango.CodeGeneration.csproj @@ -63,6 +63,7 @@ <Compile Include="Class.cs" /> <Compile Include="CodeFile.cs" /> <Compile Include="CodeObject.cs" /> + <Compile Include="CustomResolver.cs" /> <Compile Include="DpProperty.cs" /> <Compile Include="EntityCodeFileJavaExtension.cs" /> <Compile Include="EntityCodeFileJava.cs" /> diff --git a/Software/Visual_Studio/Tango.Core/ExtensionMethods/ObjectExtensions.cs b/Software/Visual_Studio/Tango.Core/ExtensionMethods/ObjectExtensions.cs index 87f410542..9d17bd198 100644 --- a/Software/Visual_Studio/Tango.Core/ExtensionMethods/ObjectExtensions.cs +++ b/Software/Visual_Studio/Tango.Core/ExtensionMethods/ObjectExtensions.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; +using Tango.Core.Json; using Tango.Serialization; /// <summary> @@ -93,4 +94,28 @@ public static class ObjectExtensions return JsonConvert.SerializeObject(obj, Formatting.Indented); } + + /// <summary> + /// Serializes the specified object to indented json string. + /// </summary> + /// <param name="obj">The object.</param> + /// <returns></returns> + public static String ToJsonString(this Object obj, params String[] ignoreProperties) + { + var settings = new JsonSerializerSettings() { ContractResolver = new DynamicContractResolver(ignoreProperties) }; + settings.Converters.Add(new StringEnumConverter { CamelCaseText = false }); + return JsonConvert.SerializeObject(obj, Formatting.Indented, settings); + } + + /// <summary> + /// Serializes the specified object to indented json html string. + /// </summary> + /// <param name="obj">The object.</param> + /// <returns></returns> + public static String ToHtmlJsonString(this Object obj, params String[] ignoreProperties) + { + var settings = new JsonSerializerSettings() { ContractResolver = new HtmlContractResolver(ignoreProperties) }; + settings.Converters.Add(new StringEnumConverter { CamelCaseText = false }); + return JsonConvert.SerializeObject(obj, Formatting.Indented, settings); + } } diff --git a/Software/Visual_Studio/Tango.Core/Helpers/EmbeddedResourceHelper.cs b/Software/Visual_Studio/Tango.Core/Helpers/EmbeddedResourceHelper.cs index d1d556296..bdf2cb684 100644 --- a/Software/Visual_Studio/Tango.Core/Helpers/EmbeddedResourceHelper.cs +++ b/Software/Visual_Studio/Tango.Core/Helpers/EmbeddedResourceHelper.cs @@ -15,5 +15,15 @@ namespace Tango.Core.Helpers Assembly asm = Assembly.GetCallingAssembly(); return asm.GetManifestResourceStream(relativePath); } + + public static String GetEmbeddedResourceText(String relativePath) + { + Assembly asm = Assembly.GetCallingAssembly(); + using (Stream st = asm.GetManifestResourceStream(relativePath)) + { + StreamReader reader = new StreamReader(st); + return reader.ReadToEnd(); + } + } } } diff --git a/Software/Visual_Studio/Tango.Core/Json/DynamicContractResolver.cs b/Software/Visual_Studio/Tango.Core/Json/DynamicContractResolver.cs new file mode 100644 index 000000000..293b6947f --- /dev/null +++ b/Software/Visual_Studio/Tango.Core/Json/DynamicContractResolver.cs @@ -0,0 +1,30 @@ +using Newtonsoft.Json.Serialization; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; +using System.Reflection; + +namespace Tango.Core.Json +{ + public class DynamicContractResolver : DefaultContractResolver + { + private List<String> _ignoreProperties; + + public DynamicContractResolver(params String[] ignoreProperties) + { + _ignoreProperties = ignoreProperties.ToList(); + } + + protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) + { + JsonProperty property = base.CreateProperty(member, memberSerialization); + + property.ShouldSerialize = (x) => !_ignoreProperties.Contains(property.PropertyName); + + return property; + } + } +} diff --git a/Software/Visual_Studio/Tango.Core/Json/HtmlContractResolver.cs b/Software/Visual_Studio/Tango.Core/Json/HtmlContractResolver.cs new file mode 100644 index 000000000..c4affd3ad --- /dev/null +++ b/Software/Visual_Studio/Tango.Core/Json/HtmlContractResolver.cs @@ -0,0 +1,39 @@ +using Newtonsoft.Json.Serialization; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; +using System.Reflection; + +namespace Tango.Core.Json +{ + public class HtmlContractResolver : DefaultContractResolver + { + private List<String> _ignoreProperties; + + public HtmlContractResolver(params String[] ignoreProperties) + { + if (ignoreProperties != null) + { + _ignoreProperties = ignoreProperties.ToList(); + } + else + { + _ignoreProperties = new List<string>(); + } + } + + protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) + { + JsonProperty property = base.CreateProperty(member, memberSerialization); + + property.ShouldSerialize = (x) => !_ignoreProperties.Contains(property.PropertyName); + + property.PropertyName = "<b>" + property.PropertyName.Replace("\"", "") + "<b/>"; + + return property; + } + } +} diff --git a/Software/Visual_Studio/Tango.Core/Tango.Core.csproj b/Software/Visual_Studio/Tango.Core/Tango.Core.csproj index a9196237b..648137190 100644 --- a/Software/Visual_Studio/Tango.Core/Tango.Core.csproj +++ b/Software/Visual_Studio/Tango.Core/Tango.Core.csproj @@ -91,6 +91,8 @@ <Compile Include="Helpers\PathHelper.cs" /> <Compile Include="Helpers\ThreadsHelper.cs" /> <Compile Include="IParameterized.cs" /> + <Compile Include="Json\HtmlContractResolver.cs" /> + <Compile Include="Json\DynamicContractResolver.cs" /> <Compile Include="ParameterIgnoreAttribute.cs" /> <Compile Include="ParameterItem.cs" /> <Compile Include="ParameterItemAttribute.cs" /> diff --git a/Software/Visual_Studio/Tango.Emulations/Emulators/MachineEmulator.cs b/Software/Visual_Studio/Tango.Emulations/Emulators/MachineEmulator.cs index a2c53f7b3..0b7ddc9ef 100644 --- a/Software/Visual_Studio/Tango.Emulations/Emulators/MachineEmulator.cs +++ b/Software/Visual_Studio/Tango.Emulations/Emulators/MachineEmulator.cs @@ -301,10 +301,6 @@ namespace Tango.Emulations.Emulators res.Events.AddRange(EventsStates.Where(x => x.IsActive).Select(x => new Event() { Type = x.EventType, Message = "Generated by Tango Embedded Emulator" })); - res.Version = "1.0.0.0"; - res.VersionBuildDate = DateTime.Now.ToString(); - res.VersionName = "Embedded Emulator"; - Transporter.SendResponse<PushDiagnosticsResponse>(res, request.Container.Token); Thread.Sleep(10); } @@ -648,7 +644,15 @@ namespace Tango.Emulations.Emulators _continousResponseTokens.Clear(); - Transporter.SendResponse<ConnectResponse>(new ConnectResponse(), request.Container.Token, null, request.Message.Password == "1234" ? ErrorCode.None : ErrorCode.UnauthorizedConnection); + Transporter.SendResponse<ConnectResponse>(new ConnectResponse() + { + DeviceInformation = new DeviceInformation() + { + Version = "1.0.0.0", + BuildDate = DateTime.Now.ToString(), + Name = "Machine Emulator", + }, + }, request.Container.Token, null, request.Message.Password == "1234" ? ErrorCode.None : ErrorCode.UnauthorizedConnection); } #endregion diff --git a/Software/Visual_Studio/Tango.Integration/Operation/IMachineOperator.cs b/Software/Visual_Studio/Tango.Integration/Operation/IMachineOperator.cs index 861935e83..efe4fe7af 100644 --- a/Software/Visual_Studio/Tango.Integration/Operation/IMachineOperator.cs +++ b/Software/Visual_Studio/Tango.Integration/Operation/IMachineOperator.cs @@ -64,6 +64,21 @@ namespace Tango.Integration.Operation bool EnableEmbeddedDebugging { get; set; } /// <summary> + /// Gets the last process parameters table sent to the embedded device. + /// </summary> + ProcessParametersTable CurrentProcessParameters { get; } + + /// <summary> + /// Gets the last hardware configuration sent to the embedded device. + /// </summary> + HardwareConfiguration CurrentHardwareConfiguration { get; } + + /// <summary> + /// Gets or sets the embedded device information. + /// </summary> + DeviceInformation DeviceInformation { get; } + + /// <summary> /// Gets or sets the machine events state provider used to get notifications about current machine events and errors. /// </summary> IMachineEventsStateProvider MachineEventsStateProvider { get; set; } diff --git a/Software/Visual_Studio/Tango.Integration/Operation/MachineOperator.cs b/Software/Visual_Studio/Tango.Integration/Operation/MachineOperator.cs index f7b447843..118bbc38a 100644 --- a/Software/Visual_Studio/Tango.Integration/Operation/MachineOperator.cs +++ b/Software/Visual_Studio/Tango.Integration/Operation/MachineOperator.cs @@ -49,6 +49,7 @@ namespace Tango.Integration.Operation public MachineOperator() : base() { + DeviceInformation = new DeviceInformation(); MachineEventsStateProvider = new DefaultMachineEventsStateProvider(); EnableEmbeddedDebugging = true; LogEmbeddedDebuggingToFile = true; @@ -154,6 +155,27 @@ namespace Tango.Integration.Operation /// </summary> public IMachineEventsStateProvider MachineEventsStateProvider { get; set; } + /// <summary> + /// Gets the last process parameters table sent to the embedded device. + /// </summary> + public ProcessParametersTable CurrentProcessParameters { get; private set; } + + /// <summary> + /// Gets the last hardware configuration sent to the embedded device. + /// </summary> + public HardwareConfiguration CurrentHardwareConfiguration { get; private set; } + + private DeviceInformation _deviceInformation; + /// <summary> + /// Gets or sets the embedded device information. + /// </summary> + public DeviceInformation DeviceInformation + { + get { return _deviceInformation; } + set { _deviceInformation = value; RaisePropertyChangedAuto(); } + } + + #endregion #region Virtual Methods @@ -367,6 +389,8 @@ namespace Tango.Integration.Operation var response = await SendRequest<ConnectRequest, ConnectResponse>(request); LogResponseReceived(response.Message); + DeviceInformation = response.Message.DeviceInformation; + OnEnableDiagnosticsChanged(EnableDiagnostics); OnEnableEmbeddedDebuggingChanged(EnableEmbeddedDebugging); } @@ -391,6 +415,8 @@ namespace Tango.Integration.Operation /// <returns></returns> public JobHandler Print(Job job, ProcessParametersTable processParameters) { + CurrentProcessParameters = processParameters; + JobRequest request = new JobRequest(); JobTicket ticket = new JobTicket(); @@ -508,6 +534,7 @@ namespace Tango.Integration.Operation try { + CurrentProcessParameters = processParameters; LogRequestSent(request); response = await SendRequest<UploadProcessParametersRequest, UploadProcessParametersResponse>(request); LogResponseReceived(response); @@ -530,8 +557,23 @@ namespace Tango.Integration.Operation { UploadHardwareConfigurationRequest request = new UploadHardwareConfigurationRequest(); request.HardwareConfiguration = hardwareConfiguration; - LogRequestSent(request); - return await SendRequest<UploadHardwareConfigurationRequest, UploadHardwareConfigurationResponse>(request); + + UploadHardwareConfigurationResponse response = null; + + try + { + CurrentHardwareConfiguration = hardwareConfiguration; + LogRequestSent(request); + response = await SendRequest<UploadHardwareConfigurationRequest, UploadHardwareConfigurationResponse>(request); + LogResponseReceived(response); + } + catch (Exception ex) + { + LogRequestFailed(request, ex); + throw ex; + } + + return response; } /// <summary> diff --git a/Software/Visual_Studio/Tango.PMR/Connection/ConnectResponse.cs b/Software/Visual_Studio/Tango.PMR/Connection/ConnectResponse.cs index 1e5562f11..3ff3c91c1 100644 --- a/Software/Visual_Studio/Tango.PMR/Connection/ConnectResponse.cs +++ b/Software/Visual_Studio/Tango.PMR/Connection/ConnectResponse.cs @@ -23,12 +23,14 @@ namespace Tango.PMR.Connection { byte[] descriptorData = global::System.Convert.FromBase64String( string.Concat( "ChVDb25uZWN0UmVzcG9uc2UucHJvdG8SFFRhbmdvLlBNUi5Db25uZWN0aW9u", - "IhEKD0Nvbm5lY3RSZXNwb25zZUIgCh5jb20udHdpbmUudGFuZ28ucG1yLmNv", - "bm5lY3Rpb25iBnByb3RvMw==")); + "GhdEZXZpY2VJbmZvcm1hdGlvbi5wcm90byJVCg9Db25uZWN0UmVzcG9uc2US", + "QgoRRGV2aWNlSW5mb3JtYXRpb24YASABKAsyJy5UYW5nby5QTVIuQ29ubmVj", + "dGlvbi5EZXZpY2VJbmZvcm1hdGlvbkIgCh5jb20udHdpbmUudGFuZ28ucG1y", + "LmNvbm5lY3Rpb25iBnByb3RvMw==")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, - new pbr::FileDescriptor[] { }, + new pbr::FileDescriptor[] { global::Tango.PMR.Connection.DeviceInformationReflection.Descriptor, }, new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { - new pbr::GeneratedClrTypeInfo(typeof(global::Tango.PMR.Connection.ConnectResponse), global::Tango.PMR.Connection.ConnectResponse.Parser, null, null, null, null) + new pbr::GeneratedClrTypeInfo(typeof(global::Tango.PMR.Connection.ConnectResponse), global::Tango.PMR.Connection.ConnectResponse.Parser, new[]{ "DeviceInformation" }, null, null, null) })); } #endregion @@ -59,6 +61,7 @@ namespace Tango.PMR.Connection { [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public ConnectResponse(ConnectResponse other) : this() { + DeviceInformation = other.deviceInformation_ != null ? other.DeviceInformation.Clone() : null; } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -66,6 +69,17 @@ namespace Tango.PMR.Connection { return new ConnectResponse(this); } + /// <summary>Field number for the "DeviceInformation" field.</summary> + public const int DeviceInformationFieldNumber = 1; + private global::Tango.PMR.Connection.DeviceInformation deviceInformation_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public global::Tango.PMR.Connection.DeviceInformation DeviceInformation { + get { return deviceInformation_; } + set { + deviceInformation_ = value; + } + } + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public override bool Equals(object other) { return Equals(other as ConnectResponse); @@ -79,12 +93,14 @@ namespace Tango.PMR.Connection { if (ReferenceEquals(other, this)) { return true; } + if (!object.Equals(DeviceInformation, other.DeviceInformation)) return false; return true; } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public override int GetHashCode() { int hash = 1; + if (deviceInformation_ != null) hash ^= DeviceInformation.GetHashCode(); return hash; } @@ -95,11 +111,18 @@ namespace Tango.PMR.Connection { [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void WriteTo(pb::CodedOutputStream output) { + if (deviceInformation_ != null) { + output.WriteRawTag(10); + output.WriteMessage(DeviceInformation); + } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int CalculateSize() { int size = 0; + if (deviceInformation_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(DeviceInformation); + } return size; } @@ -108,6 +131,12 @@ namespace Tango.PMR.Connection { if (other == null) { return; } + if (other.deviceInformation_ != null) { + if (deviceInformation_ == null) { + deviceInformation_ = new global::Tango.PMR.Connection.DeviceInformation(); + } + DeviceInformation.MergeFrom(other.DeviceInformation); + } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -118,6 +147,13 @@ namespace Tango.PMR.Connection { default: input.SkipLastField(); break; + case 10: { + if (deviceInformation_ == null) { + deviceInformation_ = new global::Tango.PMR.Connection.DeviceInformation(); + } + input.ReadMessage(deviceInformation_); + break; + } } } } diff --git a/Software/Visual_Studio/Tango.PMR/Connection/DeviceInformation.cs b/Software/Visual_Studio/Tango.PMR/Connection/DeviceInformation.cs new file mode 100644 index 000000000..0fec71b8b --- /dev/null +++ b/Software/Visual_Studio/Tango.PMR/Connection/DeviceInformation.cs @@ -0,0 +1,216 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: DeviceInformation.proto +#pragma warning disable 1591, 0612, 3021 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +namespace Tango.PMR.Connection { + + /// <summary>Holder for reflection information generated from DeviceInformation.proto</summary> + public static partial class DeviceInformationReflection { + + #region Descriptor + /// <summary>File descriptor for DeviceInformation.proto</summary> + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static DeviceInformationReflection() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "ChdEZXZpY2VJbmZvcm1hdGlvbi5wcm90bxIUVGFuZ28uUE1SLkNvbm5lY3Rp", + "b24iRQoRRGV2aWNlSW5mb3JtYXRpb24SDAoETmFtZRgBIAEoCRIPCgdWZXJz", + "aW9uGAIgASgJEhEKCUJ1aWxkRGF0ZRgDIAEoCUIgCh5jb20udHdpbmUudGFu", + "Z28ucG1yLmNvbm5lY3Rpb25iBnByb3RvMw==")); + descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { + new pbr::GeneratedClrTypeInfo(typeof(global::Tango.PMR.Connection.DeviceInformation), global::Tango.PMR.Connection.DeviceInformation.Parser, new[]{ "Name", "Version", "BuildDate" }, null, null, null) + })); + } + #endregion + + } + #region Messages + public sealed partial class DeviceInformation : pb::IMessage<DeviceInformation> { + private static readonly pb::MessageParser<DeviceInformation> _parser = new pb::MessageParser<DeviceInformation>(() => new DeviceInformation()); + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser<DeviceInformation> Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::Tango.PMR.Connection.DeviceInformationReflection.Descriptor.MessageTypes[0]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public DeviceInformation() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public DeviceInformation(DeviceInformation other) : this() { + name_ = other.name_; + version_ = other.version_; + buildDate_ = other.buildDate_; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public DeviceInformation Clone() { + return new DeviceInformation(this); + } + + /// <summary>Field number for the "Name" field.</summary> + public const int NameFieldNumber = 1; + private string name_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string Name { + get { return name_; } + set { + name_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// <summary>Field number for the "Version" field.</summary> + public const int VersionFieldNumber = 2; + private string version_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string Version { + get { return version_; } + set { + version_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// <summary>Field number for the "BuildDate" field.</summary> + public const int BuildDateFieldNumber = 3; + private string buildDate_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string BuildDate { + get { return buildDate_; } + set { + buildDate_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as DeviceInformation); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(DeviceInformation other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Name != other.Name) return false; + if (Version != other.Version) return false; + if (BuildDate != other.BuildDate) return false; + return true; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (Name.Length != 0) hash ^= Name.GetHashCode(); + if (Version.Length != 0) hash ^= Version.GetHashCode(); + if (BuildDate.Length != 0) hash ^= BuildDate.GetHashCode(); + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + if (Name.Length != 0) { + output.WriteRawTag(10); + output.WriteString(Name); + } + if (Version.Length != 0) { + output.WriteRawTag(18); + output.WriteString(Version); + } + if (BuildDate.Length != 0) { + output.WriteRawTag(26); + output.WriteString(BuildDate); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (Name.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Name); + } + if (Version.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Version); + } + if (BuildDate.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(BuildDate); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(DeviceInformation other) { + if (other == null) { + return; + } + if (other.Name.Length != 0) { + Name = other.Name; + } + if (other.Version.Length != 0) { + Version = other.Version; + } + if (other.BuildDate.Length != 0) { + BuildDate = other.BuildDate; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + input.SkipLastField(); + break; + case 10: { + Name = input.ReadString(); + break; + } + case 18: { + Version = input.ReadString(); + break; + } + case 26: { + BuildDate = input.ReadString(); + break; + } + } + } + } + + } + + #endregion + +} + +#endregion Designer generated code diff --git a/Software/Visual_Studio/Tango.PMR/Diagnostics/PushDiagnosticsResponse.cs b/Software/Visual_Studio/Tango.PMR/Diagnostics/PushDiagnosticsResponse.cs index c266bb0a8..db4479cf6 100644 --- a/Software/Visual_Studio/Tango.PMR/Diagnostics/PushDiagnosticsResponse.cs +++ b/Software/Visual_Studio/Tango.PMR/Diagnostics/PushDiagnosticsResponse.cs @@ -25,19 +25,17 @@ namespace Tango.PMR.Diagnostics { "Ch1QdXNoRGlhZ25vc3RpY3NSZXNwb25zZS5wcm90bxIVVGFuZ28uUE1SLkRp", "YWdub3N0aWNzGhBEaWdpdGFsUGluLnByb3RvGhlWYWx1ZUNvbXBvbmVudFN0", "YXRlLnByb3RvGhlEaWFnbm9zdGljc01vbml0b3JzLnByb3RvGgtFdmVudC5w", - "cm90byLDAgoXUHVzaERpYWdub3N0aWNzUmVzcG9uc2USPAoITW9uaXRvcnMY", + "cm90byKDAgoXUHVzaERpYWdub3N0aWNzUmVzcG9uc2USPAoITW9uaXRvcnMY", "ASABKAsyKi5UYW5nby5QTVIuRGlhZ25vc3RpY3MuRGlhZ25vc3RpY3NNb25p", "dG9ycxI2CgtEaWdpdGFsUGlucxgCIAMoCzIhLlRhbmdvLlBNUi5EaWFnbm9z", "dGljcy5EaWdpdGFsUGluEkQKEENvbXBvbmVudHNTdGF0ZXMYAyADKAsyKi5U", "YW5nby5QTVIuRGlhZ25vc3RpY3MuVmFsdWVDb21wb25lbnRTdGF0ZRIsCgZF", - "dmVudHMYBCADKAsyHC5UYW5nby5QTVIuRGlhZ25vc3RpY3MuRXZlbnQSDwoH", - "VmVyc2lvbhgFIAEoCRITCgtWZXJzaW9uTmFtZRgGIAEoCRIYChBWZXJzaW9u", - "QnVpbGREYXRlGAcgASgJQiEKH2NvbS50d2luZS50YW5nby5wbXIuZGlhZ25v", - "c3RpY3NiBnByb3RvMw==")); + "dmVudHMYBCADKAsyHC5UYW5nby5QTVIuRGlhZ25vc3RpY3MuRXZlbnRCIQof", + "Y29tLnR3aW5lLnRhbmdvLnBtci5kaWFnbm9zdGljc2IGcHJvdG8z")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { global::Tango.PMR.Diagnostics.DigitalPinReflection.Descriptor, global::Tango.PMR.Diagnostics.ValueComponentStateReflection.Descriptor, global::Tango.PMR.Diagnostics.DiagnosticsMonitorsReflection.Descriptor, global::Tango.PMR.Diagnostics.EventReflection.Descriptor, }, new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { - new pbr::GeneratedClrTypeInfo(typeof(global::Tango.PMR.Diagnostics.PushDiagnosticsResponse), global::Tango.PMR.Diagnostics.PushDiagnosticsResponse.Parser, new[]{ "Monitors", "DigitalPins", "ComponentsStates", "Events", "Version", "VersionName", "VersionBuildDate" }, null, null, null) + new pbr::GeneratedClrTypeInfo(typeof(global::Tango.PMR.Diagnostics.PushDiagnosticsResponse), global::Tango.PMR.Diagnostics.PushDiagnosticsResponse.Parser, new[]{ "Monitors", "DigitalPins", "ComponentsStates", "Events" }, null, null, null) })); } #endregion @@ -72,9 +70,6 @@ namespace Tango.PMR.Diagnostics { digitalPins_ = other.digitalPins_.Clone(); componentsStates_ = other.componentsStates_.Clone(); events_ = other.events_.Clone(); - version_ = other.version_; - versionName_ = other.versionName_; - versionBuildDate_ = other.versionBuildDate_; } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -135,42 +130,6 @@ namespace Tango.PMR.Diagnostics { get { return events_; } } - /// <summary>Field number for the "Version" field.</summary> - public const int VersionFieldNumber = 5; - private string version_ = ""; - /// <summary> - ///Software Information - /// </summary> - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - public string Version { - get { return version_; } - set { - version_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - - /// <summary>Field number for the "VersionName" field.</summary> - public const int VersionNameFieldNumber = 6; - private string versionName_ = ""; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - public string VersionName { - get { return versionName_; } - set { - versionName_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - - /// <summary>Field number for the "VersionBuildDate" field.</summary> - public const int VersionBuildDateFieldNumber = 7; - private string versionBuildDate_ = ""; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - public string VersionBuildDate { - get { return versionBuildDate_; } - set { - versionBuildDate_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public override bool Equals(object other) { return Equals(other as PushDiagnosticsResponse); @@ -188,9 +147,6 @@ namespace Tango.PMR.Diagnostics { if(!digitalPins_.Equals(other.digitalPins_)) return false; if(!componentsStates_.Equals(other.componentsStates_)) return false; if(!events_.Equals(other.events_)) return false; - if (Version != other.Version) return false; - if (VersionName != other.VersionName) return false; - if (VersionBuildDate != other.VersionBuildDate) return false; return true; } @@ -201,9 +157,6 @@ namespace Tango.PMR.Diagnostics { hash ^= digitalPins_.GetHashCode(); hash ^= componentsStates_.GetHashCode(); hash ^= events_.GetHashCode(); - if (Version.Length != 0) hash ^= Version.GetHashCode(); - if (VersionName.Length != 0) hash ^= VersionName.GetHashCode(); - if (VersionBuildDate.Length != 0) hash ^= VersionBuildDate.GetHashCode(); return hash; } @@ -221,18 +174,6 @@ namespace Tango.PMR.Diagnostics { digitalPins_.WriteTo(output, _repeated_digitalPins_codec); componentsStates_.WriteTo(output, _repeated_componentsStates_codec); events_.WriteTo(output, _repeated_events_codec); - if (Version.Length != 0) { - output.WriteRawTag(42); - output.WriteString(Version); - } - if (VersionName.Length != 0) { - output.WriteRawTag(50); - output.WriteString(VersionName); - } - if (VersionBuildDate.Length != 0) { - output.WriteRawTag(58); - output.WriteString(VersionBuildDate); - } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -244,15 +185,6 @@ namespace Tango.PMR.Diagnostics { size += digitalPins_.CalculateSize(_repeated_digitalPins_codec); size += componentsStates_.CalculateSize(_repeated_componentsStates_codec); size += events_.CalculateSize(_repeated_events_codec); - if (Version.Length != 0) { - size += 1 + pb::CodedOutputStream.ComputeStringSize(Version); - } - if (VersionName.Length != 0) { - size += 1 + pb::CodedOutputStream.ComputeStringSize(VersionName); - } - if (VersionBuildDate.Length != 0) { - size += 1 + pb::CodedOutputStream.ComputeStringSize(VersionBuildDate); - } return size; } @@ -270,15 +202,6 @@ namespace Tango.PMR.Diagnostics { digitalPins_.Add(other.digitalPins_); componentsStates_.Add(other.componentsStates_); events_.Add(other.events_); - if (other.Version.Length != 0) { - Version = other.Version; - } - if (other.VersionName.Length != 0) { - VersionName = other.VersionName; - } - if (other.VersionBuildDate.Length != 0) { - VersionBuildDate = other.VersionBuildDate; - } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -308,18 +231,6 @@ namespace Tango.PMR.Diagnostics { events_.AddEntriesFrom(input, _repeated_events_codec); break; } - case 42: { - Version = input.ReadString(); - break; - } - case 50: { - VersionName = input.ReadString(); - break; - } - case 58: { - VersionBuildDate = input.ReadString(); - break; - } } } } diff --git a/Software/Visual_Studio/Tango.PMR/ExtensionMethods.cs b/Software/Visual_Studio/Tango.PMR/ExtensionMethods.cs index 9707399d9..3a255480d 100644 --- a/Software/Visual_Studio/Tango.PMR/ExtensionMethods.cs +++ b/Software/Visual_Studio/Tango.PMR/ExtensionMethods.cs @@ -13,7 +13,7 @@ namespace Tango.PMR /// <summary> /// Contains PMR extension methods. /// </summary> - public static class ExtensionMethods + public static class ExtensionMethods { /// <summary> /// Gets the protobuf <see cref="OriginalNameAttribute"/> attribute value from the message type. diff --git a/Software/Visual_Studio/Tango.PMR/Tango.PMR.csproj b/Software/Visual_Studio/Tango.PMR/Tango.PMR.csproj index bab99641a..07098b09f 100644 --- a/Software/Visual_Studio/Tango.PMR/Tango.PMR.csproj +++ b/Software/Visual_Studio/Tango.PMR/Tango.PMR.csproj @@ -59,6 +59,7 @@ <Compile Include="ColorLab\OutputLiquid.cs" /> <Compile Include="Connection\ConnectRequest.cs" /> <Compile Include="Connection\ConnectResponse.cs" /> + <Compile Include="Connection\DeviceInformation.cs" /> <Compile Include="Connection\DisconnectRequest.cs" /> <Compile Include="Connection\DisconnectResponse.cs" /> <Compile Include="Connection\KeepAliveRequest.cs" /> diff --git a/Software/Visual_Studio/Tango.SharedUI/Controls/ScriptEditorControl.xaml.cs b/Software/Visual_Studio/Tango.SharedUI/Controls/ScriptEditorControl.xaml.cs index a74e649c0..3fe87c7e2 100644 --- a/Software/Visual_Studio/Tango.SharedUI/Controls/ScriptEditorControl.xaml.cs +++ b/Software/Visual_Studio/Tango.SharedUI/Controls/ScriptEditorControl.xaml.cs @@ -182,7 +182,7 @@ namespace Tango.SharedUI.Controls foreach (var v in variables) { - var hT = IntellisenseTypes.SingleOrDefault(x => x.Value.Name == v.Type); + var hT = IntellisenseTypes.SingleOrDefault(x => x.Key == v.Type); if (hT.Value != null) { @@ -259,7 +259,7 @@ namespace Tango.SharedUI.Controls - foreach (var name in HighlightTypes.Select(x => x.Value).Select(x => x.Name)) + foreach (var name in HighlightTypes.Select(x => x.Key)) { code += String.Format("<Word>{0}</Word>", name) + Environment.NewLine; } diff --git a/Software/Visual_Studio/Tango.TFS/ITeamFoundationServiceClient.cs b/Software/Visual_Studio/Tango.TFS/ITeamFoundationServiceClient.cs index 51394f663..1fe169218 100644 --- a/Software/Visual_Studio/Tango.TFS/ITeamFoundationServiceClient.cs +++ b/Software/Visual_Studio/Tango.TFS/ITeamFoundationServiceClient.cs @@ -35,6 +35,42 @@ namespace Tango.TFS Task<WorkItem> GetWorkItem(Project project, int id); /// <summary> + /// Gets all the work items created by the specified team member. + /// </summary> + /// <param name="project">The project.</param> + /// <param name="member">The member.</param> + /// <returns></returns> + Task<List<WorkItem>> GetWorkItemsCreatedBy(Project project, TeamMember member); + + /// <summary> + /// Sets the state of the work item. + /// </summary> + /// <param name="project">The project.</param> + /// <param name="item">The item.</param> + /// <param name="state">The state.</param> + /// <returns></returns> + Task<WorkItem> SetWorkItemState(Project project, WorkItem item, State state); + + /// <summary> + /// Sets the work item assignment. + /// </summary> + /// <param name="project">The project.</param> + /// <param name="item">The item.</param> + /// <param name="member">The member.</param> + /// <returns></returns> + Task<WorkItem> SetWorkItemAssignment(Project project, WorkItem item, TeamMember member); + + /// <summary> + /// Adds a comment to the work item discussion. + /// </summary> + /// <param name="project">The project.</param> + /// <param name="item">The item.</param> + /// <param name="teamMember">Team member</param> + /// <param name="comment">The comment.</param> + /// <returns></returns> + Task<WorkItem> AddWorkItemComment(Project project, WorkItem item, TeamMember teamMember, String comment); + + /// <summary> /// Deletes the specified work item. /// </summary> /// <param name="project">The project.</param> diff --git a/Software/Visual_Studio/Tango.TFS/ResolvedReason.cs b/Software/Visual_Studio/Tango.TFS/ResolvedReason.cs new file mode 100644 index 000000000..e1af689a2 --- /dev/null +++ b/Software/Visual_Studio/Tango.TFS/ResolvedReason.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.TFS +{ + public enum ResolvedReason + { + [Description("As Designed")] + AsDesigned, + [Description("Cannot Reproduce")] + CannotReproduce, + [Description("Copied To Backlog")] + CopiedToBacklog, + [Description("Deferred")] + Deferred, + [Description("Duplicate")] + Duplicate, + [Description("Fixed")] + Fixed, + [Description("Obsolete")] + Obsolete, + } +} diff --git a/Software/Visual_Studio/Tango.TFS/Tango.TFS.csproj b/Software/Visual_Studio/Tango.TFS/Tango.TFS.csproj index 00c9171c3..cf15c2e9e 100644 --- a/Software/Visual_Studio/Tango.TFS/Tango.TFS.csproj +++ b/Software/Visual_Studio/Tango.TFS/Tango.TFS.csproj @@ -204,6 +204,7 @@ <Compile Include="Iteration.cs" /> <Compile Include="Priority.cs" /> <Compile Include="Project.cs" /> + <Compile Include="ResolvedReason.cs" /> <Compile Include="Severity.cs" /> <Compile Include="State.cs" /> <Compile Include="Tag.cs" /> @@ -244,6 +245,10 @@ <Project>{a34ee0f0-649d-41c8-8489-b6f1cc6924ee}</Project> <Name>Tango.Core</Name> </ProjectReference> + <ProjectReference Include="..\Tango.Logging\Tango.Logging.csproj"> + <Project>{bc932dbd-7cdb-488c-99e4-f02cf441f55e}</Project> + <Name>Tango.Logging</Name> + </ProjectReference> </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="..\packages\Microsoft.TeamFoundationServer.ExtendedClient.15.112.1\build\Microsoft.TeamFoundationServer.ExtendedClient.targets" Condition="Exists('..\packages\Microsoft.TeamFoundationServer.ExtendedClient.15.112.1\build\Microsoft.TeamFoundationServer.ExtendedClient.targets')" /> diff --git a/Software/Visual_Studio/Tango.TFS/TeamFoundationServiceClient.cs b/Software/Visual_Studio/Tango.TFS/TeamFoundationServiceClient.cs index e6390260e..6eef1ef92 100644 --- a/Software/Visual_Studio/Tango.TFS/TeamFoundationServiceClient.cs +++ b/Software/Visual_Studio/Tango.TFS/TeamFoundationServiceClient.cs @@ -31,6 +31,9 @@ namespace Tango.TFS public const String PRIORITY = "Microsoft.VSTS.Common.Priority"; public const String STEPS_TO_REP = "Microsoft.VSTS.TCM.ReproSteps"; public const String SYSTEM_INFO = "Microsoft.VSTS.TCM.SystemInfo"; + public const String RESOLVED_BY = "Microsoft.VSTS.Common.ResolvedBy"; + public const String RESOLVED_DATE = "Microsoft.VSTS.Common.ResolvedDate"; + public const String RESOLVED_REASON = "Microsoft.VSTS.Common.ResolvedReason"; } #endregion @@ -86,14 +89,17 @@ namespace Tango.TFS VssConnection connection = CreateConnection(); + LogManager.Log("Retrieving project " + name + " details..."); ProjectHttpClient projectClient = connection.GetClient<ProjectHttpClient>(); - TeamProjectReference project = projectClient.GetProjects(null).Result.FirstOrDefault(x => x.Name == name); + TeamProjectReference project = projectClient.GetProjects().Result.FirstOrDefault(x => x.Name == name); if (project == null) { - throw new ArgumentException(String.Format("Project '{0}' could not be found.", name)); + throw LogManager.Log(new ArgumentException(String.Format("Project '{0}' could not be found.", name))); } + LogManager.Log("Project details successfully retrieved."); + p.Name = project.Name; p.ID = project.Id; p.URL = project.Url; @@ -128,7 +134,7 @@ namespace Tango.TFS }); } - var projCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(CollectionURL)); + var projCollection = new TfsTeamProjectCollection(new Uri(CollectionURL), connection.Credentials); var store = projCollection.GetService<WorkItemStore>(); WorkItemCollection queryResults = store.Query("Select [State], [Title] " + "From WorkItems " + "Where [Work Item Type] = 'User Story'"); @@ -213,6 +219,17 @@ namespace Tango.TFS }); } + if (!String.IsNullOrWhiteSpace(workItem.Comment)) + { + patchDocument.Add(new JsonPatchOperation + { + Operation = Operation.Add, + Path = GetFieldNameForWrite(CoreField.History), + Value = workItem.Comment, + From = workItem.CreatedBy.AssignName + }); + } + patchDocument.Add(new JsonPatchOperation { Operation = Operation.Add, @@ -367,72 +384,150 @@ namespace Tango.TFS { return Task.Factory.StartNew<WorkItem>(() => { - WorkItem workItem = new WorkItem(); - var connection = CreateConnection(); WorkItemTrackingHttpClient witClient = connection.GetClient<WorkItemTrackingHttpClient>(); var item = witClient.GetWorkItemAsync(id, expand: Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.WorkItemExpand.All).Result; - workItem.ID = item.Id.Value; - workItem.Title = item.Fields[GetFieldNameForRead(CoreField.Title)].ToString(); - workItem.Description = TryGetField(item.Fields, GetFieldNameForRead(CoreField.Description)); - workItem.Area = new Area() - { - Path = item.Fields[GetFieldNameForRead(CoreField.AreaPath)].ToString(), - Name = Path.GetFileName(item.Fields[GetFieldNameForRead(CoreField.AreaPath)].ToString()), - }; - workItem.Iteration = new Iteration() - { - Path = item.Fields[GetFieldNameForRead(CoreField.IterationPath)].ToString(), - Name = Path.GetFileName(item.Fields[GetFieldNameForRead(CoreField.IterationPath)].ToString()), - }; + return ConvertToWorkItem(project, item); + }); + } - workItem.AssignedTo = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.AssignedTo))); - workItem.CreatedBy = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.CreatedBy))); - workItem.ChangedBy = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.ChangedBy))); - workItem.AuthorizedAs = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.AuthorizedAs))); + /// <summary> + /// Deletes the specified work item. + /// </summary> + /// <param name="project">The project.</param> + /// <param name="id">The identifier.</param> + /// <returns></returns> + public Task DeleteWorkItem(Project project, int id) + { + return Task.Factory.StartNew(() => + { + var connection = CreateConnection(); - workItem.Type = (WorkItemType)Enum.Parse(typeof(WorkItemType), item.Fields[GetFieldNameForRead(CoreField.WorkItemType)].ToString()); - workItem.URL = item.Url; + WorkItemTrackingHttpClient witClient = connection.GetClient<WorkItemTrackingHttpClient>(); + var result = witClient.DeleteWorkItemAsync(id, true).Result; + }); + } - if (item.Fields.ContainsKey(GetFieldNameForRead(CoreField.Tags))) + /// <summary> + /// Gets all the work items created by the specified team member. + /// </summary> + /// <param name="project">The project.</param> + /// <param name="member">The member.</param> + /// <returns></returns> + public Task<List<WorkItem>> GetWorkItemsCreatedBy(Project project, TeamMember member) + { + return Task.Factory.StartNew<List<WorkItem>>(() => + { + var connection = CreateConnection(); + + var projCollection = new TfsTeamProjectCollection(new Uri(CollectionURL), connection.Credentials); + var store = projCollection.GetService<WorkItemStore>(); + + WorkItemCollection queryResults = store.Query(String.Format("Select [Id]" + "From WorkItems " + "Where [Created By] = '{0}' And [Work Item Type] = 'Bug'", member.AssignName)); + var ids = queryResults.OfType<Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem>().Where(x => x.Project.Name == project.Name).ToList().Select(x => x.Id).ToList(); + + if (ids.Count == 0) return new List<WorkItem>(); + + WorkItemTrackingHttpClient witClient = connection.GetClient<WorkItemTrackingHttpClient>(); + + var items = witClient.GetWorkItemsAsync(ids).Result; + + return items.Select(x => ConvertToWorkItem(project, x)).ToList(); + }); + } + + /// <summary> + /// Sets the state of the work item. + /// </summary> + /// <param name="project">The project.</param> + /// <param name="item">The item.</param> + /// <param name="state">The state.</param> + /// <returns></returns> + public Task<WorkItem> SetWorkItemState(Project project, WorkItem item, State state) + { + return Task.Factory.StartNew<WorkItem>(() => + { + var connection = CreateConnection(); + + WorkItemTrackingHttpClient witClient = connection.GetClient<WorkItemTrackingHttpClient>(); + + var patchDocument = new JsonPatchDocument(); + + patchDocument.Add(new JsonPatchOperation { - List<String> tags = item.Fields[GetFieldNameForRead(CoreField.Tags)].ToString().Split(';').Select(x => x.Trim()).ToList(); - workItem.Tags = tags.Select(x => new Tag() { Name = x }).ToList(); - } + Operation = Operation.Replace, + Path = GetFieldNameForWrite(CoreField.State), + Value = state.ToString(), + }); - workItem.FoundInBuild = item.Fields[ExtensionFields.FOUND_IN_BUILD].ToString(); + var updatedItem = witClient.UpdateWorkItemAsync(patchDocument, item.ID).Result; - workItem.State = (State)Enum.Parse(typeof(State), item.Fields[GetFieldNameForRead(CoreField.State)].ToString()); + return ConvertToWorkItem(project, updatedItem); + }); + } - workItem.Severity = ParseEnumByDescription<Severity>(item.Fields[ExtensionFields.SEVERITY].ToString()); + /// <summary> + /// Adds a comment to the work item discussion. + /// </summary> + /// <param name="project">The project.</param> + /// <param name="item">The item.</param> + /// <param name="teamMember">Team member</param> + /// <param name="comment">The comment.</param> + /// <returns></returns> + public Task<WorkItem> AddWorkItemComment(Project project, WorkItem item, TeamMember teamMember, String comment) + { + return Task.Factory.StartNew<WorkItem>(() => + { + var connection = CreateConnection(); - workItem.Priority = (Priority)int.Parse(item.Fields[ExtensionFields.PRIORITY].ToString()); + WorkItemTrackingHttpClient witClient = connection.GetClient<WorkItemTrackingHttpClient>(); - workItem.StepsToReproduce = item.Fields[ExtensionFields.STEPS_TO_REP].ToString(); + var patchDocument = new JsonPatchDocument(); - workItem.SystemInformation = item.Fields[ExtensionFields.SYSTEM_INFO].ToString(); + patchDocument.Add(new JsonPatchOperation + { + Operation = Operation.Add, + Path = GetFieldNameForWrite(CoreField.History), + Value = comment, + From = teamMember.AssignName + }); - return workItem; + var updatedItem = witClient.UpdateWorkItemAsync(patchDocument, item.ID).Result; + + return ConvertToWorkItem(project, updatedItem); }); } /// <summary> - /// Deletes the specified work item. + /// Sets the work item assignment. /// </summary> /// <param name="project">The project.</param> - /// <param name="id">The identifier.</param> + /// <param name="item">The item.</param> + /// <param name="member">The member.</param> /// <returns></returns> - public Task DeleteWorkItem(Project project, int id) + public Task<WorkItem> SetWorkItemAssignment(Project project, WorkItem item, TeamMember member) { - return Task.Factory.StartNew(() => + return Task.Factory.StartNew<WorkItem>(() => { var connection = CreateConnection(); WorkItemTrackingHttpClient witClient = connection.GetClient<WorkItemTrackingHttpClient>(); - var result = witClient.DeleteWorkItemAsync(id, true).Result; + + var patchDocument = new JsonPatchDocument(); + + patchDocument.Add(new JsonPatchOperation + { + Operation = Operation.Replace, + Path = GetFieldNameForWrite(CoreField.AssignedTo), + Value = member.AssignName, + }); + + var updatedItem = witClient.UpdateWorkItemAsync(patchDocument, item.ID).Result; + + return ConvertToWorkItem(project, updatedItem); }); } @@ -446,7 +541,19 @@ namespace Tango.TFS /// <returns></returns> private VssConnection CreateConnection() { - VssConnection connection = new VssConnection(new Uri(CollectionURL), new VssBasicCredential(UserName, PersonalToken)); + LogManager.Log("Generating VSTS connection using personal token " + PersonalToken); + VssConnection connection = new VssConnection(new Uri(CollectionURL), new VssBasicCredential(String.Empty, PersonalToken)); + connection.Credentials.PromptType = CredentialPromptType.DoNotPrompt; + connection.ConnectAsync(VssConnectMode.User).SyncResult(); + LogManager.Log("VSS Connection established..."); + + LogManager.Log("Authenticated: " + connection.HasAuthenticated); + + if (connection.HasAuthenticated) + { + LogManager.Log("Authenticated Identity: " + connection.AuthorizedIdentity.DisplayName); + } + return connection; } @@ -514,6 +621,65 @@ namespace Tango.TFS return values[description]; } + private WorkItem ConvertToWorkItem(Project project, Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.WorkItem item) + { + WorkItem workItem = new WorkItem(); + + + workItem.ID = item.Id.Value; + workItem.Title = item.Fields[GetFieldNameForRead(CoreField.Title)].ToString(); + workItem.CreatedDate = DateTime.Parse(item.Fields[GetFieldNameForRead(CoreField.CreatedDate)].ToString()); + workItem.Description = TryGetField(item.Fields, GetFieldNameForRead(CoreField.Description)); + workItem.Area = new Area() + { + Path = item.Fields[GetFieldNameForRead(CoreField.AreaPath)].ToString(), + Name = Path.GetFileName(item.Fields[GetFieldNameForRead(CoreField.AreaPath)].ToString()), + }; + workItem.Iteration = new Iteration() + { + Path = item.Fields[GetFieldNameForRead(CoreField.IterationPath)].ToString(), + Name = Path.GetFileName(item.Fields[GetFieldNameForRead(CoreField.IterationPath)].ToString()), + }; + + workItem.AssignedTo = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.AssignedTo))); + workItem.CreatedBy = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.CreatedBy))); + workItem.ChangedBy = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.ChangedBy))); + workItem.AuthorizedAs = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.AuthorizedAs))); + + workItem.ResolvedBy = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, ExtensionFields.RESOLVED_BY)); + + if (item.Fields.ContainsKey(ExtensionFields.RESOLVED_REASON)) + { + workItem.ResolvedReason = ParseEnumByDescription<ResolvedReason>(item.Fields[ExtensionFields.RESOLVED_REASON].ToString()); + workItem.ResolvedDate = DateTime.Parse(item.Fields[ExtensionFields.RESOLVED_DATE].ToString()); + } + + workItem.Type = (WorkItemType)Enum.Parse(typeof(WorkItemType), item.Fields[GetFieldNameForRead(CoreField.WorkItemType)].ToString()); + workItem.URL = item.Url; + + if (item.Fields.ContainsKey(GetFieldNameForRead(CoreField.Tags))) + { + List<String> tags = item.Fields[GetFieldNameForRead(CoreField.Tags)].ToString().Split(';').Select(x => x.Trim()).ToList(); + workItem.Tags = tags.Select(x => new Tag() { Name = x }).ToList(); + } + + workItem.FoundInBuild = TryGetField(item.Fields, ExtensionFields.FOUND_IN_BUILD).ToString(); + + workItem.State = (State)Enum.Parse(typeof(State), item.Fields[GetFieldNameForRead(CoreField.State)].ToString()); + + workItem.Severity = ParseEnumByDescription<Severity>(item.Fields[ExtensionFields.SEVERITY].ToString()); + + workItem.Priority = (Priority)int.Parse(item.Fields[ExtensionFields.PRIORITY].ToString()); + + workItem.StepsToReproduce = TryGetField(item.Fields, ExtensionFields.STEPS_TO_REP).ToString(); + + workItem.SystemInformation = TryGetField(item.Fields, ExtensionFields.SYSTEM_INFO).ToString(); + + workItem.Comment = TryGetField(item.Fields, GetFieldNameForRead(CoreField.History)).ToString(); + + return workItem; + } + #endregion } } diff --git a/Software/Visual_Studio/Tango.TFS/WorkItem.cs b/Software/Visual_Studio/Tango.TFS/WorkItem.cs index 98ac05191..59f277f2e 100644 --- a/Software/Visual_Studio/Tango.TFS/WorkItem.cs +++ b/Software/Visual_Studio/Tango.TFS/WorkItem.cs @@ -101,6 +101,13 @@ namespace Tango.TFS set { _assignedTo = value; RaisePropertyChangedAuto(); } } + private DateTime _createdDate; + public DateTime CreatedDate + { + get { return _createdDate; } + set { _createdDate = value; RaisePropertyChangedAuto(); } + } + private TeamMember _createdBy; public TeamMember CreatedBy { @@ -115,6 +122,27 @@ namespace Tango.TFS set { _changedBy = value; RaisePropertyChangedAuto(); } } + private TeamMember _resolvedBy; + public TeamMember ResolvedBy + { + get { return _resolvedBy; } + set { _resolvedBy = value; RaisePropertyChangedAuto(); } + } + + private DateTime _resolvedDate; + public DateTime ResolvedDate + { + get { return _resolvedDate; } + set { _resolvedDate = value; RaisePropertyChangedAuto(); } + } + + private ResolvedReason _resolvedReason; + public ResolvedReason ResolvedReason + { + get { return _resolvedReason; } + set { _resolvedReason = value; RaisePropertyChangedAuto(); } + } + private TeamMember _authorizedAs; public TeamMember AuthorizedAs { @@ -140,6 +168,30 @@ namespace Tango.TFS set { _foundInBuild = value; RaisePropertyChangedAuto(); } } + private String _comment; + public String Comment + { + get { return _comment; } + set { _comment = value; RaisePropertyChangedAuto(); } + } + + public bool IsBuildVersionValid + { + get + { + Version v = null; + return Version.TryParse(FoundInBuild, out v); + } + } + + public Version FoundInBuildVersion + { + get + { + return Version.Parse(FoundInBuild); + } + } + public WorkItem() { Attachments = new List<Attachment>(); diff --git a/Software/Visual_Studio/Tango.UnitTesting/TFS_TST.cs b/Software/Visual_Studio/Tango.UnitTesting/TFS_TST.cs index 2727ad00e..3fff8daae 100644 --- a/Software/Visual_Studio/Tango.UnitTesting/TFS_TST.cs +++ b/Software/Visual_Studio/Tango.UnitTesting/TFS_TST.cs @@ -43,12 +43,62 @@ namespace Tango.UnitTesting Assert.IsNotNull(project); - var workItem = client.GetWorkItem(project, 153).Result; + var workItem = client.GetWorkItem(project, 168).Result; Assert.IsNotNull(workItem); } [TestMethod] + public void Get_Work_Items_Created_By() + { + ITeamFoundationServiceClient client = CreateClient(); + + var project = client.GetProject("Tango").Result; + + Assert.IsNotNull(project); + + var workItems = client.GetWorkItemsCreatedBy(project, project.Members.SingleOrDefault(x => x.AssignName.ToLower().Contains("roy"))).Result; + + Assert.IsTrue(workItems.Count > 0); + } + + [TestMethod] + public void Set_Work_Item_State() + { + ITeamFoundationServiceClient client = CreateClient(); + + var project = client.GetProject("Tango").Result; + + Assert.IsNotNull(project); + + var workItem = client.GetWorkItem(project, 164).Result; + + Assert.IsNotNull(workItem); + + var updated = client.SetWorkItemState(project, workItem, State.Active).Result; + + Assert.IsTrue(updated.State == State.Active); + } + + [TestMethod] + public void Add_Work_Item_Comment() + { + ITeamFoundationServiceClient client = CreateClient(); + + var project = client.GetProject("Tango").Result; + + Assert.IsNotNull(project); + + var workItem = client.GetWorkItem(project, 165).Result; + + Assert.IsNotNull(workItem); + + var updated = client.AddWorkItemComment(project, workItem, project.Members.SingleOrDefault(x => x.AssignName.ToLower().Contains("roy")), "Test Comment").Result; + + Assert.AreEqual(updated.Comment, "Test Comment"); + } + + [TestMethod] public void Upload_Work_Item() { ITeamFoundationServiceClient client = CreateClient(); |
