diff options
| author | Avi Levkovich <avi@twine-s.com> | 2020-08-25 10:08:01 +0300 |
|---|---|---|
| committer | Avi Levkovich <avi@twine-s.com> | 2020-08-25 10:08:01 +0300 |
| commit | 338edba081dba2a2aefb634811be1cc84ec93d64 (patch) | |
| tree | 0021538796c254a8eab8527e8461a2e831e68c1c /Software/Visual_Studio/FSE/Modules | |
| parent | 49ddda1cc22d6cbb72f499b37e5db32c95252dfa (diff) | |
| download | Tango-338edba081dba2a2aefb634811be1cc84ec93d64.tar.gz Tango-338edba081dba2a2aefb634811be1cc84ec93d64.zip | |
merge
Diffstat (limited to 'Software/Visual_Studio/FSE/Modules')
53 files changed, 2994 insertions, 211 deletions
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Diagnostics/ViewModels/DiagnosticsViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Diagnostics/ViewModels/DiagnosticsViewVM.cs index ac9279ab8..8a89fbc8f 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Diagnostics/ViewModels/DiagnosticsViewVM.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Diagnostics/ViewModels/DiagnosticsViewVM.cs @@ -15,8 +15,10 @@ using Tango.BL.Enumerations; using Tango.Core.Commands; using Tango.FSE.Common; using Tango.FSE.Common.Diagnostics; +using Tango.FSE.Common.FileAssociation; using Tango.FSE.Common.Notifications; using Tango.FSE.Diagnostics.Project; +using Tango.FSE.Diagnostics.Views; using Tango.PMR.Diagnostics; namespace Tango.FSE.Diagnostics.ViewModels @@ -403,6 +405,8 @@ namespace Tango.FSE.Diagnostics.ViewModels { base.OnApplicationStarted(); DiagnosticsProvider.FrameReceived += DiagnosticsProvider_FrameReceived; + + FileAssociationProvider.RegisterFileAssociationHandler("diagnostics", HandleDiagnosticsFileAssociation); } public async override void OnApplicationReady() @@ -781,5 +785,36 @@ namespace Tango.FSE.Diagnostics.ViewModels } #endregion + + #region File Association + + private async void HandleDiagnosticsFileAssociation(FileAssociationPackage package) + { + if (!CurrentUser.HasPermission(Permissions.FSE_EditDiagnosticsProject)) + { + await NotificationProvider.ShowError("Current user profile does not allow loading custom diagnostics projects."); + return; + } + + if (File.Exists(package.File)) + { + try + { + LogManager.Log("Opening diagnostics project from file association..."); + await NavigationManager.NavigateTo<DiagnosticsModule>(true, nameof(DiagnosticsView)); + using (NotificationProvider.PushTaskItem("Loading diagnostics project...")) + { + await Task.Delay(2000); + await LoadProject(package.File); + } + } + catch (Exception ex) + { + LogManager.Log(ex, "Error occurred while trying to handle the diagnostics file association."); + } + } + } + + #endregion } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Diagnostics/diagnostics.tdp b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Diagnostics/diagnostics.tdp index 9c75729b6..d5a250f16 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Diagnostics/diagnostics.tdp +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Diagnostics/diagnostics.tdp @@ -2442,7 +2442,7 @@ }, { "$type": "Tango.FSE.Diagnostics.Project.Widgets.Output.OutputWidget, Tango.FSE.Diagnostics", - "IO": "GPO_TFEED_BREAK_1", + "IO": "GPO_LED4", "EnableComponentSelection": false, "ID": "419fa0da-9c51-49fc-82cf-83707c03a44a", "Column": 10, diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Controls/RemoteDesktopControl.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Controls/RemoteDesktopControl.xaml index 77b977b65..4e6959e5b 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Controls/RemoteDesktopControl.xaml +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Controls/RemoteDesktopControl.xaml @@ -43,7 +43,7 @@ <Grid HorizontalAlignment="Right" VerticalAlignment="Top"> <StackPanel Margin="0 5 27 0" Orientation="Horizontal"> - <controls:ToggleIconButton IsChecked="{Binding TouchMode}" UncheckedIcon="CursorHand" CheckedIcon="CursorHand" UncheckedForeground="{StaticResource FSE_GrayBrush}" CheckedForeground="{StaticResource FSE_GreenBrush}" Cursor="Hand" ToolTip="Touch mode" IsEnabled="{Binding RemoteDesktopProvider.InSession}" Width="16" Height="16" Padding="0" /> + <controls:ToggleIconButton Visibility="Collapsed" IsChecked="{Binding TouchMode}" UncheckedIcon="CursorHand" CheckedIcon="CursorHand" UncheckedForeground="{StaticResource FSE_GrayBrush}" CheckedForeground="{StaticResource FSE_GreenBrush}" Cursor="Hand" ToolTip="Touch mode" IsEnabled="{Binding RemoteDesktopProvider.InSession}" Width="16" Height="16" Padding="0" /> <controls:ToggleIconButton Margin="7 0 0 0" IsChecked="{Binding CursorVisible}" UncheckedIcon="CursorDefault" CheckedIcon="CursorDefault" UncheckedForeground="{StaticResource FSE_GrayBrush}" CheckedForeground="{StaticResource FSE_GreenBrush}" Cursor="Hand" ToolTip="Display remote cursor" IsEnabled="{Binding RemoteDesktopProvider.InSession}" Width="16" Height="16" Padding="0" /> <controls:IconButton Margin="7 0 0 0" Cursor="Hand" ToolTip="Take snapshot" x:Name="btnSnapshot" Click="BtnSnapshot_Click" Command="{Binding TakeSnapshotCommand}" IsEnabled="{Binding RemoteDesktopProvider.InSession}" Icon="ImagePlus" Width="24" Height="24" Padding="0" /> <controls:IconButton Margin="5 0 0 0" Cursor="Hand" ToolTip="Open in separate window" Icon="OpenInNew" Width="24" Height="24" Padding="0" Command="{Binding WindowsManager.DetachToWindowCommand}" CommandParameter="{Binding ElementName=remoteDesktopControl}" Visibility="{Binding ElementName=remoteDesktopControl,Path=(win:WindowsManagerHelper.IsDetached),Converter={StaticResource BooleanToVisibilityInverseConverter}}" /> diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/RemoteDesktopView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/RemoteDesktopView.xaml index 95e4b4a52..012c16c64 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/RemoteDesktopView.xaml +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/RemoteDesktopView.xaml @@ -6,6 +6,7 @@ xmlns:global="clr-namespace:Tango.FSE.PPCConsole" xmlns:vm="clr-namespace:Tango.FSE.PPCConsole.ViewModels" xmlns:local="clr-namespace:Tango.FSE.PPCConsole.Views" + xmlns:auth="clr-namespace:Tango.FSE.Common.Authorization;assembly=Tango.FSE.Common" xmlns:controls="clr-namespace:Tango.FSE.Common.Controls;assembly=Tango.FSE.Common" xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:win="clr-namespace:Tango.FSE.Common.WindowsManager;assembly=Tango.FSE.Common" @@ -153,11 +154,11 @@ </Style.Triggers> </Style> </UniformGrid.Style> - <Button IsEnabled="{Binding RemoteDesktopProvider.InSession}" Width="250" Margin="5" Style="{StaticResource FSE_Button_Polygon}" Content="Hide PPC and Open Shell" Command="{Binding HideAndOpenShellCommand}" /> + <Button auth:AuthorizationHelper.Mode="Collapsed" auth:AuthorizationHelper.Permission="FSE_RemoteDesktopControl" IsEnabled="{Binding RemoteDesktopProvider.InSession}" Width="250" Margin="5" Style="{StaticResource FSE_Button_Polygon}" Content="Hide PPC and Open Shell" Command="{Binding HideAndOpenShellCommand}" /> - <Button IsEnabled="{Binding RemoteDesktopProvider.InSession}" Width="250" Margin="5" Style="{StaticResource FSE_Button_Polygon}" Content="Restart application"/> + <Button auth:AuthorizationHelper.Mode="Collapsed" auth:AuthorizationHelper.Permission="FSE_RemoteDesktopControl" IsEnabled="{Binding RemoteDesktopProvider.InSession}" Width="250" Margin="5" Style="{StaticResource FSE_Button_Polygon}" Content="Restart application"/> - <Button IsEnabled="{Binding RemoteDesktopProvider.InSession}" Width="250" Margin="5" Style="{StaticResource FSE_Button_Polygon}" Content="Open Task Manager" Command="{Binding OpenTaskManagerCommand}" /> + <Button auth:AuthorizationHelper.Mode="Collapsed" auth:AuthorizationHelper.Permission="FSE_RemoteDesktopControl" IsEnabled="{Binding RemoteDesktopProvider.InSession}" Width="250" Margin="5" Style="{StaticResource FSE_Button_Polygon}" Content="Open Task Manager" Command="{Binding OpenTaskManagerCommand}" /> <CheckBox Margin="5" HorizontalAlignment="Center" IsEnabled="{Binding RemoteDesktopProvider.InSession,Converter={StaticResource BooleanInverseConverter}}" IsChecked="{Binding RemoteDesktopProvider.EnableWebRtc}">Enable P2P communication channel</CheckBox> </UniformGrid> diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Content/VersionHistory/VersionHistory.aml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Content/VersionHistory/VersionHistory.aml new file mode 100644 index 000000000..b24762998 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Content/VersionHistory/VersionHistory.aml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<topic id="338391aa-2c40-45b6-b5ad-05e86b91bfa9" revisionNumber="1"> + <developerConceptualDocument xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5" xmlns:xlink="http://www.w3.org/1999/xlink"> + <introduction> + <para>The topics in this section describe the various changes made to the [TODO: Project Title] over the +life of the project.</para> + </introduction> + + <section> + <title>Version History</title> + <content> + <para>Select a version below to see a description of its changes.</para> + + <list class="bullet"> + <listItem> + <para><link xlink:href="38a7c3b2-7174-4466-b95b-e952cdc29c3f" /></para> + </listItem> + + <listItem> + <para>[TODO: Add links to each specific version page]</para> + </listItem> + + </list> + + </content> + </section> + + <relatedTopics> + <link xlink:href="0f61f11a-7ea4-4d45-b34a-1f31a2c77a5a" /> + </relatedTopics> + + </developerConceptualDocument> +</topic> diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Content/VersionHistory/v1.0.0.0.aml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Content/VersionHistory/v1.0.0.0.aml new file mode 100644 index 000000000..b02746331 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Content/VersionHistory/v1.0.0.0.aml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<topic id="38a7c3b2-7174-4466-b95b-e952cdc29c3f" revisionNumber="1"> + <developerConceptualDocument xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5" xmlns:xlink="http://www.w3.org/1999/xlink"> + <introduction> + <para>Version [TODO: Version] was released on [TODO: Date]. + </para> + </introduction> + + <section> + <title>Changes in This Release</title> + <content> + + <list class="bullet"> + <listItem> + <para>[TODO: Add change items here]</para> + </listItem> + + </list> + + </content> + </section> + + <relatedTopics> + <link xlink:href="338391aa-2c40-45b6-b5ad-05e86b91bfa9" /> + </relatedTopics> + + </developerConceptualDocument> +</topic> diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Content/Welcome.aml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Content/Welcome.aml new file mode 100644 index 000000000..eb51fa37f --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Content/Welcome.aml @@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="utf-8"?> +<topic id="0f61f11a-7ea4-4d45-b34a-1f31a2c77a5a" revisionNumber="1"> + <developerConceptualDocument xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5" xmlns:xlink="http://www.w3.org/1999/xlink"> + + <section> + <title>Welcome</title> + <content> + <para>Welcome to the Tango FSE Procedures API documentation.</para> + <para>This document contains a detailed description of each part of the API with some examples.</para> + + <para> + The best way to start exploring the main capabilities of a procedure is the <codeEntityReference>T:Tango.FSE.Procedures.IProcedureContext</codeEntityReference> interface. + </para> + </content> + </section> + + <section> + <title>What is a Procedure ?</title> + <content> + <list class="bullet"> + <listItem> + <para> + A procedure is a sequence of a pre-programmed actions/commands. + </para> + </listItem> + + <listItem> + <para> + A procedure can have one or more user input parameters. + </para> + </listItem> + + <listItem> + <para> + A procedure can have one or more output values. + </para> + </listItem> + </list> + + <para> + Procedures are not legacy “stubs”. They are a completely different creature. + <lineBreak/> + Procedures are a way for providing immediate and dynamic response for almost any scenario or issue that is being raised by customers/technicians. + <lineBreak/> + Procedure programmers can leverage an intuitive and extensive API and accomplish almost any requirement. + <lineBreak/> + Programming and managing Procedures is done through the “Procedure Designer” module. + </para> + </content> + </section> + + <section> + <title>The Procedure Designer</title> + <content> + <para> + The procedure designer module can be seen as a fully fledged IDE (Integrated Development Environment), designed to provide the best development experience for the “procedure programmer”. + </para> + <para> + The procedure designer is basically a project editor. In the technical sense, a “Procedure” is basically a project. + </para> + <para> + A project can be composed of one or more script files that are automatically linked together. + </para> + <para> + A project, is represented as a single file (.pproj) and is designed to encapsulate all the files and information about the project. + </para> + <para> + A project can have multiple assembly references and make use of them. An assembly reference can be any DLL file from the entire .NET framework, or from Twine’s libraries. + </para> + <para> + A “Procedure” in the “Procedures Module” is actually a single published procedure project. + <lineBreak/> + A published procedure is a project that is saved on Twine’s global database (per environment). + <lineBreak/> + Each published project has a name, description and version, while preserving the history of the last 10 versions. + <lineBreak/> + When the user navigates to the Procedures Module, Tango FSE will retrieves all published projects from the server. + </para> + <para> + Accessing the procedure designer module requires the “Run Procedure Designer” permission. + <lineBreak/> + Publishing a procedure project requires the “Publish Procedure Projects” permission. + </para> + + <legacyBold> + <legacyUnderline> + Summery: + </legacyUnderline> + </legacyBold> + + <list class="bullet"> + <listItem> + <para> + Published procedure projects are intended for all users and can be executed using the Procedures Module. + </para> + </listItem> + + <listItem> + <para> + Procedure Project files are intended for Twine’s programmers and technicians and can be executed/edited using the Procedure Designer. + </para> + </listItem> + </list> + + <para> + We can say that once a procedure project that is being designed for end-users, is well-tested and optimized, it can be published and be available as a “Procedure” for end-users. + </para> + </content> + </section> + </developerConceptualDocument> +</topic> diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/ContentLayout.content b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/ContentLayout.content new file mode 100644 index 000000000..b1d5a0239 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/ContentLayout.content @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<Topics> + <Topic id="0f61f11a-7ea4-4d45-b34a-1f31a2c77a5a" visible="True" isDefault="true" isSelected="true" title="Tango FSE Procedures API"> + <HelpKeywords> + <HelpKeyword index="K" term="Welcome" /> + </HelpKeywords> + </Topic> +</Topics>
\ No newline at end of file diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Help/proc-doc.chm b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Help/proc-doc.chm Binary files differnew file mode 100644 index 000000000..1fb821112 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Help/proc-doc.chm diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Help/proc-doc.chw b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Help/proc-doc.chw Binary files differnew file mode 100644 index 000000000..f3921f60d --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Help/proc-doc.chw diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Tango.FSE.Procedures.Documentation.shfbproj b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Tango.FSE.Procedures.Documentation.shfbproj new file mode 100644 index 000000000..abb8a83f9 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Tango.FSE.Procedures.Documentation.shfbproj @@ -0,0 +1,229 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <!-- The configuration and platform will be used to determine which assemblies to include from solution and + project documentation sources --> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>12ec97a5-c3c2-40cb-b658-ec4018b89269</ProjectGuid> + <SHFBSchemaVersion>2017.9.26.0</SHFBSchemaVersion> + <!-- AssemblyName, Name, and RootNamespace are not used by SHFB but Visual Studio adds them anyway --> + <AssemblyName>Tango.FSE.Procedures.Documentation</AssemblyName> + <RootNamespace>Tango.FSE.Procedures.Documentation</RootNamespace> + <Name>Tango.FSE.Procedures.Documentation</Name> + <!-- SHFB properties --> + <FrameworkVersion>.NET Framework 4.6.1</FrameworkVersion> + <OutputPath>.\Help\</OutputPath> + <HtmlHelpName>proc-doc</HtmlHelpName> + <Language>en-US</Language> + <TransformComponentArguments> + <Argument Key="logoFile" Value="machine_48px.png" /> + <Argument Key="logoHeight" Value="" /> + <Argument Key="logoWidth" Value="" /> + <Argument Key="logoAltText" Value="" /> + <Argument Key="logoPlacement" Value="left" /> + <Argument Key="logoAlignment" Value="left" /> + <Argument Key="maxVersionParts" Value="" /> + <Argument Key="defaultLanguage" Value="cs" /> + <Argument Key="includeEnumValues" Value="true" /> + <Argument Key="baseSourceCodeUrl" Value="" /> + <Argument Key="requestExampleUrl" Value="" /> + </TransformComponentArguments> + <DocumentationSources> + <DocumentationSource sourceFile="..\Tango.FSE.Procedures\Tango.FSE.Procedures.csproj" /> + <DocumentationSource sourceFile="..\..\Tango.FSE.Common\Tango.FSE.Common.csproj" /> + <DocumentationSource sourceFile="..\..\..\PPC\Tango.PPC.Shared\Tango.PPC.Shared.csproj" /> + </DocumentationSources> + <HelpFileFormat>HtmlHelp1</HelpFileFormat> + <SyntaxFilters>C#</SyntaxFilters> + <PresentationStyle>VS2013</PresentationStyle> + <CleanIntermediates>True</CleanIntermediates> + <KeepLogFile>True</KeepLogFile> + <DisableCodeBlockComponent>False</DisableCodeBlockComponent> + <IndentHtml>False</IndentHtml> + <BuildAssemblerVerbosity>OnlyWarningsAndErrors</BuildAssemblerVerbosity> + <SaveComponentCacheCapacity>100</SaveComponentCacheCapacity> + <HelpTitle>Tango FSE Procedures API</HelpTitle> + <HelpFileVersion>1.0.0.0</HelpFileVersion> + <NamingMethod>MemberName</NamingMethod> + <ContentPlacement>AboveNamespaces</ContentPlacement> + <RootNamespaceContainer>False</RootNamespaceContainer> + <NamespaceGrouping>False</NamespaceGrouping> + <MaximumGroupParts>2</MaximumGroupParts> + <Preliminary>False</Preliminary> + <SdkLinkTarget>Blank</SdkLinkTarget> + <VisibleItems>InheritedMembers, InheritedFrameworkMembers, ProtectedInternalAsProtected, NonBrowsable</VisibleItems> + <ApiFilter> + <Filter entryType="Namespace" fullName="" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Authentication" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Authorization" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.AutoComplete" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Behaviors" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.BugReporting" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Connection" isExposed="False"> + <Filter entryType="Enumeration" fullName="Tango.FSE.Common.Connection.MachineConnectionTypes" filterName="MachineConnectionTypes" isExposed="True" /> +</Filter> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Connectivity" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Console" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Controls" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Converters" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Core" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.DemoMode" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Diagnostics" isExposed="False"> + <Filter entryType="Class" fullName="Tango.FSE.Common.Diagnostics.DiagnosticsPackage" filterName="DiagnosticsPackage" isExposed="True"> + <Filter entryType="Property" fullName="Tango.FSE.Common.Diagnostics.DiagnosticsPackage.Frame" filterName="Frame" isExposed="False" /> + <Filter entryType="Property" fullName="Tango.FSE.Common.Diagnostics.DiagnosticsPackage.MonitorsProperties" filterName="MonitorsProperties" isExposed="False" /> + </Filter> +</Filter> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Dialogs" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Events" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.EventTriggerActions" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.FileAssociation" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.FileSystem" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Firmware" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.FSEApplication" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Graphs" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Helpers" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Logging" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.MachineUpdates" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Modules" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Navigation" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Notifications" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Performance" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.RemoteDesktop" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.RemoteJob" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.RemoteUpgrade" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Resolution" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Storage" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.SystemInfo" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Threading" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Tiles" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.Updates" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Common.WindowsManager" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures" isExposed="False"> + <Filter entryType="Enumeration" fullName="Tango.FSE.Procedures.ArrayParsingStyle" filterName="ArrayParsingStyle" isExposed="True" /> + <Filter entryType="Interface" fullName="Tango.FSE.Procedures.IDialogController" filterName="IDialogController" isExposed="True" /> + <Filter entryType="Interface" fullName="Tango.FSE.Procedures.IProcedureContext" filterName="IProcedureContext" isExposed="True"> + <Filter entryType="Method" fullName="Tango.FSE.Procedures.IProcedureContext.BreakPoint" filterName="BreakPoint" isExposed="False" /> + <Filter entryType="Property" fullName="Tango.FSE.Procedures.IProcedureContext.Results" filterName="Results" isExposed="False" /> + <Filter entryType="Event" fullName="Tango.FSE.Procedures.IProcedureContext.BreakPointRequest" filterName="BreakPointRequest" isExposed="False" /> + <Filter entryType="Event" fullName="Tango.FSE.Procedures.IProcedureContext.Progress" filterName="Progress" isExposed="False" /> + </Filter> + <Filter entryType="Class" fullName="Tango.FSE.Procedures.Result" filterName="Result" isExposed="True"> + <Filter entryType="Property" fullName="Tango.FSE.Procedures.Result.IsBitmap" filterName="IsBitmap" isExposed="False" /> + <Filter entryType="Property" fullName="Tango.FSE.Procedures.Result.IsGraph" filterName="IsGraph" isExposed="False" /> + <Filter entryType="Property" fullName="Tango.FSE.Procedures.Result.IsValueArray" filterName="IsValueArray" isExposed="False" /> + </Filter> + <Filter entryType="Enumeration" fullName="Tango.FSE.Procedures.ResultType" filterName="ResultType" isExposed="True" /> + <Filter entryType="Class" fullName="Tango.FSE.Procedures.UserInput" filterName="UserInput" isExposed="True" /> +</Filter> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Contracts" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Controls" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Converters" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.CSV" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Designer" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Dialogs" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Examples.AddResult" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Examples.Connection" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Examples.Csv" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Examples.Diagnostics" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Examples.RequestUserInputFor" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Examples.Send" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Examples.SendContinuous" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Examples.Sql" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Helpers" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Messages" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Navigation" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.ViewModels" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Views" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Windows" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.PPC.Shared.Information" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.PPC.Shared.Jobs" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.PPC.Shared.Logs" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.PPC.Shared.Performance" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.PPC.Shared.RemoteUpgrade" isExposed="False" /> +<Filter entryType="Namespace" fullName="Tango.PPC.Shared.SQL" isExposed="True"> + <Filter entryType="Class" fullName="Tango.PPC.Shared.SQL.ExecuteSqlRequest" filterName="ExecuteSqlRequest" isExposed="False" /> + <Filter entryType="Class" fullName="Tango.PPC.Shared.SQL.ExecuteSqlResponse" filterName="ExecuteSqlResponse" isExposed="False" /> +</Filter> +<Filter entryType="Namespace" fullName="Tango.PPC.Shared.Updates" isExposed="False" /> +<Filter entryType="Namespace" fullName="" isExposed="False" /></ApiFilter> + <ProjectSummary>This is the global summery for the help file.</ProjectSummary> + <NamespaceSummaries> + <NamespaceSummaryItem name="(global)" isDocumented="True">This is the global summery for the help file.</NamespaceSummaryItem> + <NamespaceSummaryItem name="Tango.FSE.Procedures" isDocumented="True">Contains a collection of interfaces and classes for the main procedures API.</NamespaceSummaryItem> + </NamespaceSummaries> + <ComponentConfigurations> + </ComponentConfigurations> + <MissingTags>Summary, Parameter, Returns, AutoDocumentCtors, TypeParameter, AutoDocumentDispose</MissingTags> + </PropertyGroup> + <!-- There are no properties for these groups. AnyCPU needs to appear in order for Visual Studio to perform + the build. The others are optional common platform types that may appear. --> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' "> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' "> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|Win32' "> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|Win32' "> + </PropertyGroup> + <ItemGroup> + <Folder Include="Content" /> + <Folder Include="Content\VersionHistory" /> + <Folder Include="icons" /> + <Folder Include="media" /> + </ItemGroup> + <ItemGroup> + <None Include="Content\VersionHistory\v1.0.0.0.aml" /> + <None Include="Content\VersionHistory\VersionHistory.aml" /> + <None Include="Content\Welcome.aml" /> + </ItemGroup> + <ItemGroup> + <ContentLayout Include="ContentLayout.content" /> + </ItemGroup> + <ItemGroup> + <Image Include="icons\Help.png"> + <ImageId>Help</ImageId> + <AlternateText>Help</AlternateText> + </Image> + </ItemGroup> + <ItemGroup> + <Content Include="icons\machine_48px.png"> + <ImageId>machine_48px</ImageId> + <AlternateText>machine 48px</AlternateText> + </Content> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\Tango.FSE.Common\Tango.FSE.Common.csproj"> + <Name>Tango.FSE.Common</Name> + <Project>{bc37cccb-7392-4f78-8d1c-e9629e6e046e}</Project> + <Private>False</Private> + </ProjectReference> + <ProjectReference Include="..\Tango.FSE.Procedures\Tango.FSE.Procedures.csproj"> + <Name>Tango.FSE.Procedures</Name> + <Project>{1754f846-4763-4000-807f-c7bfaa145db2}</Project> + <Private>False</Private> + </ProjectReference> + </ItemGroup> + <!-- Import the SHFB build targets --> + <Import Project="$(SHFBROOT)\SandcastleHelpFileBuilder.targets" /> + <!-- The pre-build and post-build event properties must appear *after* the targets file import in order to be + evaluated correctly. --> + <PropertyGroup> + <PreBuildEvent> + </PreBuildEvent> + <PostBuildEvent>copy /Y "$(OutputPath)proc-doc.chm" "$(SolutionDir)Build\FSE\$(Configuration)" +</PostBuildEvent> + <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent> + </PropertyGroup> +</Project>
\ No newline at end of file diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/icons/Help.png b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/icons/Help.png Binary files differnew file mode 100644 index 000000000..945e89fb9 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/icons/Help.png diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/icons/machine_48px.png b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/icons/machine_48px.png Binary files differnew file mode 100644 index 000000000..95cf615c5 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/icons/machine_48px.png diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ArrayParsingStyle.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ArrayParsingStyle.cs index c829a5b21..e731b7672 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ArrayParsingStyle.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ArrayParsingStyle.cs @@ -6,9 +6,19 @@ using System.Threading.Tasks; namespace Tango.FSE.Procedures { + /// <summary> + /// Represents an array parsing style. + /// </summary> public enum ArrayParsingStyle { + /// <summary> + /// Formats the array separated by commas. (e.g 1,2,3,4...) + /// </summary> Comma, + + /// <summary> + /// Each item enclosed in square brackets. (e.g [1] [2] [3]...) + /// </summary> SquareBrackets, } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/BreakPointRequestEventArgs.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/BreakPointRequestEventArgs.cs new file mode 100644 index 000000000..768f4bb0d --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/BreakPointRequestEventArgs.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.Scripting.Basic; +using Tango.Scripting.Core; + +namespace Tango.FSE.Procedures +{ + public class BreakPointRequestEventArgs : EventArgs + { + private Action _releaseAction; + + public int LineNumber { get; set; } + public Script Script { get; set; } + + public List<ScriptBreakPointSymbol> Symbols { get; set; } + + public BreakPointRequestEventArgs(Action releaseAction) + { + Symbols = new List<ScriptBreakPointSymbol>(); + _releaseAction = releaseAction; + } + + public void Release() + { + _releaseAction?.Invoke(); + } + } +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Contracts/IProcedureDesignerView.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Contracts/IProcedureDesignerView.cs index 5797d622a..07899056c 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Contracts/IProcedureDesignerView.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Contracts/IProcedureDesignerView.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using Tango.FSE.Common; +using Tango.Scripting.Basic; +using Tango.Scripting.Core; namespace Tango.FSE.Procedures.Contracts { @@ -21,5 +23,12 @@ namespace Tango.FSE.Procedures.Contracts void ResetColrization(); void HighlightError(int position, int length); void ClearErrors(); + void ScrollToLine(int lineNumber); + void HighlightRuntimeError(int lineNumber); + void CloseRunTimeError(); + List<ScriptBreakPoint> GetBreakPoints(); + void HighlightBreakPointRequest(int lineNumber, List<ScriptBreakPointSymbol> symbols); + void ResetBreakPointRequest(); + String GetCaretWord(); } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Controls/ObjectInTreeView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Controls/ObjectInTreeView.xaml new file mode 100644 index 000000000..d331935c8 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Controls/ObjectInTreeView.xaml @@ -0,0 +1,27 @@ +<UserControl x:Class="Tango.FSE.Procedures.Controls.ObjectInTreeView" + 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:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:local="clr-namespace:Tango.FSE.Procedures.Controls" + mc:Ignorable="d" + d:DesignHeight="450" d:DesignWidth="800" x:Name="ObjectInTreeViewControl"> + <TreeView ItemsSource="{Binding TreeNodes, ElementName=ObjectInTreeViewControl}" Style="{x:Null}" Foreground="{StaticResource FSE_PrimaryForegroundBrush}" Background="Transparent" FontSize="{StaticResource FSE_SmallFontSize}" BorderThickness="0"> + <TreeView.Resources> + <Style TargetType="TreeViewItem" BasedOn="{StaticResource {x:Type TreeViewItem}}"> + + </Style> + <HierarchicalDataTemplate DataType="{x:Type local:TreeNode}" ItemsSource="{Binding Path=Children}"> + <TreeViewItem Style="{x:Null}"> + <TreeViewItem.Header> + <StackPanel Orientation="Horizontal"> + <TextBlock Text="{Binding Path=Name}"/> + <TextBlock Text=" : "/> + <TextBlock Text="{Binding Path=Value}"/> + </StackPanel> + </TreeViewItem.Header> + </TreeViewItem> + </HierarchicalDataTemplate> + </TreeView.Resources> + </TreeView> +</UserControl> diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Controls/ObjectInTreeView.xaml.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Controls/ObjectInTreeView.xaml.cs new file mode 100644 index 000000000..b1d1ee14d --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Controls/ObjectInTreeView.xaml.cs @@ -0,0 +1,50 @@ +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.FSE.Procedures.Controls +{ + /// <summary> + /// Interaction logic for ObjectInTreeView.xaml + /// </summary> + public partial class ObjectInTreeView : UserControl + { + public ObjectInTreeView() + { + InitializeComponent(); + } + + public object ObjectToVisualize + { + get { return (object)GetValue(ObjectToVisualizeProperty); } + set { SetValue(ObjectToVisualizeProperty, value); } + } + public static readonly DependencyProperty ObjectToVisualizeProperty = + DependencyProperty.Register("ObjectToVisualize", typeof(object), typeof(ObjectInTreeView), new PropertyMetadata(null, OnObjectChanged)); + + private static void OnObjectChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + TreeNode tree = TreeNode.CreateTree(e.NewValue); + (d as ObjectInTreeView).TreeNodes = new List<TreeNode>() { tree }; + } + + public List<TreeNode> TreeNodes + { + get { return (List<TreeNode>)GetValue(TreeNodesProperty); } + set { SetValue(TreeNodesProperty, value); } + } + public static readonly DependencyProperty TreeNodesProperty = + DependencyProperty.Register("TreeNodes", typeof(List<TreeNode>), typeof(ObjectInTreeView), new PropertyMetadata(null)); + } +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Controls/TreeNode.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Controls/TreeNode.cs new file mode 100644 index 000000000..47407077f --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Controls/TreeNode.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web.Script.Serialization; + +namespace Tango.FSE.Procedures.Controls +{ + public class TreeNode + { + public string Name { get; set; } + public string Value { get; set; } + public List<TreeNode> Children { get; set; } = new List<TreeNode>(); + + public static TreeNode CreateTree(object obj) + { + if (obj.GetType().IsValueTypeOrString()) + { + return new TreeNode() + { + Name = "Value", + Value = obj.ToStringSafe() + }; + } + else + { + JavaScriptSerializer jss = new JavaScriptSerializer(); + var serialized = Newtonsoft.Json.JsonConvert.SerializeObject(obj); + Dictionary<string, object> dic = jss.Deserialize<Dictionary<string, object>>(serialized); + var root = new TreeNode(); + root.Name = "Root"; + BuildTree(dic, root); + return root; + } + } + + private static void BuildTree(object item, TreeNode node) + { + if (item is KeyValuePair<string, object>) + { + KeyValuePair<string, object> kv = (KeyValuePair<string, object>)item; + TreeNode keyValueNode = new TreeNode(); + keyValueNode.Name = kv.Key; + keyValueNode.Value = GetValueAsString(kv.Value); + node.Children.Add(keyValueNode); + BuildTree(kv.Value, keyValueNode); + } + else if (item is ArrayList) + { + ArrayList list = (ArrayList)item; + int index = 0; + foreach (object value in list) + { + TreeNode arrayItem = new TreeNode(); + arrayItem.Name = $"[{index}]"; + arrayItem.Value = ""; + node.Children.Add(arrayItem); + BuildTree(value, arrayItem); + index++; + } + } + else if (item is Dictionary<string, object>) + { + Dictionary<string, object> dictionary = (Dictionary<string, object>)item; + foreach (KeyValuePair<string, object> d in dictionary) + { + BuildTree(d, node); + } + } + } + + private static string GetValueAsString(object value) + { + if (value == null) + return "null"; + var type = value.GetType(); + if (type.IsArray) + { + return "[]"; + } + + if (value is ArrayList) + { + var arr = value as ArrayList; + return $"[{arr.Count}]"; + } + + if (type.IsGenericType) + { + return "{}"; + } + + return value.ToString(); + } + } +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/DebugNode.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/DebugNode.cs new file mode 100644 index 000000000..81f29c96c --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/DebugNode.cs @@ -0,0 +1,256 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Tango.Core; +using Tango.Core.Commands; + +namespace Tango.FSE.Procedures +{ + public class DebugNode : ExtendedObject + { + private Object _originalValue; + + public String Name { get; set; } + public Object ParentObject { get; set; } + + private Object _value; + public Object Value + { + get { return _value; } + set + { + _value = value; + + if (_originalValue == null) + { + _originalValue = _value; + } + + DisplayValue = GetDisplayValue(_value); + RaisePropertyChangedAuto(); + } + } + + public bool IsSimpleValue { get; set; } + + public String Type { get; set; } + public PropertyInfo PropertyInfo { get; set; } + public FieldInfo FieldInfo { get; set; } + + public Type MemberType + { + get + { + if (PropertyInfo != null) + { + return PropertyInfo.PropertyType; + } + else if (FieldInfo != null) + { + return FieldInfo.FieldType; + } + else + { + return Value.GetType(); + } + } + } + + private bool _isEdited; + public bool IsEdited + { + get { return _isEdited; } + set { _isEdited = value; RaisePropertyChangedAuto(); } + } + + public RelayCommand UpdateValueCommand { get; set; } + + public bool IsEditable + { + get + { + if (IsSimpleValue) + { + return false; //Sorry can't update simple script symbols as they are passed to me by value... + } + + if (MemberType != null && MemberType.IsValueTypeOrString() && ((PropertyInfo != null && PropertyInfo.SetMethod != null) || FieldInfo != null)) + { + return true; + } + + return false; + } + } + + private Object _displayValue; + public Object DisplayValue + { + get { return _displayValue; } + set { _displayValue = value; RaisePropertyChangedAuto(); } + } + + public List<DebugNode> Nodes + { + get + { + return GetChildNodes(); + } + } + + public DebugNode() + { + UpdateValueCommand = new RelayCommand(UpdateValue); + } + + private void UpdateValue() + { + try + { + if (IsSimpleValue && Value != null) + { + _value = Convert.ChangeType(DisplayValue, Value.GetType()); + IsEdited = _originalValue.ToStringSafe() != Value.ToStringSafe(); + } + else if (IsEditable && ((PropertyInfo != null && PropertyInfo.SetMethod != null) || FieldInfo != null)) + { + if (PropertyInfo != null) + { + _value = Convert.ChangeType(DisplayValue, PropertyInfo.PropertyType); + PropertyInfo.SetValue(ParentObject, Value); + IsEdited = _originalValue.ToStringSafe() != Value.ToStringSafe(); + } + else if (FieldInfo != null) + { + _value = Convert.ChangeType(DisplayValue, FieldInfo.FieldType); + FieldInfo.SetValue(ParentObject, Value); + IsEdited = _originalValue.ToStringSafe() != Value.ToStringSafe(); + } + } + } + catch { } + + DisplayValue = GetDisplayValue(Value); + } + + private List<DebugNode> GetChildNodes() + { + List<DebugNode> childNodes = new List<DebugNode>(); + + if (Value == null) return childNodes; + + var type = Value.GetType(); + + if (type.IsValueTypeOrString()) + { + return childNodes; + } + + if (typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(String)) + { + List<Object> list = (Value as IEnumerable).Cast<Object>().ToList(); + + for (int i = 0; i < list.Count; i++) + { + var item = list[i]; + + DebugNode listNode = new DebugNode(); + listNode.Name = $"[{i}]"; + listNode.Value = item; + listNode.Type = GetFriendlyName(item.GetType()); + childNodes.Add(listNode); + } + } + else + { + foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)) + { + DebugNode propNode = new DebugNode(); + propNode.Type = GetFriendlyName(prop.PropertyType); + propNode.Name = prop.Name; + propNode.Value = prop.GetValue(Value); + propNode.PropertyInfo = prop; + propNode.ParentObject = Value; + childNodes.Add(propNode); + } + + foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public)) + { + DebugNode propNode = new DebugNode(); + propNode.Type = GetFriendlyName(field.FieldType); + propNode.Name = field.Name; + propNode.Value = field.GetValue(Value); + propNode.FieldInfo = field; + propNode.ParentObject = Value; + childNodes.Add(propNode); + } + } + + return childNodes; + } + + public static DebugNode CreateNode(String name, Object obj) + { + DebugNode node = new DebugNode(); + node.Name = name; + node.Value = obj; + node.Type = GetFriendlyName(obj.GetType()); + + if (obj.GetType().IsValueTypeOrString()) + { + node.IsSimpleValue = true; + } + + return node; + } + + private static String GetFriendlyName(Type type) + { + if (type == typeof(int)) + return "int"; + else if (type == typeof(short)) + return "short"; + else if (type == typeof(byte)) + return "byte"; + else if (type == typeof(bool)) + return "bool"; + else if (type == typeof(long)) + return "long"; + else if (type == typeof(float)) + return "float"; + else if (type == typeof(double)) + return "double"; + else if (type == typeof(decimal)) + return "decimal"; + else if (type == typeof(string)) + return "string"; + else if (type.IsGenericType) + return type.Name.Split('`')[0] + "<" + string.Join(", ", type.GetGenericArguments().Select(x => GetFriendlyName(x)).ToArray()) + ">"; + else + return type.Name; + } + + private static Object GetDisplayValue(Object value) + { + if (value == null) + { + return "null"; + } + else if (value.GetType().IsValueTypeOrString()) + { + return value; + } + else if (typeof(IEnumerable).IsAssignableFrom(value.GetType())) + { + List<Object> list = (value as IEnumerable).Cast<Object>().ToList(); + return $"Count = {list.Count}"; + } + + return value.ToStringSafe(); + } + } +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Dialogs/BreakPointValueDialogView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Dialogs/BreakPointValueDialogView.xaml new file mode 100644 index 000000000..0c96cf33e --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Dialogs/BreakPointValueDialogView.xaml @@ -0,0 +1,24 @@ +<UserControl x:Class="Tango.FSE.Procedures.Dialogs.BreakPointValueDialogView" + 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:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes" + xmlns:local="clr-namespace:Tango.FSE.Procedures.Dialogs" + mc:Ignorable="d" + Width="700" Height="500" d:DataContext="{d:DesignInstance Type=local:BreakPointValueDialogViewVM, IsDesignTimeCreatable=False}" Background="{StaticResource FSE_PrimaryBackgroundLightBrush}" Foreground="{StaticResource FSE_PrimaryForegroundBrush}"> + <Grid Margin="10"> + <DockPanel> + <StackPanel DockPanel.Dock="Top" Orientation="Horizontal"> + <material:PackIcon Kind="Cube" Width="32" Height="32" /> + <TextBlock Margin="10 0 0 0" FontSize="{StaticResource FSE_LargeFontSize}" VerticalAlignment="Center" Text="{Binding Name}"></TextBlock> + </StackPanel> + + <CheckBox DockPanel.Dock="Bottom" Margin="0 10 0 0" IsChecked="{Binding AsJson}">Display in JSON format</CheckBox> + + <Border Margin="0 20 0 0" BorderThickness="1" BorderBrush="{StaticResource FSE_BorderBrush}" Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}" CornerRadius="5"> + <TextBox Style="{StaticResource FSE_Rounded_Corners_TextBox_Multiline}" FontSize="{StaticResource FSE_SmallFontSize}" Foreground="{StaticResource FSE_GrayBrush}" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" IsReadOnly="True" Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}" Text="{Binding DisplayValue,Mode=OneWay}"></TextBox> + </Border> + </DockPanel> + </Grid> +</UserControl> diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Dialogs/BreakPointValueDialogView.xaml.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Dialogs/BreakPointValueDialogView.xaml.cs new file mode 100644 index 000000000..ec496e7fc --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Dialogs/BreakPointValueDialogView.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.FSE.Procedures.Dialogs +{ + /// <summary> + /// Interaction logic for BreakPointValueDialogView.xaml + /// </summary> + public partial class BreakPointValueDialogView : UserControl + { + public BreakPointValueDialogView() + { + InitializeComponent(); + } + } +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Dialogs/BreakPointValueDialogViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Dialogs/BreakPointValueDialogViewVM.cs new file mode 100644 index 000000000..13d952ffd --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Dialogs/BreakPointValueDialogViewVM.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Tango.Core.ExtensionMethods; +using Tango.FSE.Common; + +namespace Tango.FSE.Procedures.Dialogs +{ + public class BreakPointValueDialogViewVM : FSEDialogViewVM + { + public String Name { get; set; } + + public Object DebugObject { get; set; } + + private bool _asJson; + public bool AsJson + { + get { return _asJson; } + set { _asJson = value; RaisePropertyChangedAuto(); RaisePropertyChanged(nameof(DisplayValue)); } + } + + public BreakPointValueDialogViewVM(String name, Object debugObject) + { + AutoMode = false; + CanCancel = true; + CanClose = true; + Name = name; + DebugObject = debugObject; + } + + public String DisplayValue + { + get + { + if (DebugObject != null) + { + if (AsJson) + { + try + { + return DebugObject.ToJsonString(); + } + catch + { + return DebugObject.ToString(); + } + } + else + { + return DebugObject.ToString(); + } + } + + return "null"; + } + } + } +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/AddResult/Program.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/AddResult/Program.cs new file mode 100644 index 000000000..750dd436d --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/AddResult/Program.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Drawing; +using Google.Protobuf; +using Tango.BL.Entities; +using Tango.BL.Enumerations; +using Tango.PMR.Stubs; +using Tango.PMR.Diagnostics; +using Tango.FSE.Common.Connection; +using Tango.FSE.Common.Diagnostics; +using Tango.FSE.Procedures; + +namespace Tango.FSE.Procedures.Examples.AddResult +{ + #region Example + public class Program + { + public class Person + { + public int Age; + + //Here we use the Description attribute in order to set a custom name + //for this field at the results grid and csv file. + [Description("First Name")] + public String Name; + } + + public void OnExecute(IProcedureContext context) + { + //Add scalar result with the value 10. + context.AddResult(ResultType.Passed, "My Scalar Result", 10); + + //Initialize a list of persons. + List<Person> persons = new List<Person>(); + + for (int i = 0; i < 100; i++) + { + Person p = new Person(); + p.Age = i; + p.Name = "Name " + i; + persons.Add(p); + } + + //Add the persons list as a result. + //This will allow the user to display a grid with all the persons in the list. + //The list can also be exported to CSV. + context.AddResult(ResultType.Passed, "Persons", persons); + + //Here is how we can get only the ages from the persons list and cast them as double values. + List<double> ages = persons.Select(x => x.Age).Cast<double>().ToList(); + + //Adding the ages list as a result.. + context.AddResult(ResultType.Passed, "Ages", ages); + + //Plotting the ages list as a graph result.. + context.AddGraphResult(ResultType.Passed, "Ages Graph", ages); + + + + //And here is how we can draw a completely customized result by drawing a bitmap. + Bitmap bitmap = context.CreateBitmap(400, 200); + Graphics g = context.GetDrawingContext(bitmap); + + //Draw a simple rectangle border. + g.DrawRectangle(Pens.Gray, 0, 0, 399, 199); + + //Draw a diagonal red line across the rectangle. + g.DrawLine(Pens.Red, 0, 0, 400, 200); + + //Draw a string at the center of the rect. + Font font = new Font("Tahoma", 24, FontStyle.Bold, GraphicsUnit.Pixel); + RectangleF layout = new RectangleF(0, 0, 400, 200); + StringFormat format = new StringFormat(); + format.Alignment = StringAlignment.Center; + format.LineAlignment = StringAlignment.Center; + g.DrawString("Bitmap Result", font, Brushes.YellowGreen, layout, format); + + //Dispose the drawing context after you done drawing. + g.Dispose(); + + context.AddBitmapResult(ResultType.Passed, "Bitmap Result", bitmap); + } + } + #endregion +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Connection/Program.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Connection/Program.cs new file mode 100644 index 000000000..63c8cc5f7 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Connection/Program.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Drawing; +using Google.Protobuf; +using Tango.BL.Entities; +using Tango.BL.Enumerations; +using Tango.PMR.Stubs; +using Tango.PMR.Diagnostics; +using Tango.FSE.Common.Connection; +using Tango.FSE.Common.Diagnostics; +using Tango.FSE.Procedures; + +namespace Tango.FSE.Procedures.Examples.Connection +{ + #region Example + public class Program + { + public void OnExecute(IProcedureContext context) + { + + if (context.IsConnected) //Check if there is an active machine connection. + { + if (context.ConnectionType == MachineConnectionTypes.Wifi) //Check enumeration of the active connection type. + { + context.WriteLine("Machine " + context.ConnectedMachine.SerialNumber + " is connected via WiFi."); + } + } + + } + } + #endregion +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Csv/Program.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Csv/Program.cs new file mode 100644 index 000000000..70526436b --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Csv/Program.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Drawing; +using Google.Protobuf; +using Tango.BL.Entities; +using Tango.BL.Enumerations; +using Tango.PMR.Stubs; +using Tango.PMR.Diagnostics; +using Tango.FSE.Common.Connection; +using Tango.FSE.Common.Diagnostics; +using Tango.FSE.Procedures; + +namespace Tango.FSE.Procedures.Examples.Csv +{ + #region Example + //CSV Model must contain properties, not plain fields. + public class Person + { + public String Name { get; set; } + public int Age { get; set; } + } + + public class Program + { + //This example demonstrates CSV file writing and reading. + //The csv reading method also accepts a byte array so you can also read a CSV file from resource. + public void OnExecute(IProcedureContext context) + { + //Initialize a list of person model (see Person.csx). + List<Person> persons = new List<Person>(); + + for (int i = 0; i < 100; i++) + { + Person p = new Person(); + p.Name = "Person " + i; + p.Age = i; + persons.Add(p); + } + + //Request the user to choose the csv file location. + String csvFile = context.RequestFileSave("Select CSV File Location", "*.csv", "persons.csv"); + + if (csvFile != null) //Check if user selected a file and pressed 'OK'. + { + //Write the persons list to the selected csv file. + context.WriteCsv<Person>(csvFile, persons); + + //Test csv file for reading... + persons = context.ReadCsv<Person>(csvFile); + + //Write the reading result to the output window. + context.WriteLine(persons); + } + } + } + #endregion +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Diagnostics/Program.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Diagnostics/Program.cs new file mode 100644 index 000000000..c783abaa1 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Diagnostics/Program.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Drawing; +using Google.Protobuf; +using Tango.BL.Entities; +using Tango.BL.Enumerations; +using Tango.PMR.Stubs; +using Tango.PMR.Diagnostics; +using Tango.FSE.Common.Connection; +using Tango.FSE.Common.Diagnostics; +using Tango.FSE.Procedures; + +namespace Tango.FSE.Procedures.Examples.Diagnostics +{ + #region Example + public class Program + { + public void OnExecute(IProcedureContext context) + { + //Initialize a new list of dancer angle values. + List<double> dancerValues = new List<double>(); + + //Collect 100 samples of the middle dancer angle. + for (int i = 0; i < 100; i++) + { + //Get the current diagnostics package. (set 'true' to block the execution until a fresh diagnostics frame arrives) + DiagnosticsPackage package = context.GetDiagnosticsPackage(true); + + //Add the last value in the array to the list. + dancerValues.Add(package.GetMonitorLastValue(TechMonitors.Dancer2Angle)); + } + + //Plot the dancer samples to a graph result. + context.AddGraphResult(ResultType.Passed, "Dancer Angle", dancerValues); + } + } + #endregion +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/RequestUserInputFor/Program.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/RequestUserInputFor/Program.cs new file mode 100644 index 000000000..3ea277bf8 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/RequestUserInputFor/Program.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Drawing; +using Google.Protobuf; +using Tango.BL.Entities; +using Tango.BL.Enumerations; +using Tango.PMR.Stubs; +using Tango.PMR.Diagnostics; +using Tango.FSE.Common.Connection; +using Tango.FSE.Common.Diagnostics; +using Tango.FSE.Procedures; + +namespace Tango.FSE.Procedures.Examples.RequestUserInputFor +{ + #region Example + public enum PersonType + { + Student = 0, + Employee = 1, + Guest = 2 + } + + public class Person + { + public int Age; + public String name; + public bool Activated; + public PersonType Type = PersonType.Employee; + } + + public class Program + { + public void OnExecute(IProcedureContext context) + { + + Person p = context.RequestUserInputFor<Person>("Input Demo", "Please fill in the form"); + context.WriteLine(p); + + } + } + #endregion +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Send/Program.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Send/Program.cs new file mode 100644 index 000000000..bf7c38e13 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Send/Program.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Drawing; +using Google.Protobuf; +using Tango.BL.Entities; +using Tango.BL.Enumerations; +using Tango.PMR.Stubs; +using Tango.PMR.Diagnostics; +using Tango.FSE.Common.Connection; +using Tango.FSE.Common.Diagnostics; +using Tango.FSE.Procedures; + +namespace Tango.FSE.Procedures.Examples.Send +{ + #region Example + public class Program + { + public void OnExecute(IProcedureContext context) + { + //Sends a request by providing the request name and comma separated arguments. + CalculateResponse response1 = context.Send<CalculateResponse>("CalculateRequest", 10, 5); + + //Sends a request by providing the request short name and comma separated arguments. + CalculateResponse response2 = context.Send<CalculateResponse>("calculate", 10, 5); + + //Sends a request by providing the request short name, a timeout and comma separated arguments. + CalculateResponse response3 = context.Send<CalculateResponse>("calculate", TimeSpan.FromSeconds(5), 10, 5); + + //Sends a request by providing a request object. + CalculateRequest request = new CalculateRequest(); + request.A = 10; + request.B = 5; + + CalculateResponse response4 = context.Send<CalculateResponse>(request); + + //Sends a request by providing a request object and 5 seconds timeout. + CalculateResponse response5 = context.Send<CalculateResponse>(request, 5); + } + } + #endregion +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/SendContinuous/Program.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/SendContinuous/Program.cs new file mode 100644 index 000000000..2f8115642 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/SendContinuous/Program.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Drawing; +using Google.Protobuf; +using Tango.BL.Entities; +using Tango.BL.Enumerations; +using Tango.PMR.Stubs; +using Tango.PMR.Diagnostics; +using Tango.FSE.Common.Connection; +using Tango.FSE.Common.Diagnostics; +using Tango.FSE.Procedures; + +namespace Tango.FSE.Procedures.Examples.SendContinuous +{ + #region Example + public class Program + { + private IProcedureContext _context; + + public void OnExecute(IProcedureContext context) + { + //Sends a continuous request by providing the request name, a callback method and comma separated arguments. + context.SendContinuous<ProgressResponse>("ProgressRequest", (response) => + { + //Each response will be provided here... + context.WriteLine(response); + }, 100, 10); + + + //Sends a continuous request by providing the request name, a callback method, a timeout, and comma separated arguments. + context.SendContinuous<ProgressResponse>("progress", (response) => + { + context.WriteLine(response); + }, TimeSpan.FromSeconds(5), 100, 10); + + + //Sends a continuous request by providing the request object, a callback method and a 5 seconds timeout. + ProgressRequest request = new ProgressRequest(); + request.Amount = 100; + request.Delay = 10; + + context.SendContinuous<ProgressResponse>(request, (response) => + { + context.WriteLine(response); + }, 5); + + + //Sends a continuous request by providing the request object, a defined callback method and a 5 seconds timeout. + //We need to store the context globally so the callback method will have access to it. + _context = context; + context.SendContinuous<ProgressResponse>(request, OnProgressResponse, 5); + } + + private void OnProgressResponse(ProgressResponse response) + { + _context.WriteLine(response); + } + } + #endregion +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Sql/Program.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Sql/Program.cs new file mode 100644 index 000000000..33072a2dd --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Sql/Program.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Drawing; +using Google.Protobuf; +using Tango.BL.Entities; +using Tango.BL.Enumerations; +using Tango.PMR.Stubs; +using Tango.PMR.Diagnostics; +using Tango.FSE.Common.Connection; +using Tango.FSE.Common.Diagnostics; +using Tango.FSE.Procedures; + +namespace Tango.FSE.Procedures.Examples.Sql +{ + #region Example + //Additional name spaces that are required for working with remote SQL. + using Tango.FSE.Common.SQL; + using Tango.PPC.Shared.SQL; + + public class Program + { + public void OnExecute(IProcedureContext context) + { + + //Get the remote SQL provider from the Tango IOC container. + IRemoteSqlProvider sql = context.GetService<IRemoteSqlProvider>(); + + //Create the SQL command and direct it to affect both the local machine's database and the global Twine's database. + RemoteSqlCommand command = new RemoteSqlCommand(); + command.SQL = "UPDATE MACHINES SET IS_DEMO = 0 WHERE SERIAL_NUMBER = '" + context.ConnectedMachine.SerialNumber + "'"; + command.Mode = RemoteSqlCommandMode.Both; + + //Execute the command... + RemoteSqlCommandResult result = sql.ExecuteSqlCommand(command); + + context.WriteLine("Command executed."); + context.WriteLine("Local Affected Rows " + result.LocalAffectedRecords); + context.WriteLine("Global Affected Rows " + result.GlobalAffectedRecords); + + context.WriteLine(""); + + //Query the machine's database for all the jobs names and length. + command.SQL = "SELECT NAME,LAST_RUN FROM JOBS"; + command.Mode = RemoteSqlCommandMode.Local; + + result = sql.ExecuteSqlCommand(command); + + //Check for errors. + if (!result.HasLocalError) + { + //Write the formatted results set to the console. + context.WriteLine(result.LocalDatSet.ToTableString()); + context.WriteLine(""); + + //Iterate over the data set rows and get values. + foreach (RemoteSqlRow row in result.LocalDatSet.Rows) + { + String name = row.Get<String>("NAME"); + String lastRun = row.Get<String>("LAST_RUN"); + + context.WriteLine(String.Format("NAME {0}, LAST_RUN {1}", name, lastRun)); + } + } + else + { + //Write the error to console. + context.WriteLine(result.LocalError); + } + } + } + #endregion +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ExtensionMethods/TreeViewExtensions.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ExtensionMethods/TreeViewExtensions.cs new file mode 100644 index 000000000..0859affc2 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ExtensionMethods/TreeViewExtensions.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Controls; + +public static class TreeViewExtensions +{ + public static void SetExpansion(this TreeView treeView, bool isExpanded) => SetExpansion((ItemsControl)treeView, isExpanded); + + private static void SetExpansion(ItemsControl parent, bool isExpanded) + { + if (parent == null) return; + + if (parent is TreeViewItem tvi) + tvi.IsExpanded = isExpanded; + + if (parent.HasItems) + foreach (var item in parent.Items.Cast<object>() + .Select(i => GetTreeViewItem(parent, i, isExpanded))) + SetExpansion(item, isExpanded); + } + + private static TreeViewItem GetTreeViewItem(ItemsControl parent, object item, bool isExpanded) + { + if (item is TreeViewItem tvi) + return tvi; + + var result = ContainerFromItem(parent, item); + if (result == null && isExpanded) + { + parent.UpdateLayout(); + result = ContainerFromItem(parent, item); + } + return result; + } + + static TreeViewItem ContainerFromItem(ItemsControl parent, object item) => (TreeViewItem)parent.ItemContainerGenerator.ContainerFromItem(item); +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Helpers/ProcedureExceptionHelper.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Helpers/ProcedureExceptionHelper.cs new file mode 100644 index 000000000..7a57681e7 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Helpers/ProcedureExceptionHelper.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Tango.FSE.Procedures.Helpers +{ + public static class ProcedureExceptionHelper + { + public static int? GetExceptionLineNumber(Exception ex, ProcedureProject project) + { + try + { + Regex regex = new Regex(@"OnExecute\(IProcedureContext context\) in :line (\d+)"); + var matches = regex.Matches(ex.ToString()).OfType<Match>().ToList(); + + if (matches.Count > 0) + { + var match = matches.First(); + + if (match.Groups.Count > 1) + { + var line = match.Groups[1].Value; + int lineNumber = int.Parse(line) - project.Scripts.Count + 1; + return lineNumber; + } + } + } + catch { } + + return null; + } + } +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/IDialogController.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/IDialogController.cs index 50865e66d..9462330d6 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/IDialogController.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/IDialogController.cs @@ -7,10 +7,27 @@ using System.Windows; namespace Tango.FSE.Procedures { + /// <summary> + /// Represents a procedure dialog controller. + /// </summary> public interface IDialogController { + /// <summary> + /// Opens the dialog. + /// </summary> void Show(); + + /// <summary> + /// Finds the dialog control of type T. + /// </summary> + /// <typeparam name="T">Type of expected control.</typeparam> + /// <param name="name">The name of the control in terms of 'x:Name="..."'.</param> + /// <returns>Returns the control if it was found.</returns> T FindControl<T>(String name) where T : DependencyObject; + + /// <summary> + /// Closes the dialog. + /// </summary> void Close(); } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/IProcedureContext.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/IProcedureContext.cs index e93311d80..829da6a43 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/IProcedureContext.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/IProcedureContext.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Drawing; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -14,61 +15,105 @@ using Tango.FSE.Common.Connection; using Tango.FSE.Common.Diagnostics; using Tango.Integration.Operation; using Tango.Scripting.Basic; +using Tango.Scripting.Editors.Intellisense; namespace Tango.FSE.Procedures { + /// <summary> + /// Represents the main procedures API. + /// </summary> + /// <seealso cref="Tango.Scripting.Basic.IContext" /> public interface IProcedureContext : IContext { /// <summary> /// Occurs when the procedure is reporting about some progress. /// </summary> + [HideIntellisense] event EventHandler<TangoProgressChangedEventArgs<double>> Progress; /// <summary> + /// Occurs when a procedure object break point request occurs through the IDE break points. + /// </summary> + [HideIntellisense] + event EventHandler<BreakPointRequestEventArgs> BreakPointRequest; + + /// <summary> /// Gets the list of current results. /// </summary> + [HideIntellisense] ReadOnlyCollection<Result> Results { get; } /// <summary> /// Adds a new procedure result. + /// The value object can be a primitive, a collection of primitives or a collection of complex types. /// </summary> - /// <param name="type">The result type.</param> - /// <param name="name">The result name.</param> - /// <param name="value">The result value.</param> - /// <returns></returns> + /// <param name="type">Result type.</param> + /// <param name="name">Result title.</param> + /// <param name="value">Result value.</param> + /// <returns>The result instance.</returns> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates how to add various procedure results. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/AddResult/Program.cs" title="Add results" region="Example" /> + /// </example> Result AddResult(ResultType type, String name, object value); /// <summary> - /// Adds a new procedure graph result. + /// Adds a new procedure result that will be displayed as a graph with X/Y axis. + /// The specified values will be plotted as Y axis values while the X axis will be auto generated by each value index. /// </summary> - /// <param name="type">The type.</param> - /// <param name="name">The name.</param> - /// <param name="values">The values.</param> - /// <returns></returns> + /// <param name="type">Result type.</param> + /// <param name="name">Result title.</param> + /// <param name="values">Graph Y axis values.</param> + /// <returns>The result instance.</returns> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates how to add various procedure results. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/AddResult/Program.cs" title="Add results" region="Example" /> + /// </example> Result AddGraphResult(ResultType type, String name, IEnumerable<double> values); /// <summary> - /// Adds a new procedure bitmap result. + /// Adds a new procedure result with custom appearance by providing a bitmap to draw as the result. + /// Use the <see cref="CreateBitmap(int, int)"/> to create a new bitmap. + /// Then, <see cref="GetDrawingContext(Bitmap)"/> to get the bitmap drawing context in order to start drawing. /// </summary> - /// <param name="type">The type.</param> - /// <param name="name">The name.</param> - /// <param name="bitmap">The bitmap.</param> - /// <returns></returns> + /// <param name="type">Result type.</param> + /// <param name="name">Result title.</param> + /// <param name="bitmap">Result bitmap.</param> + /// <returns>The result instance.</returns> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates how to add various procedure results. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/AddResult/Program.cs" title="Add results" region="Example" /> + /// </example> Result AddBitmapResult(ResultType type, String name, Bitmap bitmap); /// <summary> - /// Creates a new bitmap. + /// Creates a new bitmap to be provided as a bitmap result via <see cref="AddBitmapResult(ResultType, string, Bitmap)"/>. /// </summary> /// <param name="width">The bitmap width.</param> /// <param name="height">The bitmap height.</param> - /// <returns></returns> + /// <returns>The bitmap instance.</returns> Bitmap CreateBitmap(int width, int height); /// <summary> /// Creates a new drawing context for the specified bitmap. /// </summary> + /// <remarks> + /// Please use <see cref="Graphics.Dispose"/> when finish drawing. + /// </remarks> /// <param name="bitmap">The bitmap.</param> - /// <returns></returns> + /// <returns>The drawing context.</returns> Graphics GetDrawingContext(Bitmap bitmap); /// <summary> @@ -76,6 +121,7 @@ namespace Tango.FSE.Procedures /// </summary> /// <param name="result">The result.</param> /// <returns></returns> + [HideIntellisense] Result AddResult(Result result); /// <summary> @@ -90,84 +136,174 @@ namespace Tango.FSE.Procedures void ClearResults(); /// <summary> - /// Sends a request by name with optional comma separated arguments. + /// Sends a stub request by name, with optional timeout and comma separated arguments. /// </summary> - /// <param name="messageName">Name of the message.</param> - /// <param name="timeout">The timeout in seconds.</param> - /// <param name="args">The arguments separated by commas.</param> - /// <returns></returns> + /// <remarks> + /// This method accepts only stub messages. + /// </remarks> + /// <param name="messageName">Full or shortened name of the message (e.g "CalculateRequest" or "calculate").</param> + /// <param name="timeout">Request timeout.</param> + /// <param name="args">Request arguments separated by commas.</param> + /// <returns>The corresponding response message.</returns> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates how to send a request to the machine and get a response using the various different Send method overrides. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Send/Program.cs" title="Send a request" region="Example" /> + /// </example> IMessage Send(String messageName, TimeSpan? timeout = null, params Object[] args); /// <summary> /// Sends a request by name with optional comma separated arguments. /// </summary> - /// <param name="messageName">Name of the message.</param> - /// <param name="args">The arguments separated by commas.</param> - /// <returns></returns> + /// <remarks> + /// This method accepts only stub messages. + /// </remarks> + /// <param name="messageName">Full or shortened name of the message (e.g "CalculateRequest" or "calculate").</param> + /// <param name="args">Request arguments separated by commas.</param> + /// <returns>The corresponding response message.</returns> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates how to send a request to the machine and get a response using the various different Send method overrides. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Send/Program.cs" title="Send a request" region="Example" /> + /// </example> IMessage Send(String messageName, params Object[] args); /// <summary> - /// Sends a request by name with optional comma separated arguments. + /// Sends a request by name with optional timeout and comma separated arguments. /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="messageName">Name of the message.</param> - /// <param name="timeout">The timeout in seconds.</param> - /// <param name="args">The arguments separated by commas.</param> - /// <returns></returns> + /// <remarks> + /// This method accepts only stub messages. + /// </remarks> + /// <typeparam name="T">Type of expected response message.</typeparam> + /// <param name="messageName">Full or shortened name of the message (e.g "CalculateRequest" or "calculate").</param> + /// <param name="timeout">The request timeout.</param> + /// <param name="args">Request arguments separated by commas.</param> + /// <returns>The corresponding response message of type T.</returns> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates how to send a request to the machine and get a response using the various different Send method overrides. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Send/Program.cs" title="Send a request" region="Example" /> + /// </example> T Send<T>(String messageName, TimeSpan? timeout = null, params Object[] args) where T : class, IMessage; /// <summary> /// Sends a request by name with optional comma separated arguments. /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="messageName">Name of the message.</param> - /// <param name="args">The arguments separated by commas.</param> - /// <returns></returns> + /// <remarks> + /// This method accepts only stub messages. + /// </remarks> + /// <typeparam name="T">Type of expected response message.</typeparam> + /// <param name="messageName">Full or shortened name of the message (e.g "CalculateRequest" or "calculate").</param> + /// <param name="args">Request arguments separated by commas.</param> + /// <returns>The corresponding response message of type T.</returns> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates how to send a request to the machine and get a response using the various different Send method overrides. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Send/Program.cs" title="Send a request" region="Example" /> + /// </example> T Send<T>(String messageName, params Object[] args) where T : class, IMessage; /// <summary> - /// Sends the specified message. + /// Sends the specified request message. /// </summary> - /// <param name="message">The message.</param> - /// <param name="timeout">The timeout in seconds.</param> - /// <returns></returns> + /// <param name="message">Request message instance.</param> + /// <param name="timeout">Request timeout in seconds. (leave empty to get the default timeout)</param> + /// <returns>The corresponding response message.</returns> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates how to send a request to the machine and get a response using the various different Send method overrides. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Send/Program.cs" title="Send a request" region="Example" /> + /// </example> IMessage Send(IMessage message, int? timeout = null); /// <summary> - /// Sends the specified message. + /// Sends the specified request message. /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="message">The message.</param> - /// <param name="timeout">The timeout in seconds.</param> - /// <returns></returns> + /// <typeparam name="T">Type of expected response message.</typeparam> + /// <param name="message">Request message instance.</param> + /// <param name="timeout">Request timeout in seconds. (leave empty to get the default timeout)</param> + /// <returns>The corresponding response message of type T.</returns> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates how to send a request to the machine and get a response using the various different Send method overrides. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Send/Program.cs" title="Send a request" region="Example" /> + /// </example> T Send<T>(IMessage message, int? timeout = null) where T : class, IMessage; /// <summary> - /// Sends the specified continuous message. + /// Sends the specified continuous request message. /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="message">The message.</param> - /// <param name="callback">Callback for continuous responses.</param> - /// <param name="timeout">The timeout in seconds.</param> + /// <typeparam name="T">Type of expected continuous response message.</typeparam> + /// <param name="message">Request message instance.</param> + /// <param name="callback">Specify a callback method to handle the incoming response messages.</param> + /// <param name="timeout">First and continuous request timeout in seconds. (leave empty to get the default timeout)</param> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates how to send a continuous request to the machine and get a continuous response using the various different SendContinuous method overrides. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/SendContinuous/Program.cs" title="Send a continuous request" region="Example" /> + /// </example> void SendContinuous<T>(IMessage message, Action<T> callback, int? timeout) where T : class, IMessage; /// <summary> - /// Sends a continuous message with optional comma separated arguments. + /// Sends a continuous request message with optional timeout and comma separated arguments. /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="messageName">Name of the message.</param> - /// <param name="callback">Callback for continuous responses.</param> - /// <param name="timeout">The timeout in seconds.</param> - /// <param name="args">The arguments.</param> - void SendContinuous<T>(String messageName, Action<T> callback, int? timeout, params Object[] args) where T : class, IMessage; + /// <remarks> + /// This method accepts only stub messages. + /// </remarks> + /// <typeparam name="T">Type of expected continuous response message.</typeparam> + /// <param name="messageName">Full or shortened name of the message (e.g "CalculateRequest" or "calculate").</param> + /// <param name="callback">Specify a callback method to handle the incoming response messages.</param> + /// <param name="timeout">First and continuous request timeout in seconds. (leave empty to get the default timeout)</param> + /// <param name="args">Request arguments separated by commas.</param> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates how to send a continuous request to the machine and get a continuous response using the various different SendContinuous method overrides. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/SendContinuous/Program.cs" title="Send a continuous request" region="Example" /> + /// </example> + void SendContinuous<T>(String messageName, Action<T> callback, TimeSpan? timeout, params Object[] args) where T : class, IMessage; /// <summary> - /// Sends a continuous message with optional comma separated arguments. + /// Sends a continuous request message with optional timeout and comma separated arguments. /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="messageName">Name of the message.</param> - /// <param name="callback">Callback for continuous responses.</param> - /// <param name="args">The arguments.</param> + /// <remarks> + /// This method accepts only stub messages. + /// </remarks> + /// <typeparam name="T">Type of expected continuous response message.</typeparam> + /// <param name="messageName">Full or shortened name of the message (e.g "CalculateRequest" or "calculate").</param> + /// <param name="callback">Specify a callback method to handle the incoming response messages.</param> + /// <param name="args">Request arguments separated by commas.</param> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates how to send a continuous request to the machine and get a continuous response using the various different SendContinuous method overrides. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/SendContinuous/Program.cs" title="Send a continuous request" region="Example" /> + /// </example> void SendContinuous<T>(String messageName, Action<T> callback, params Object[] args) where T : class, IMessage; /// <summary> @@ -177,6 +313,11 @@ namespace Tango.FSE.Procedures void WriteLine(Object obj); /// <summary> + /// Writes a carriage return to the output window. + /// </summary> + void WriteLine(); + + /// <summary> /// Writes the specified object string representation to the output window. /// </summary> /// <param name="obj">The object.</param> @@ -200,21 +341,21 @@ namespace Tango.FSE.Procedures /// Writes the specified array to the output window using the specified parsing style. /// </summary> /// <param name="array">The array.</param> - /// <param name="style">The style.</param> + /// <param name="style">Array parsing style.</param> void WriteLineArray(IEnumerable array, ArrayParsingStyle style); /// <summary> /// Reads the specified file as string. /// </summary> /// <param name="path">The file path.</param> - /// <returns></returns> + /// <returns>The file text content.</returns> String GetFileText(String path); /// <summary> - /// Read the specified file bytes + /// Read the specified file bytes. /// </summary> /// <param name="path">The file path.</param> - /// <returns></returns> + /// <returns>The file byte array.</returns> byte[] GetFileBytes(String path); /// <summary> @@ -223,7 +364,7 @@ namespace Tango.FSE.Procedures void Clear(); /// <summary> - /// Writes a string content to the specified file. + /// Writes a string to the specified file. /// If the file already exists it will be overwritten. /// </summary> /// <param name="filePath">The file path.</param> @@ -240,29 +381,29 @@ namespace Tango.FSE.Procedures /// <summary> /// Get the value of a user input by key. /// </summary> - /// <typeparam name="T"></typeparam> + /// <typeparam name="T">Type of expected value.</typeparam> /// <param name="key">The input key.</param> - /// <returns></returns> + /// <returns>The input value.</returns> T GetInput<T>(String key); /// <summary> /// Gets the value of a user input as an array. /// User input must be a comma separated string. /// </summary> - /// <typeparam name="T"></typeparam> + /// <typeparam name="T">Type of expected values.</typeparam> /// <param name="key">The input key.</param> - /// <returns></returns> + /// <returns>List containing the input array.</returns> List<T> GetInputArray<T>(String key); /// <summary> /// Get the value of a user input by key. /// </summary> /// <param name="key">The input key.</param> - /// <returns></returns> + /// <returns>The input value.</returns> Object GetInput(String key); /// <summary> - /// Fails the current procedure with the specified error message. + /// Fails the procedure with the specified error message. /// </summary> /// <param name="message">The error message.</param> void Fail(String message); @@ -300,22 +441,40 @@ namespace Tango.FSE.Procedures bool ShowWarningQuestion(String message); /// <summary> - /// Request a user input for the specified primitive or complex type. + /// Request user input for the specified primitive or complex type (model). /// </summary> - /// <typeparam name="T"></typeparam> + /// <typeparam name="T">Type of input (primitive or complex)</typeparam> /// <param name="title">The request title.</param> /// <param name="message">The request message.</param> - /// <returns></returns> + /// <returns>Request result of type T.</returns> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates how to create a model and request the user to fill or modify that model. + /// The model can contain primitives, arrays (comma separated), enums and booleans. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/RequestUserInputFor/Program.cs" title="Request user input" region="Example" /> + /// </example> T RequestUserInputFor<T>(String title, String message); /// <summary> /// Request a user input for the specified primitive or complex type (model). /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="model">The existing model.</param> + /// <typeparam name="T">Type of input (primitive or complex)</typeparam> + /// <param name="model">Existing model (default values).</param> /// <param name="title">The request title.</param> /// <param name="message">The request message.</param> - /// <returns></returns> + /// <returns>Request result of type T.</returns> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates how to create a model and request the user to fill or modify that model. + /// The model can contain primitives, arrays (comma separated), enums and booleans. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/RequestUserInputFor/Program.cs" title="Request user input" region="Example" /> + /// </example> T RequestUserInputFor<T>(T model, String title, String message); /// <summary> @@ -323,7 +482,7 @@ namespace Tango.FSE.Procedures /// </summary> /// <param name="message">The message.</param> /// <param name="extension">Extension filter (default "*.*").</param> - /// <returns></returns> + /// <returns>The full path to the selected file or null if user canceled.</returns> String RequestFileOpen(String message, String extension = "*.*"); /// <summary> @@ -332,142 +491,215 @@ namespace Tango.FSE.Procedures /// <param name="message">The message.</param> /// <param name="extension">Extension filter (default "*.*").</param> /// <param name="defaultFileName">Optional default file name.</param> - /// <returns></returns> + /// <returns>The full path to the selected file or null if user canceled.</returns> String RequestFileSave(String message, String extension = "*.*", String defaultFileName = null); /// <summary> - /// Updates the procedure progress. + /// Updates the procedure user progress. /// </summary> - /// <param name="message">The message.</param> - /// <param name="isIndeterminate">Is the progress indeterminate (not need to set value and max).</param> + /// <param name="message">The progress message.</param> + /// <param name="isIndeterminate">Is the progress indeterminate (no need to set value and max).</param> /// <param name="value">The progress value.</param> - /// <param name="maximum">The maximum progress .</param> + /// <param name="maximum">The maximum progress.</param> void UpdateProgress(String message, bool isIndeterminate = true, double value = 0, double maximum = 100); /// <summary> - /// Gets the latest diagnostics package. + /// Gets the current diagnostics package. /// </summary> - /// <param name="waitForNext">Waits for a fresh package.</param> - /// <returns></returns> + /// <param name="waitForNext">Blocks the execution until a fresh diagnostics frame is available. + /// This will guarantee that you will get a distinct package each time. + /// </param> + /// <returns>The diagnostics package.</returns> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates how to collect 100 middle dancer samples and plot them as a graph result. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Diagnostics/Program.cs" title="Collect dancer samples" region="Example" /> + /// </example> + /// <seealso cref="AddGraphResult(ResultType, string, IEnumerable{double})"/> DiagnosticsPackage GetDiagnosticsPackage(bool waitForNext = false); /// <summary> - /// Pauses the script execution for the specified time in milliseconds. + /// Pauses the procedure execution for the specified time in milliseconds. /// </summary> - /// <param name="milliseconds">The milliseconds.</param> + /// <param name="milliseconds">Milliseconds.</param> void Sleep(int milliseconds); /// <summary> /// Runs the specified action asynchronously. /// </summary> /// <param name="action">The action.</param> - /// <returns></returns> + /// <returns>The thread instance that has been created for the task.</returns> Thread RunAsync(Action action); /// <summary> - /// Loads the specified procedure dialog on the main UI thread. + /// Loads the specified procedure dialog on the main UI thread as a standard FSE dialog. /// </summary> /// <param name="name">The name of the dialog file.</param> - /// <returns></returns> + /// <returns>The dialog controller.</returns> IDialogController LoadDialog(String name); /// <summary> /// Loads the specified procedure dialog as a window on the current thread. /// </summary> - /// <param name="name">The name.</param> - /// <param name="windowTitle">The window title</param> - /// <returns></returns> + /// <param name="name">The name of the dialog.</param> + /// <param name="windowTitle">The window title.</param> + /// <returns>The dialog controller.</returns> IDialogController LoadDialogAsWindow(String name, String windowTitle); /// <summary> /// Gets the type of the current machine connection. + /// It is recommended to use <see cref="IsConnected"/> before, to ensure active machine connection. /// </summary> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates how to check for valid machine connection, check the connection type and get the connected machine's serial number. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Connection/Program.cs" title="Validate connection" region="Example" /> + /// </example> MachineConnectionTypes ConnectionType { get; } /// <summary> /// Gets or sets a value indicating whether a machine is currently connected. /// </summary> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates how to check for valid machine connection, check the connection type and get the connected machine's serial number. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Connection/Program.cs" title="Validate connection" region="Example" /> + /// </example> bool IsConnected { get; } /// <summary> /// Gets the currently connected machine entity. /// </summary> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates how to check for valid machine connection, check the connection type and get the connected machine's serial number. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Connection/Program.cs" title="Validate connection" region="Example" /> + /// </example> Machine ConnectedMachine { get; } /// <summary> - /// Gets a registered injected service from the Tango FSE application. + /// Gets the specified service of type T from the global Tango FSE IOC container.. /// </summary> /// <typeparam name="T">Type of service</typeparam> - /// <returns></returns> + /// <returns>The service instance.</returns> T GetService<T>(); /// <summary> /// Gets the specified resource as byte array. /// </summary> /// <param name="resourceName">Name of the resource.</param> - /// <returns></returns> + /// <returns>Resource bytes.</returns> byte[] GetResourceBytes(String resourceName); /// <summary> /// Gets the specified resource as UTF-8 string. /// </summary> /// <param name="resourceName">Name of the resource.</param> - /// <returns></returns> + /// <returns>Resource text.</returns> String GetResourceString(String resourceName); /// <summary> - /// Copies the specified resource to a temporary path and opens it using the default application. + /// Copies the specified resource to a temporary path and opens it using the default windows application. /// </summary> /// <param name="resourceName">Name of the resource.</param> void OpenResource(String resourceName); /// <summary> - /// Gets the specified procedure variable. + /// Gets the specified procedure variable value. /// </summary> /// <param name="name">The name of the variable.</param> - /// <returns></returns> + /// <returns>The variable value.</returns> Object GetVariable(String name); /// <summary> - /// Gets the specified procedure variable. + /// Gets the specified procedure variable value. /// </summary> + /// <typeparam name="T">Type of expected variable value.</typeparam> /// <param name="name">The name of the variable.</param> - /// <returns></returns> + /// <returns>The variable value of type T.</returns> T GetVariable<T>(String name); /// <summary> /// Gets the specified procedure variable value as an array. - /// User value must be a comma separated string. + /// Value must be a comma separated string. /// </summary> /// <param name="name">The name of the variable.</param> - /// <returns></returns> + /// <returns>List containing the variable values.</returns> List<T> GetVariableArray<T>(String name); /// <summary> /// Reads the specified csv file to a list of type T. /// The given type T model must contain properties not fields. /// </summary> - /// <typeparam name="T"></typeparam> + /// <typeparam name="T">CSV model type.</typeparam> /// <param name="file">The file path.</param> - /// <returns></returns> + /// <returns>A list containing all the CSV rows as models of type T.</returns> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates CSV file writing and reading. + /// The csv reading method also accepts a byte array so you can also read a CSV file from a procedure resource. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Csv/Program.cs" title="Read/Write CSV" region="Example" /> + /// </example> List<T> ReadCsv<T>(String file) where T : class, new(); /// <summary> /// Reads the specified csv byte array to a list of type T. /// The given type T model must contain properties not fields. /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="data">The data.</param> - /// <returns></returns> + /// <typeparam name="T">CSV model type.</typeparam> + /// <param name="data">The byte array.</param> + /// <returns>A list containing all the CSV rows as models of type T.</returns> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates CSV file writing and reading. + /// The csv reading method also accepts a byte array so you can also read a CSV file from a procedure resource. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Csv/Program.cs" title="Read/Write CSV" region="Example" /> + /// </example> List<T> ReadCsv<T>(byte[] data) where T : class, new(); /// <summary> /// Writes a CSV file to the specified file. /// The given type T model must contain properties not fields. /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="file">The file path.</param> + /// <typeparam name="T">CSV model type.</typeparam> + /// <param name="file">The output file path.</param> /// <param name="items">The items to write.</param> + /// <example> + /// <para> + /// <i> + /// The following example demonstrates CSV file writing and reading. + /// The csv reading method also accepts a byte array so you can also read a CSV file from a procedure resource. + /// </i> + /// </para> + /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Csv/Program.cs" title="Read/Write CSV" region="Example" /> + /// </example> void WriteCsv<T>(String file, List<T> items); + + /// <summary> + /// Request a breakpoint operation from the host IDE (internal use only). + /// </summary> + /// <param name="file">The file.</param> + /// <param name="lineNumber">The line number.</param> + /// <param name="symbolsMap">The symbols map.</param> + [HideIntellisense] + void BreakPoint(String file, int lineNumber, params Object[] symbolsMap); } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Navigation/RunProcedureNavigationObject.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Navigation/RunProcedureNavigationObject.cs new file mode 100644 index 000000000..c19857c21 --- /dev/null +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Navigation/RunProcedureNavigationObject.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Tango.FSE.Procedures.Navigation +{ + public class RunProcedureNavigationObject + { + public ProcedureProject Project { get; set; } + public bool StartProcedure { get; set; } + } +} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ProcedureContext.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ProcedureContext.cs index ef5664772..8089d5437 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ProcedureContext.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ProcedureContext.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -25,9 +26,11 @@ using Tango.FSE.Common.Diagnostics; using Tango.FSE.Common.Notifications; using Tango.FSE.Common.Threading; using Tango.FSE.Procedures.Dialogs; +using Tango.FSE.Procedures.Helpers; using Tango.Integration.Operation; using Tango.PMR; using Tango.Scripting.Basic; +using Tango.Scripting.Core; namespace Tango.FSE.Procedures { @@ -38,6 +41,10 @@ namespace Tango.FSE.Procedures private Dictionary<String, ProcedureInput> _inputs; private DiagnosticsFrame _lastDiagnosticsFrame; + public event EventHandler<TangoProgressChangedEventArgs<double>> Progress; + + public event EventHandler<BreakPointRequestEventArgs> BreakPointRequest; + [TangoInject] private IMachineProvider MachineProvider { get; set; } @@ -87,7 +94,7 @@ namespace Tango.FSE.Procedures var stubType = MessageFactory.GetAvailableRequestStubs().SingleOrDefault(x => x.Name.ToLower() == messageName.ToLower() || x.Name.Replace("Request", "").ToLower() == messageName.ToLower()); if (stubType == null) { - throw new ArgumentException("Invalid stub '" + messageName + "'."); + throw new ArgumentException($"Stub '{messageName}' could not be located on the PMR."); } var stubProps = stubType.GetProperties(BindingFlags.Public | BindingFlags.Instance); @@ -161,7 +168,7 @@ namespace Tango.FSE.Procedures return Send(messageName, timeout) as T; } - public void SendContinuous<T>(IMessage messageName, Action<T> callback, int? timeout = null) where T : class, IMessage + public void SendContinuous<T>(IMessage message, Action<T> callback, int? timeout = null) where T : class, IMessage { TaskCompletionSource<object> completion = new TaskCompletionSource<object>(); @@ -172,7 +179,7 @@ namespace Tango.FSE.Procedures timespan = TimeSpan.FromMilliseconds(timeout.Value); } - MachineProvider.MachineOperator.SendContinuousRequest(messageName, new Transport.TransportContinuousRequestConfig() + MachineProvider.MachineOperator.SendContinuousRequest(message, new Transport.TransportContinuousRequestConfig() { Timeout = timespan, ContinuousTimeout = timespan @@ -194,7 +201,7 @@ namespace Tango.FSE.Procedures completion.Task.GetAwaiter().GetResult(); } - public void SendContinuous<T>(string messageName, Action<T> callback, int? timeout = null, params object[] args) where T : class, IMessage + public void SendContinuous<T>(string messageName, Action<T> callback, TimeSpan? timeout = null, params object[] args) where T : class, IMessage { var stubType = MessageFactory.GetAvailableRequestStubs().SingleOrDefault(x => x.Name.ToLower() == messageName.ToLower() || x.Name.Replace("Request", "").ToLower() == messageName.ToLower()); if (stubType == null) @@ -235,7 +242,7 @@ namespace Tango.FSE.Procedures } } - SendContinuous<IMessage>(request, callback as Action<IMessage>, timeout); + SendContinuous<IMessage>(request, callback as Action<IMessage>, timeout != null ? (int?)timeout.Value.TotalSeconds : null); } public void SendContinuous<T>(string messageName, Action<T> callback, params object[] args) where T : class, IMessage @@ -243,6 +250,11 @@ namespace Tango.FSE.Procedures SendContinuous<T>(messageName, callback, null, args); } + public void WriteLine() + { + _logger?.WriteLine(String.Empty); + } + public void WriteLine(object obj) { String line = "null"; @@ -298,16 +310,19 @@ namespace Tango.FSE.Procedures public void WriteToFile(string filePath, string content) { + EnsureFileLocation(filePath); File.WriteAllText(filePath, content); } public void WriteToFile(string filePath, byte[] data) { + EnsureFileLocation(filePath); File.WriteAllBytes(filePath, data); } public void AppendToFile(string filePath, string content) { + EnsureFileLocation(filePath); File.AppendAllText(filePath, content); } @@ -561,8 +576,6 @@ namespace Tango.FSE.Procedures }); } - public event EventHandler<TangoProgressChangedEventArgs<double>> Progress; - public Result AddBitmapResult(ResultType type, String name, Bitmap bitmap) { return AddResult(new Result() @@ -799,5 +812,52 @@ namespace Tango.FSE.Procedures csvFile.Dispose(); } + + public void BreakPoint(string file, int lineNumber, params object[] symbolsMap) + { + if (BreakPointRequest != null && symbolsMap.Length > 0) + { + bool released = false; + + BreakPointRequestEventArgs args = new BreakPointRequestEventArgs(() => + { + released = true; + }); + + args.LineNumber = lineNumber; + args.Script = _project.Scripts.SingleOrDefault(x => x.Name == file); + + for (int i = 0; i < symbolsMap.Length; i += 4) + { + args.Symbols.Add(new ScriptBreakPointSymbol() + { + Name = symbolsMap[i].ToString(), + Offset = (int)symbolsMap[i + 1], + Length = (int)symbolsMap[i + 2], + SymbolObject = symbolsMap[i + 3], + }); + } + + DispatcherProvider.Invoke(() => + { + BreakPointRequest?.Invoke(this, args); + }); + + while (!released) + { + Thread.Sleep(100); + } + } + } + + private void EnsureFileLocation(String file) + { + String dir = Path.GetDirectoryName(file); + + if (!Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + } } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Resources/main_template.csx b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Resources/main_template.csx index 8a2516cac..26e4fcb1c 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Resources/main_template.csx +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Resources/main_template.csx @@ -20,7 +20,6 @@ public class Program { public void OnExecute(IProcedureContext context) { - context.AddResult(ResultType.Passed, "My Value Name", "Test for this value has passed."); } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Result.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Result.cs index 8b7724eca..dc98957d4 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Result.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Result.cs @@ -9,10 +9,24 @@ using Tango.Core.ExtensionMethods; namespace Tango.FSE.Procedures { + /// <summary> + /// Represents a procedure result. + /// </summary> public class Result { + /// <summary> + /// Gets or sets the type. + /// </summary> public ResultType Type { get; set; } + + /// <summary> + /// Gets or sets the result name. + /// </summary> public String Name { get; set; } + + /// <summary> + /// Gets or sets the result value. + /// </summary> public Object Value { get; set; } [JsonIgnore] diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ResultType.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ResultType.cs index 2eea5ec7f..84c912143 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ResultType.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ResultType.cs @@ -6,10 +6,22 @@ using System.Threading.Tasks; namespace Tango.FSE.Procedures { + /// <summary> + /// Represents a procedure result type. + /// </summary> public enum ResultType { + /// <summary> + /// Positive result. + /// </summary> Passed, + /// <summary> + /// Negative result. + /// </summary> Failed, + /// <summary> + /// Partial success. + /// </summary> Warning, } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/SqlResult.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/SqlResult.cs deleted file mode 100644 index caf4cae32..000000000 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/SqlResult.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Tango.FSE.Procedures -{ - public class SqlResult - { - public int AffectedRecords { get; set; } - public List<Dictionary<String, Object>> Rows { get; set; } - - public SqlResult() - { - Rows = new List<Dictionary<string, object>>(); - } - } -} diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Tango.FSE.Procedures.csproj b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Tango.FSE.Procedures.csproj index fcd5c7603..b7e4b238c 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Tango.FSE.Procedures.csproj +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Tango.FSE.Procedures.csproj @@ -35,6 +35,7 @@ <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <DocumentationFile>..\..\..\Build\FSE\Release\Tango.FSE.Procedures.xml</DocumentationFile> + <NoWarn>1591</NoWarn> </PropertyGroup> <ItemGroup> <Reference Include="ControlzEx, Version=3.0.2.4, Culture=neutral, processorArchitecture=MSIL"> @@ -80,6 +81,7 @@ <Reference Include="System.Reactive.Windows.Threading, Version=3.0.1000.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL"> <HintPath>..\..\..\packages\System.Reactive.Windows.Threading.3.1.1\lib\net45\System.Reactive.Windows.Threading.dll</HintPath> </Reference> + <Reference Include="System.Web.Extensions" /> <Reference Include="System.Windows" /> <Reference Include="System.Windows.Forms" /> <Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> @@ -100,12 +102,18 @@ </ItemGroup> <ItemGroup> <Compile Include="ArrayParsingStyle.cs" /> + <Compile Include="BreakPointRequestEventArgs.cs" /> <Compile Include="Contracts\IProcedureDesignerView.cs" /> + <Compile Include="Controls\ObjectInTreeView.xaml.cs"> + <DependentUpon>ObjectInTreeView.xaml</DependentUpon> + </Compile> + <Compile Include="Controls\TreeNode.cs" /> <Compile Include="Converters\BitmapToBitmapSourceConverter.cs" /> <Compile Include="CreateGroup.cs" /> <Compile Include="CreateItem.cs" /> <Compile Include="CSV\CsvColumn.cs" /> <Compile Include="CSV\CsvRow.cs" /> + <Compile Include="DebugNode.cs" /> <Compile Include="Designer\ProjectModel.cs" /> <Compile Include="Designer\ScriptTabModel.cs" /> <Compile Include="DialogController.cs" /> @@ -114,6 +122,10 @@ <DependentUpon>AddReferenceAssemblyView.xaml</DependentUpon> </Compile> <Compile Include="Dialogs\AddReferenceAssemblyViewVM.cs" /> + <Compile Include="Dialogs\BreakPointValueDialogView.xaml.cs"> + <DependentUpon>BreakPointValueDialogView.xaml</DependentUpon> + </Compile> + <Compile Include="Dialogs\BreakPointValueDialogViewVM.cs" /> <Compile Include="Dialogs\InputSelectionConfigurationDialogView.xaml.cs"> <DependentUpon>InputSelectionConfigurationDialogView.xaml</DependentUpon> </Compile> @@ -142,10 +154,21 @@ <DependentUpon>UserInputDialogView.xaml</DependentUpon> </Compile> <Compile Include="Dialogs\UserInputDialogViewVM.cs" /> + <Compile Include="Examples\AddResult\Program.cs" /> + <Compile Include="Examples\Connection\Program.cs" /> + <Compile Include="Examples\Csv\Program.cs" /> + <Compile Include="Examples\Diagnostics\Program.cs" /> + <Compile Include="Examples\RequestUserInputFor\Program.cs" /> + <Compile Include="Examples\SendContinuous\Program.cs" /> + <Compile Include="Examples\Send\Program.cs" /> + <Compile Include="Examples\Sql\Program.cs" /> + <Compile Include="ExtensionMethods\TreeViewExtensions.cs" /> + <Compile Include="Helpers\ProcedureExceptionHelper.cs" /> <Compile Include="IDialogController.cs" /> <Compile Include="IProcedureContext.cs" /> <Compile Include="IProcedureLogger.cs" /> <Compile Include="Messages\ProcedureProjectPublishedOrSuppressed.cs" /> + <Compile Include="Navigation\RunProcedureNavigationObject.cs" /> <Compile Include="ProcedureDialog.cs" /> <Compile Include="ProcedureInputSelection.cs" /> <Compile Include="ProcedureInputType.cs" /> @@ -160,7 +183,6 @@ <Compile Include="ProcedureFailedException.cs" /> <Compile Include="ProcedureInput.cs" /> <Compile Include="ProcedureProject.cs" /> - <Compile Include="SqlResult.cs" /> <Compile Include="UserInput.cs" /> <Compile Include="ViewModelLocator.cs" /> <Compile Include="ProceduresModule.cs" /> @@ -222,6 +244,10 @@ <EmbeddedResource Include="Resources\procedure_dialog_template.zip" /> </ItemGroup> <ItemGroup> + <ProjectReference Include="..\..\..\PPC\Tango.PPC.Shared\Tango.PPC.Shared.csproj"> + <Project>{208c8bd8-72c6-4e3c-acaa-351091a2acc7}</Project> + <Name>Tango.PPC.Shared</Name> + </ProjectReference> <ProjectReference Include="..\..\..\Scripting\Tango.Scripting.Basic\Tango.Scripting.Basic.csproj"> <Project>{2b29a699-1d65-463a-8250-a2ce81d019c9}</Project> <Name>Tango.Scripting.Basic</Name> @@ -300,10 +326,18 @@ <Generator>MSBuild:Compile</Generator> <SubType>Designer</SubType> </Page> + <Page Include="Controls\ObjectInTreeView.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> <Page Include="Dialogs\AddReferenceAssemblyView.xaml"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> + <Page Include="Dialogs\BreakPointValueDialogView.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> <Page Include="Dialogs\InputSelectionConfigurationDialogView.xaml"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> @@ -386,6 +420,7 @@ <ItemGroup> <Resource Include="Images\blend.png" /> </ItemGroup> + <ItemGroup /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="..\..\..\packages\MaterialDesignThemes.3.0.1\build\MaterialDesignThemes.targets" Condition="Exists('..\..\..\packages\MaterialDesignThemes.3.0.1\build\MaterialDesignThemes.targets')" /> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Themes/Generic.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Themes/Generic.xaml index 06e60997d..3917368f2 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Themes/Generic.xaml +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Themes/Generic.xaml @@ -2,6 +2,10 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:realTimeGraphX="clr-namespace:RealTimeGraphX.WPF;assembly=RealTimeGraphX.WPF" xmlns:commonGraph="clr-namespace:Tango.FSE.Common.Graphs;assembly=Tango.FSE.Common" + xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" + xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes" + xmlns:controls="clr-namespace:Tango.FSE.Common.Controls;assembly=Tango.FSE.Common" + xmlns:views="clr-namespace:Tango.FSE.Procedures.Views" xmlns:local="clr-namespace:Tango.FSE.Procedures.Themes"> <Style x:Key="FocusVisual"> @@ -467,4 +471,221 @@ </Style.Triggers> </Style> + <Style x:Key="FSE_Debug_TreeViewItemFocusVisualStyle"> + <Setter Property="Control.Template"> + <Setter.Value> + <ControlTemplate> + <Rectangle/> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> + + <PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/> + + <Style x:Key="FSE_Debug_ExpandCollapseToggleStyle" TargetType="{x:Type ToggleButton}"> + <Setter Property="Focusable" Value="False"/> + <Setter Property="Width" Value="20"/> + <Setter Property="Height" Value="20"/> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="{x:Type ToggleButton}"> + <Border Background="Transparent" Height="20" Padding="5,5,5,5" Width="20"> + <Path x:Name="ExpandPath" Data="{StaticResource TreeArrow}" Fill="Transparent" Stroke="{StaticResource FSE_PrimaryForegroundBrush}"> + <Path.RenderTransform> + <RotateTransform Angle="135" CenterY="3" CenterX="3"/> + </Path.RenderTransform> + </Path> + </Border> + <ControlTemplate.Triggers> + <Trigger Property="IsChecked" Value="True"> + <Setter Property="RenderTransform" TargetName="ExpandPath"> + <Setter.Value> + <RotateTransform Angle="180" CenterY="3" CenterX="3"/> + </Setter.Value> + </Setter> + <Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource FSE_PrimaryForegroundBrush}"/> + <Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource FSE_PrimaryForegroundBrush}"/> + </Trigger> + <Trigger Property="IsMouseOver" Value="True"> + <Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource FSE_PrimaryAccentBrush}"/> + <Setter Property="Fill" TargetName="ExpandPath" Value="Transparent"/> + </Trigger> + <MultiTrigger> + <MultiTrigger.Conditions> + <Condition Property="IsMouseOver" Value="True"/> + <Condition Property="IsChecked" Value="True"/> + </MultiTrigger.Conditions> + <Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource FSE_PrimaryAccentBrush}"/> + <Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource FSE_PrimaryAccentBrush}"/> + </MultiTrigger> + </ControlTemplate.Triggers> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> + + <Style x:Key="FSE_Debug_TreeViewItemStyle" TargetType="{x:Type TreeViewItem}"> + <Setter Property="Background" Value="Transparent"/> + <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/> + <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/> + <Setter Property="Foreground" Value="{StaticResource FSE_PrimaryForegroundBrush}"/> + <Setter Property="FocusVisualStyle" Value="{StaticResource FSE_Debug_TreeViewItemFocusVisualStyle}"/> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="{x:Type TreeViewItem}"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition MinWidth="19" Width="Auto"/> + <ColumnDefinition Width="Auto"/> + <ColumnDefinition Width="*"/> + </Grid.ColumnDefinitions> + <Grid.RowDefinitions> + <RowDefinition Height="Auto"/> + <RowDefinition/> + </Grid.RowDefinitions> + <ToggleButton x:Name="Expander" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource FSE_Debug_ExpandCollapseToggleStyle}"/> + <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="1" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true"> + <ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/> + </Border> + <ItemsPresenter x:Name="ItemsHost" Grid.ColumnSpan="2" Grid.Column="1" Grid.Row="1"/> + </Grid> + <ControlTemplate.Triggers> + <Trigger Property="IsExpanded" Value="false"> + <Setter Property="Visibility" TargetName="ItemsHost" Value="Collapsed"/> + </Trigger> + <Trigger Property="HasItems" Value="false"> + <Setter Property="Visibility" TargetName="Expander" Value="Hidden"/> + </Trigger> + <Trigger Property="IsSelected" Value="true"> + + </Trigger> + <MultiTrigger> + <MultiTrigger.Conditions> + <Condition Property="IsSelected" Value="true"/> + <Condition Property="IsSelectionActive" Value="false"/> + </MultiTrigger.Conditions> + + </MultiTrigger> + <Trigger Property="IsEnabled" Value="false"> + <Setter Property="Foreground" Value="{StaticResource FSE_GrayBrush}"/> + </Trigger> + </ControlTemplate.Triggers> + </ControlTemplate> + </Setter.Value> + </Setter> + <Style.Triggers> + <Trigger Property="VirtualizingPanel.IsVirtualizing" Value="true"> + <Setter Property="ItemsPanel"> + <Setter.Value> + <ItemsPanelTemplate> + <VirtualizingStackPanel/> + </ItemsPanelTemplate> + </Setter.Value> + </Setter> + </Trigger> + </Style.Triggers> + </Style> + + <Style x:Key="FSE_Debug_TreeViewStyle" TargetType="{x:Type TreeView}"> + <Style.Resources> + <Style TargetType="TreeViewItem" BasedOn="{StaticResource FSE_Debug_TreeViewItemStyle}"> + + </Style> + </Style.Resources> + <Setter Property="Background" Value="Transparent"/> + <Setter Property="BorderBrush" Value="Transparent"/> + <Setter Property="BorderThickness" Value="0"/> + <Setter Property="Padding" Value="0"/> + <Setter Property="Foreground" Value="{StaticResource FSE_PrimaryForegroundBrush}"/> + <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/> + <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/> + <Setter Property="ScrollViewer.PanningMode" Value="Both"/> + <Setter Property="Stylus.IsFlicksEnabled" Value="False"/> + <Setter Property="VerticalContentAlignment" Value="Center"/> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="{x:Type TreeView}"> + <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true"> + <ScrollViewer x:Name="_tv_scrollviewer_" Background="{TemplateBinding Background}" CanContentScroll="false" Focusable="false" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"> + <ItemsPresenter/> + </ScrollViewer> + </Border> + <ControlTemplate.Triggers> + <Trigger Property="IsEnabled" Value="false"> + <Setter Property="Background" TargetName="Bd" Value="Transparent"/> + </Trigger> + <Trigger Property="VirtualizingPanel.IsVirtualizing" Value="true"> + <Setter Property="CanContentScroll" TargetName="_tv_scrollviewer_" Value="true"/> + </Trigger> + </ControlTemplate.Triggers> + </ControlTemplate> + </Setter.Value> + </Setter> + <Style.Triggers> + <Trigger Property="VirtualizingPanel.IsVirtualizing" Value="true"> + <Setter Property="ItemsPanel"> + <Setter.Value> + <ItemsPanelTemplate> + <VirtualizingStackPanel/> + </ItemsPanelTemplate> + </Setter.Value> + </Setter> + </Trigger> + </Style.Triggers> + </Style> + + + <Style x:Key="FSE_Debug_TextBoxStyle" TargetType="TextBox"> + <Setter Property="Background" Value="Transparent"></Setter> + <Setter Property="BorderBrush" Value="{StaticResource FSE_BorderBrush}"></Setter> + <Setter Property="BorderThickness" Value="0"></Setter> + <Setter Property="Foreground" Value="{StaticResource FSE_PrimaryForegroundBrush}"></Setter> + <Setter Property="CaretBrush" Value="{StaticResource FSE_PrimaryForegroundBrush}"></Setter> + </Style> + + <DataTemplate x:Key="TreeViewDataTemplate"> + <DockPanel> + <material:PackIcon Kind="Cube" VerticalAlignment="Center" Width="12" Height="12" Foreground="{StaticResource FSE_PrimaryForegroundBrush}" /> + <DockPanel Margin="2 0 0 0"> + <Border BorderThickness="0.5" BorderBrush="{StaticResource FSE_BorderBrush}" Padding="2"> + <TextBlock Margin="0 0 0 0" Width="100" Text="{Binding Name}" ToolTip="{Binding Name}" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis"></TextBlock> + </Border> + <DockPanel> + <TextBlock DockPanel.Dock="Right" Margin="10 -1 10 0" Foreground="{StaticResource FSE_GrayBrush}" VerticalAlignment="Center"> + <Run>[</Run> + <Run Text="{Binding Type}"></Run> + <Run>]</Run> + </TextBlock> + <Border Height="20" Margin="0 0 0 0" BorderThickness="0.5" BorderBrush="{StaticResource FSE_BorderBrush}" Padding="2"> + <DockPanel> + <controls:IconButton Icon="Search" Padding="0" ToolTip="Display full value" Width="20" Height="20" VerticalAlignment="Center" Style="{StaticResource FSE_IconButton_Flat_Pressed_Highlight}" Cursor="Hand" Command="{Binding RelativeSource={RelativeSource AncestorType=views:ProcedureDesignerView},Path=DataContext.DisplayDebugNodeValueCommand}" CommandParameter="{Binding}" /> + <TextBox ToolTip="{Binding DisplayValue}" IsEnabled="{Binding IsEditable}" TextWrapping="NoWrap" MinWidth="100" MaxWidth="100" Margin="2 0 0 0" Text="{Binding DisplayValue,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"> + <TextBox.Style> + <Style TargetType="TextBox" BasedOn="{StaticResource FSE_Debug_TextBoxStyle}"> + <Setter Property="FontWeight" Value="Normal"></Setter> + <Style.Triggers> + <DataTrigger Binding="{Binding IsEdited}" Value="True"> + <Setter Property="FontWeight" Value="SemiBold"></Setter> + <Setter Property="Foreground" Value="Red"></Setter> + </DataTrigger> + </Style.Triggers> + </Style> + </TextBox.Style> + <i:Interaction.Triggers> + <i:EventTrigger EventName="LostFocus"> + <i:InvokeCommandAction Command="{Binding UpdateValueCommand}" /> + </i:EventTrigger> + </i:Interaction.Triggers> + <TextBox.InputBindings> + <KeyBinding Key="Return" Command="{Binding UpdateValueCommand}" /> + </TextBox.InputBindings> + </TextBox> + </DockPanel> + </Border> + </DockPanel> + </DockPanel> + </DockPanel> + </DataTemplate> + </ResourceDictionary>
\ No newline at end of file diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/UserInput.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/UserInput.cs index f5d3472d8..043b5bbae 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/UserInput.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/UserInput.cs @@ -6,10 +6,21 @@ using System.Threading.Tasks; namespace Tango.FSE.Procedures { + /// <summary> + /// Can be used to decorate a property or field in order to set their custom name when using <see cref="IProcedureContext.RequestUserInputFor{T}(string, string)"/> + /// </summary> + /// <seealso cref="System.Attribute" /> public class UserInput : Attribute { + /// <summary> + /// Gets or sets the field/property custom name. + /// </summary> public String Name { get; set; } + /// <summary> + /// Initializes a new instance of the <see cref="UserInput"/> class. + /// </summary> + /// <param name="name">The field name.</param> public UserInput(String name) { Name = name; diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ViewModels/ProcedureDesignerViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ViewModels/ProcedureDesignerViewVM.cs index 68c8538b8..e9c476a20 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ViewModels/ProcedureDesignerViewVM.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ViewModels/ProcedureDesignerViewVM.cs @@ -8,6 +8,7 @@ using System.IO; using System.Linq; using System.Reflection; using System.Text; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Windows; @@ -20,11 +21,14 @@ using Tango.Core.Commands; using Tango.Core.ExtensionMethods; using Tango.Core.Helpers; using Tango.FSE.Common; +using Tango.FSE.Common.FileAssociation; using Tango.FSE.Common.Navigation; using Tango.FSE.Common.Notifications; using Tango.FSE.Procedures.Contracts; using Tango.FSE.Procedures.Dialogs; using Tango.FSE.Procedures.Messages; +using Tango.FSE.Procedures.Navigation; +using Tango.FSE.Procedures.Views; using Tango.Integration.Operation; using Tango.Scripting.Basic; using Tango.Scripting.Editors; @@ -58,6 +62,8 @@ namespace Tango.FSE.Procedures.ViewModels private String PROJECT_DIALOG_FILTER = $"Procedure Project Files|*.pproj"; private bool _isProjectChanged; private TaskItem _symbolsTaskItem; + private BreakPointRequestEventArgs _lastBreakPointRequestArgs; + private String _lastProjectStringForCompilation; #region Properties @@ -200,6 +206,34 @@ namespace Tango.FSE.Procedures.ViewModels } } + private Exception _runtimeException; + public Exception RuntimeException + { + get { return _runtimeException; } + set { _runtimeException = value; RaisePropertyChangedAuto(); } + } + + private bool _runtimeErrorFree; + public bool RuntimeErrorFree + { + get { return _runtimeErrorFree; } + set { _runtimeErrorFree = value; RaisePropertyChangedAuto(); } + } + + private bool _isBreakPoint; + public bool IsBreakPoint + { + get { return _isBreakPoint; } + set { _isBreakPoint = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } + } + + private bool _isReadOnly; + public bool IsReadOnly + { + get { return _isReadOnly; } + set { _isReadOnly = value; RaisePropertyChangedAuto(); } + } + #endregion #region Commands @@ -244,6 +278,11 @@ namespace Tango.FSE.Procedures.ViewModels public RelayCommand<ProcedureResource> RemoveResourceCommand { get; set; } public RelayCommand<ProcedureResource> OpenResourceCommand { get; set; } public RelayCommand<ProcedureResource> ExportResourceCommand { get; set; } + public RelayCommand CloseRuntimeErrorCommand { get; set; } + public RelayCommand ContinueProjectCommand { get; set; } + public RelayCommand OpenHelpCommand { get; set; } + public RelayCommand<DebugNode> DisplayDebugNodeValueCommand { get; set; } + public RelayCommand RunOnProceduresModuleCommand { get; set; } #endregion @@ -256,6 +295,7 @@ namespace Tango.FSE.Procedures.ViewModels FontSize = 13; ReplaceText = String.Empty; + RuntimeErrorFree = true; LoadedAssemblies = new ObservableCollection<Assembly>(); ResultsViewVM = new ResultsViewVM(); @@ -271,7 +311,8 @@ namespace Tango.FSE.Procedures.ViewModels OpenScripts = new ObservableCollection<Script>(); OpenScriptCommand = new RelayCommand<Script>(OpenScript); CloseScriptCommand = new RelayCommand<Script>(CloseScript); - RunProjectCommand = new RelayCommand(RunProject, () => ProjectRunner != null && ProjectRunner.CanRun); + RunProjectCommand = new RelayCommand(RunProject, () => ProjectRunner != null && (ProjectRunner.CanRun || IsBreakPoint)); + RunOnProceduresModuleCommand = new RelayCommand(RunOnProceduresModule, () => ProjectRunner != null && (ProjectRunner.CanRun || IsBreakPoint)); StopProjectCommand = new RelayCommand(StopProject, () => ProjectRunner != null && ProjectRunner.IsRunning); CompileProjectCommand = new RelayCommand(async () => await CompileProject(), () => ProjectRunner != null && ProjectRunner.CanCompile); AddReferenceAssemblyCommand = new RelayCommand(AddReferenceAssembly); @@ -309,6 +350,10 @@ namespace Tango.FSE.Procedures.ViewModels RemoveResourceCommand = new RelayCommand<ProcedureResource>(RemoveProcedureResource); OpenResourceCommand = new RelayCommand<ProcedureResource>(OpenProcedureResource); ExportResourceCommand = new RelayCommand<ProcedureResource>(ExportProcedureResource); + CloseRuntimeErrorCommand = new RelayCommand(CloseRunTimeError); + ContinueProjectCommand = new RelayCommand(ContinueProject); + OpenHelpCommand = new RelayCommand(OpenHelpFile); + DisplayDebugNodeValueCommand = new RelayCommand<DebugNode>(DisplayDebugNodeValue); } #endregion @@ -410,6 +455,8 @@ namespace Tango.FSE.Procedures.ViewModels LogManager.Log(ex, "Error generating procedure designer auto creation groups."); } }); + + FileAssociationProvider.RegisterFileAssociationHandler("designer", HandlerProcedureFileAssociation); } public override void OnNavigatedTo() @@ -464,6 +511,8 @@ namespace Tango.FSE.Procedures.ViewModels private void StopProject() { ProjectRunner.Stop(); + ContinueProject(); + IsReadOnly = false; } private async Task<bool> CompileProject() @@ -523,15 +572,35 @@ namespace Tango.FSE.Procedures.ViewModels private async void RunProject() { + if (IsBreakPoint) + { + ContinueProject(); + return; + } + try { if (await CompileProject()) { + IsReadOnly = true; SelectedToolWindow = ToolWindows.Output; ResultsViewVM.Results = new List<Result>(); Logger.Clear(); Logger.WriteLine("Running project..."); + var context = new ProcedureContext(Project, this); + + context.BreakPointRequest += Context_BreakPointRequest; + + try + { + Project.BreakPoints = View.GetBreakPoints(); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error initializing break points for project."); + } + await ProjectRunner.Run(context); ResultsViewVM.Results = context.Results.ToList(); @@ -546,17 +615,55 @@ namespace Tango.FSE.Procedures.ViewModels { Logger.WriteLine("Project ran to completion with zero results."); } + + IsReadOnly = false; } } catch (OperationCanceledException) { + IsReadOnly = false; Logger.WriteLine("Project terminated by user."); } catch (Exception ex) { + IsReadOnly = false; SelectedToolWindow = ToolWindows.Output; Logger.WriteLine("Project terminated with error:"); Logger.WriteLine(ex.FlattenMessage()); + + try + { + int? lineNumber = Helpers.ProcedureExceptionHelper.GetExceptionLineNumber(ex, Project); + + if (lineNumber != null) + { + OpenScript(Project.Scripts.FirstOrDefault(x => x.IsEntryPoint)); + RuntimeException = ex; + View.HighlightRuntimeError(lineNumber.Value); + RuntimeErrorFree = false; + IsReadOnly = true; + } + } + catch (Exception exx) + { + LogManager.Log(exx, "Error occurred while trying to show procedure runtime error."); + } + } + finally + { + Project.BreakPoints.Clear(); + } + } + + private async void RunOnProceduresModule() + { + if (await CompileProject()) + { + await NavigationManager.NavigateWithObject<ProceduresModule, ProcedureRunnerView, RunProcedureNavigationObject>(new RunProcedureNavigationObject() + { + Project = Project, + StartProcedure = true + }); } } @@ -602,27 +709,39 @@ namespace Tango.FSE.Procedures.ViewModels _compileTimer.Stop(); - if (Project != null) + if (Project != null && !Project.IsCompiling && !Project.IsRunning) { - CompilationErrors = (await Project.Compile()).Errors; + String projectString = String.Empty; - if (CompilationErrors.Count > 0 && (SelectedToolWindow == ToolWindows.Output || SelectedToolWindow == ToolWindows.Results)) - { - SelectedToolWindow = ToolWindows.Errors; - } + projectString = String.Join(Environment.NewLine, Project.Scripts.ToList().Select(x => x.Code)); + projectString += String.Join(Environment.NewLine, Project.ReferenceAssemblies.ToList().Select(x => x.Name)); - InvokeUI(() => + if (_lastProjectStringForCompilation != projectString) { - View.ClearErrors(); + _lastProjectStringForCompilation = projectString; - foreach (var error in CompilationErrors) + CompilationErrors = (await Project.Compile()).Errors; + + if (CompilationErrors.Count > 0 && (SelectedToolWindow == ToolWindows.Output || SelectedToolWindow == ToolWindows.Results)) { - if (error.File == SelectedScript.Name) + SelectedToolWindow = ToolWindows.Errors; + } + + InvokeUI(() => + { + View.ClearErrors(); + + foreach (var error in CompilationErrors) { - View.HighlightError(error.Position, error.Length); + if (error.File == SelectedScript.Name) + { + View.HighlightError(error.Position, error.Length); + } } - } - }); + }); + } + + _lastProjectStringForCompilation = projectString; } _compileTimer.Start(); @@ -792,6 +911,12 @@ namespace Tango.FSE.Procedures.ViewModels private void OpenProject(String file) { + if (Project != null && Project.IsRunning) + { + NotificationProvider.ShowError("Cannot load a project while another project is running."); + return; + } + try { Project = ProcedureProject.FromJson(File.ReadAllText(file)); @@ -1288,5 +1413,146 @@ namespace Tango.FSE.Procedures.ViewModels } #endregion + + #region Runtime Error + + private void CloseRunTimeError() + { + View.CloseRunTimeError(); + RuntimeErrorFree = true; + IsReadOnly = false; + } + + #endregion + + #region BreakPoint Request + + private void Context_BreakPointRequest(object sender, BreakPointRequestEventArgs e) + { + try + { + _lastBreakPointRequestArgs = e; + OpenScript(e.Script); + View.HighlightBreakPointRequest(e.LineNumber, e.Symbols); + IsBreakPoint = true; + } + catch (Exception ex) + { + e.Release(); + LogManager.Log(ex, "Error initializing runtime debug request."); + } + } + + private void ContinueProject() + { + if (_lastBreakPointRequestArgs != null) + { + View.ResetBreakPointRequest(); + IsBreakPoint = false; + _lastBreakPointRequestArgs.Release(); + _lastBreakPointRequestArgs = null; + } + } + + private async void DisplayDebugNodeValue(DebugNode debugNode) + { + if (debugNode != null) + { + await NotificationProvider.ShowDialog(new BreakPointValueDialogViewVM(debugNode.Name, debugNode.Value)); + } + } + + #endregion + + #region Help + + private void OpenHelpFile() + { + String word = View.GetCaretWord(); + + if (word != null) + { + String memberSign = null; + String typeFullName = null; + + if (typeof(IProcedureContext).GetProperties(BindingFlags.Instance | BindingFlags.Public).Any(x => x.Name == word)) + { + memberSign = "P"; + } + + if (memberSign == null) + { + if (typeof(IProcedureContext).GetMethods(BindingFlags.Instance | BindingFlags.Public).Any(x => x.Name == word)) + { + memberSign = "M"; + } + } + + if (memberSign == null) + { + var type = typeof(IProcedureContext).Assembly.GetTypes().FirstOrDefault(x => x.Name == word); + if (type != null) + { + memberSign = "T"; + typeFullName = type.FullName.Replace(".", "_"); + } + } + + if (memberSign != null) + { + String url = String.Empty; + + if (memberSign == "T") + { + url = $"{typeFullName}.htm"; + } + else + { + url = $"Tango_FSE_Procedures_IProcedureContext_{word}.htm"; + } + + Process pp = new Process(); + pp.StartInfo.FileName = "hh.exe"; + pp.StartInfo.Arguments = $"ms-its:{Path.Combine(AssemblyHelper.GetCurrentAssemblyFolder(), "proc-doc.chm")}::/html/{memberSign}_{url}"; + pp.StartInfo.WindowStyle = ProcessWindowStyle.Maximized; + pp.Start(); + return; + } + } + + Process p = new Process(); + p.StartInfo.FileName = Path.Combine(AssemblyHelper.GetCurrentAssemblyFolder(), "proc-doc.chm"); + p.StartInfo.WindowStyle = ProcessWindowStyle.Maximized; + p.Start(); + } + + #endregion + + #region File Association + + private void HandlerProcedureFileAssociation(FileAssociationPackage package) + { + if (!CurrentUser.HasPermission(Permissions.FSE_RunProcedureDesigner)) + { + NotificationProvider.ShowError("Current user profile does not allow running the procedure designer."); + return; + } + + if (File.Exists(package.File)) + { + try + { + LogManager.Log("Opening procedure project from file association..."); + NavigationManager.NavigateTo<ProceduresModule>(true, nameof(ProcedureDesignerView)); + OpenProject(package.File); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error occurred while trying to handle the procedure file association."); + } + } + } + + #endregion } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ViewModels/ProcedureRunnerViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ViewModels/ProcedureRunnerViewVM.cs index da5ecf324..6ccc77669 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ViewModels/ProcedureRunnerViewVM.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ViewModels/ProcedureRunnerViewVM.cs @@ -1,24 +1,21 @@ using System; -using System.Collections; using System.Collections.Generic; -using System.ComponentModel; +using System.IO; using System.Linq; -using System.Reflection; -using System.Text; using System.Threading.Tasks; using Tango.BL.Entities; using Tango.Core; using Tango.Core.Commands; -using Tango.CSV; using Tango.FSE.Common; -using Tango.FSE.Procedures.CSV; -using Tango.FSE.Procedures.Dialogs; +using Tango.FSE.Common.FileAssociation; +using Tango.FSE.Common.Navigation; using Tango.FSE.Procedures.Messages; +using Tango.FSE.Procedures.Navigation; using Tango.FSE.Procedures.Views; namespace Tango.FSE.Procedures.ViewModels { - public class ProcedureRunnerViewVM : FSEViewModel, IProcedureLogger + public class ProcedureRunnerViewVM : FSEViewModel, IProcedureLogger, INavigationObjectReceiver<RunProcedureNavigationObject> { public enum RunnerView { @@ -27,6 +24,8 @@ namespace Tango.FSE.Procedures.ViewModels } private bool _requiresReloadingOfProjects; + private bool _isFromNavigation; + private RunProcedureNavigationObject _navigationObject; private RunnerView _selectedView; public RunnerView SelectedView @@ -117,10 +116,23 @@ namespace Tango.FSE.Procedures.ViewModels RegisterForMessage<ProcedureProjectPublishedOrSuppressed>((x) => _requiresReloadingOfProjects = true); } + public override void OnApplicationStarted() + { + base.OnApplicationStarted(); + + FileAssociationProvider.RegisterFileAssociationHandler("procedure", HandlerProcedureFileAssociation); + } + private async void StartProject() { try { + if (IsRunning) + { + await NotificationProvider.ShowError("Cannot execute a procedure while another procedure is running."); + return; + } + IsRunning = true; FailedError = null; ResultsViewVM.Results = new List<Result>(); @@ -181,6 +193,29 @@ namespace Tango.FSE.Procedures.ViewModels { LoadPublishedProcedureProjects(); } + + if (_isFromNavigation) + { + if (!IsRunning) + { + RunningProcedureProject = _navigationObject.Project; + RaisePropertyChanged(nameof(HasProcedureInputs)); + ProjectRunner = new ProjectRunner(RunningProcedureProject); + ProjectRunner.StateChanged += (x, e) => InvalidateRelayCommands(); + Status = "Ready"; + InvalidateRelayCommands(); + SelectedView = RunnerView.ProcedureRunnerExecutionView; + + if (_navigationObject.StartProcedure) + { + StartProject(); + } + } + else + { + NotificationProvider.ShowError("Cannot execute a procedure while another procedure is running."); + } + } } private async void LoadPublishedProcedureProjects() @@ -222,9 +257,18 @@ namespace Tango.FSE.Procedures.ViewModels if (SelectedView == RunnerView.ProcedureRunnerExecutionView && !IsRunning) { SelectedView = RunnerView.ProcedureRunnerCatalogView; + + if (_isFromNavigation) + { + _isFromNavigation = false; + return Task.FromResult(true); + } + return Task.FromResult(false); } + _isFromNavigation = false; + return base.OnNavigateBackRequest(); } @@ -242,5 +286,31 @@ namespace Tango.FSE.Procedures.ViewModels { //Do nothing } + + public void OnNavigatedToWithObject(RunProcedureNavigationObject obj) + { + _isFromNavigation = true; + _navigationObject = obj; + } + + private async void HandlerProcedureFileAssociation(FileAssociationPackage package) + { + if (File.Exists(package.File)) + { + try + { + ProcedureProject project = ProcedureProject.FromJson(File.ReadAllText(package.File)); + LogManager.Log("Loading procedure from file association..."); + await NavigationManager.NavigateWithObject<ProceduresModule, ProcedureRunnerView, RunProcedureNavigationObject>(new RunProcedureNavigationObject() + { + Project = project + }); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error occurred while trying to handler the procedure file association."); + } + } + } } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Views/ProcedureDesignerView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Views/ProcedureDesignerView.xaml index 82f799d7b..a271e9d07 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Views/ProcedureDesignerView.xaml +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Views/ProcedureDesignerView.xaml @@ -8,6 +8,7 @@ xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:components="clr-namespace:Tango.SharedUI.Components;assembly=Tango.SharedUI" xmlns:vm="clr-namespace:Tango.FSE.Procedures.ViewModels" + xmlns:localControls="clr-namespace:Tango.FSE.Procedures.Controls" xmlns:material="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:editors="clr-namespace:Tango.Scripting.Editors;assembly=Tango.Scripting.Editors" xmlns:controls="clr-namespace:Tango.FSE.Common.Controls;assembly=Tango.FSE.Common" @@ -22,6 +23,7 @@ <UserControl.InputBindings> <KeyBinding Key="F5" Command="{Binding RunProjectCommand}" /> <KeyBinding Key="F6" Command="{Binding CompileProjectCommand}" /> + <KeyBinding Key="F7" Command="{Binding RunOnProceduresModuleCommand}" /> <KeyBinding Modifiers="Ctrl" Key="S" Command="{Binding SaveProjectCommand}" /> <KeyBinding Modifiers="Ctrl+Shift" Key="S" Command="{Binding SaveAsProjectCommand}" /> <KeyBinding Modifiers="Ctrl" Key="N" Command="{Binding NewProjectCommand}" /> @@ -31,13 +33,14 @@ <KeyBinding Key="F3" Command="{Binding FindNextCommand}" /> <KeyBinding Modifiers="Ctrl" Key="OemPlus" Command="{Binding IncreaseFontSizeCommand}" /> <KeyBinding Modifiers="Ctrl" Key="OemMinus" Command="{Binding DecreaseFontSizeCommand}" /> + <KeyBinding Key="F1" Command="{Binding OpenHelpCommand}" /> </UserControl.InputBindings> <Grid> <Grid> <DockPanel> - <Menu IsMainMenu="True" DockPanel.Dock="Top"> - <MenuItem Header="_File"> + <Menu IsMainMenu="True" DockPanel.Dock="Top" IsHitTestVisible="{Binding RuntimeErrorFree}"> + <MenuItem Header="_File" IsEnabled="{Binding ProjectRunner.CanRun}"> <MenuItem Header="_New" MinWidth="250" Command="{Binding NewProjectCommand}" InputGestureText="Ctrl+N"> <MenuItem.Icon> <material:PackIcon Kind="FileDocument" /> @@ -72,7 +75,7 @@ </MenuItem.Icon> </MenuItem> </MenuItem> - <MenuItem Header="_Edit"> + <MenuItem Header="_Edit" IsEnabled="{Binding ProjectRunner.CanRun}"> <MenuItem MinWidth="250" Header="_Undo" Command="Undo"> <MenuItem.Icon> <material:PackIcon Kind="Undo" /> @@ -143,8 +146,14 @@ <material:PackIcon Kind="Stop" Foreground="{StaticResource FSE_RedBrush}" /> </MenuItem.Icon> </MenuItem> + <Separator/> + <MenuItem Header="Run On Procedures" ToolTip="Run this project using the standard procedures module" Command="{Binding RunOnProceduresModuleCommand}" InputGestureText="F7" Visibility="{Binding ProjectRunner.CanRun,Converter={StaticResource BooleanToVisibilityConverter}}"> + <MenuItem.Icon> + <material:PackIcon Kind="PlayBox" Foreground="{StaticResource FSE_PrimaryAccentBrush}" /> + </MenuItem.Icon> + </MenuItem> </MenuItem> - <MenuItem Header="_Wizard" ItemsSource="{Binding CreateGroups}"> + <MenuItem Header="_Wizard" ItemsSource="{Binding CreateGroups}" IsEnabled="{Binding ProjectRunner.CanRun}"> <MenuItem.ItemContainerStyle> <Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource {x:Type MenuItem}}"> <Setter Property="Command" Value="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.CreateItemCommand}" /> @@ -157,26 +166,33 @@ </HierarchicalDataTemplate> </MenuItem.ItemTemplate> </MenuItem> - <MenuItem Header="Publish"> + <MenuItem Header="Publish" IsEnabled="{Binding ProjectRunner.CanRun}"> <MenuItem Header="_Publish Project" InputGestureText="Ctrl+P" Command="{Binding TogglePublishPanelCommand}" MinWidth="250"> <MenuItem.Icon> <material:PackIcon Kind="Publish" /> </MenuItem.Icon> </MenuItem> </MenuItem> + <MenuItem Header="Help"> + <MenuItem Header="API Documentation" InputGestureText="F1" Command="{Binding OpenHelpCommand}" MinWidth="250"> + <MenuItem.Icon> + <material:PackIcon Kind="HelpCircleOutline" /> + </MenuItem.Icon> + </MenuItem> + </MenuItem> </Menu> - <Grid DockPanel.Dock="Top"> + <Grid DockPanel.Dock="Top" IsHitTestVisible="{Binding RuntimeErrorFree}"> <!--Toolbar--> <ToolBar ClipToBounds="False" Background="Transparent" HorizontalAlignment="Center" ToolBarTray.IsLocked="True"> - <Button ToolTip="Save" Command="{Binding SaveProjectCommand}"> + <Button ToolTip="Save" Command="{Binding SaveProjectCommand}" IsEnabled="{Binding ProjectRunner.CanRun}"> <material:PackIcon Kind="ContentSave" /> </Button> <Separator /> - <Button Command="Undo" ToolTip="Undo" ToolBar.OverflowMode="AsNeeded"> + <Button Command="Undo" ToolTip="Undo" ToolBar.OverflowMode="AsNeeded" IsEnabled="{Binding ProjectRunner.CanRun}"> <material:PackIcon Kind="Undo" /> </Button> - <Button Command="Redo" ToolTip="Redo" ToolBar.OverflowMode="AsNeeded"> + <Button Command="Redo" ToolTip="Redo" ToolBar.OverflowMode="AsNeeded" IsEnabled="{Binding ProjectRunner.CanRun}"> <material:PackIcon Kind="Redo" /> </Button> <Separator/> @@ -187,10 +203,16 @@ <material:PackIcon Kind="ContentCopy" /> </Button> <Separator /> - <Button Command="Paste" ToolTip="Paste" ToolBar.OverflowMode="AsNeeded"> + <Button Command="Paste" ToolTip="Paste" ToolBar.OverflowMode="AsNeeded" IsEnabled="{Binding ProjectRunner.CanRun}"> <material:PackIcon Kind="ContentPaste" /> </Button> <Separator/> + <Button Width="125" ToolTip="Continue (F5)" ToolBar.OverflowMode="AsNeeded" Command="{Binding ContinueProjectCommand}" Visibility="{Binding IsBreakPoint,Converter={StaticResource BooleanToVisibilityConverter}}"> + <DockPanel> + <material:PackIcon Kind="Play" Foreground="{StaticResource FSE_GreenBrush}" /> + <TextBlock Margin="10 2 0 0" VerticalAlignment="Center">Continue</TextBlock> + </DockPanel> + </Button> <Button Width="120" ToolTip="Run (F5)" ToolBar.OverflowMode="AsNeeded" Command="{Binding RunProjectCommand}" Visibility="{Binding ProjectRunner.IsRunning,Converter={StaticResource BooleanToVisibilityInverseConverter}}" IsEnabled="{Binding ProjectRunner.CanRun}"> <DockPanel> <material:PackIcon Kind="Play" Foreground="{StaticResource FSE_GreenBrush}" /> @@ -206,7 +228,7 @@ </ToolBar> </Grid> - <Grid DockPanel.Dock="Bottom" Height="25" Background="#007BA5" TextElement.FontSize="{StaticResource FSE_SmallFontSize}"> + <Grid DockPanel.Dock="Bottom" Height="25" Background="#007BA5" TextElement.FontSize="{StaticResource FSE_SmallFontSize}" IsHitTestVisible="{Binding RuntimeErrorFree}"> <!--Status Bar--> <Grid> <StackPanel HorizontalAlignment="Left" Orientation="Horizontal" Margin="5 0 0 0"> @@ -231,7 +253,7 @@ <ColumnDefinition MaxWidth="500" MinWidth="250" Width="134*"/> </Grid.ColumnDefinitions> - <Grid Width="350" Margin="0 33 0 0" Visibility="{Binding IsPublishPanelOpened,Converter={StaticResource BooleanToVisibilityConverter}}"> + <Grid IsHitTestVisible="{Binding RuntimeErrorFree}" Width="350" Margin="0 33 0 0" Visibility="{Binding IsPublishPanelOpened,Converter={StaticResource BooleanToVisibilityConverter}}"> <Border BorderThickness="0 0 5 0" BorderBrush="{StaticResource FSE_PrimaryBackgroundBrush}"> <DockPanel Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}"> <Border DockPanel.Dock="Top" Padding="5" Background="{StaticResource FSE_PrimaryBackgroundLightBrush}" BorderThickness="0 1 0 0" BorderBrush="{StaticResource FSE_BorderBrush}"> @@ -304,7 +326,7 @@ <RowDefinition MinHeight="30" Height="210*"/> </Grid.RowDefinitions> - <Grid Grid.Column="2"> + <Grid Grid.Column="2" IsHitTestVisible="{Binding RuntimeErrorFree}"> <!--Code Editor--> <DockPanel> <Grid DockPanel.Dock="Top" Height="35"> @@ -413,10 +435,13 @@ <ItemsControl.ItemTemplate> <DataTemplate> <editors:ScriptEditor + IsReadOnly="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.IsReadOnly}" + ScriptSource="{Binding}" ReferenceAssemblies="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.LoadedAssemblies}" AdditionalScripts="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.Project.AdditionalScripts}" FontSize="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext.FontSize}" - Code="{Binding Code,Mode=TwoWay}"> + Code="{Binding Code,Mode=TwoWay}" + BreakPointSymbolPressed="ScriptEditor_BreakPointSymbolPressed"> <editors:ScriptEditor.ContextMenu> <ContextMenu> @@ -492,13 +517,14 @@ </Border> </Border> </Grid> + </Grid> </DockPanel> </Grid> - <GridSplitter Grid.Row="1" Height="5" HorizontalAlignment="Stretch" VerticalAlignment="Center" /> + <GridSplitter IsHitTestVisible="{Binding RuntimeErrorFree}" Grid.Row="1" Height="5" HorizontalAlignment="Stretch" VerticalAlignment="Center" /> - <Grid Grid.Row="2"> + <Grid Grid.Row="2" IsHitTestVisible="{Binding RuntimeErrorFree}"> <!--Output & Error Tabs--> <TabControl SelectedIndex="{Binding SelectedToolWindow,Mode=TwoWay,Converter={StaticResource EnumToIntConverter}}" ItemContainerStyle="{StaticResource FSE_TabItem_VisualStudio_Output}" TabStripPlacement="Bottom" Margin="0" Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}" BorderThickness="0"> <TabItem Header="OUTPUT"> @@ -630,7 +656,7 @@ </Button> <controls:FSERoundedCornersComboBox Height="25" FontSize="{StaticResource FSE_SmallFontSize}" ItemsSource="{Binding SelectionInputs}" SelectedValue="{Binding Value}" SelectedValuePath="Value" DisplayMemberPath="Name"/> </DockPanel> - </StackPanel> + </StackPanel> </DataTemplate> </Setter.Value> </Setter> @@ -639,7 +665,7 @@ <Setter Property="ContentTemplate"> <Setter.Value> <DataTemplate> - <StackPanel> + <StackPanel Margin="0 0 0 7"> <TextBlock Margin="2 0 0 2" FontSize="{StaticResource FSE_SmallerFontSize}">Value</TextBlock> <DockPanel> <ToggleButton x:Name="chkInput" Cursor="Hand" IsChecked="{Binding Value}" FocusVisualStyle="{x:Null}" HorizontalAlignment="Left" /> @@ -734,11 +760,73 @@ </TabItem> </TabControl> </Grid> + + <Canvas Grid.Column="1" Grid.RowSpan="4" Background="Transparent" Margin="95 35 0 0" x:Name="runTimeErrorCanvas" IsHitTestVisible="True" Visibility="Hidden"> + <DockPanel x:Name="runTimeErrorDock" Canvas.Top="200" Canvas.Left="0"> + <Grid x:Name="runTimeErrorLineGrid" DockPanel.Dock="Bottom" Width="50" Height="50" HorizontalAlignment="Left"> + <Line RenderTransformOrigin="0.5,0.5" x:Name="runTimeErrorLine" X1="0" Y1="50" X2="50" Y2="0" Stroke="{StaticResource FSE_ErrorBrush}"> + <Line.RenderTransform> + <RotateTransform Angle="0" x:Name="runTimeErrorLineTransform" /> + </Line.RenderTransform> + </Line> + </Grid> + <Border Margin="50 0 0 0" Width="500" Height="250" BorderBrush="{StaticResource FSE_BorderBrush}" BorderThickness="1" Background="{StaticResource FSE_PrimaryBackgroundMidBrush}"> + <DockPanel> + <DockPanel DockPanel.Dock="Top" Height="25" Background="{StaticResource FSE_PrimaryBackgroundLightBrush}"> + <controls:IconButton Width="20" Height="20" Cursor="Hand" ToolTip="Close" Padding="0" Icon="Close" DockPanel.Dock="Right" Margin="0 0 5 0" Command="{Binding CloseRuntimeErrorCommand}" /> + <material:PackIcon Margin="5 0 0 0" Kind="AlertOctagon" VerticalAlignment="Center" /> + <TextBlock Margin="5 2 0 0" VerticalAlignment="Center" FontSize="{StaticResource FSE_SmallFontSize}">Runtime Error</TextBlock> + </DockPanel> + <Grid> + <DockPanel> + <TextBlock Margin="10" DockPanel.Dock="Top" Text="{Binding RuntimeException.Message,Mode=OneWay}" TextWrapping="Wrap" FontSize="{StaticResource FSE_SmallFontSize}" Foreground="{StaticResource FSE_ErrorBrush}"></TextBlock> + <TextBox Style="{x:Null}" Margin="5 10 10 10" + HorizontalScrollBarVisibility="Hidden" + VerticalScrollBarVisibility="Auto" + AcceptsReturn="True" + TextWrapping="NoWrap" + BorderThickness="0" + Padding="5" + BorderBrush="{StaticResource FSE_BorderBrush}" + Background="Transparent" + Foreground="{StaticResource FSE_GrayBrush}" + CaretBrush="{StaticResource FSE_PrimaryForegroundBrush}" + FontSize="{StaticResource FSE_SmallFontSize}" Text="{Binding RuntimeException,Mode=OneWay}" + IsReadOnly="True"> + + </TextBox> + </DockPanel> + </Grid> + </DockPanel> + </Border> + </DockPanel> + </Canvas> + + <Canvas Grid.Column="1" Grid.RowSpan="4" Background="Transparent" Margin="95 35 0 0" x:Name="runTimeBreakPointCanvas" IsHitTestVisible="True" Visibility="Hidden" MouseUp="RunTimeBreakPointCanvas_MouseUp"> + <DockPanel x:Name="runTimeBreakPointDock" Canvas.Top="200" Canvas.Left="0"> + <Border BorderBrush="{StaticResource FSE_BorderBrush}" MaxHeight="300" MaxWidth="700" BorderThickness="1" Background="{StaticResource FSE_PrimaryBackgroundMidBrush}" Padding="2" TextElement.FontSize="{StaticResource FSE_SmallFontSize}"> + <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> + <TreeView Style="{StaticResource FSE_Debug_TreeViewStyle}" x:Name="runTimeBreakPointTreeView"> + <TreeView.Resources> + <HierarchicalDataTemplate ItemsSource="{Binding Nodes}" DataType="{x:Type global:DebugNode}"> + <ContentControl Content="{Binding}" ContentTemplate="{StaticResource TreeViewDataTemplate}" /> + </HierarchicalDataTemplate> + </TreeView.Resources> + <TreeViewItem ItemsSource="{Binding Nodes}"> + <TreeViewItem.Header> + <ContentControl Content="{Binding}" ContentTemplate="{StaticResource TreeViewDataTemplate}" /> + </TreeViewItem.Header> + </TreeViewItem> + </TreeView> + </ScrollViewer> + </Border> + </DockPanel> + </Canvas> </Grid> <GridSplitter Grid.Column="2" Width="5" HorizontalAlignment="Center" VerticalAlignment="Stretch" Margin="0 33 0 30" /> - <Grid Grid.Column="3"> + <Grid Grid.Column="3" IsHitTestVisible="{Binding RuntimeErrorFree}"> <Grid Margin="0 33 0 29"> <!--Project Explorer--> <DockPanel Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}"> @@ -766,13 +854,13 @@ <TextBlock Margin="5 0 0 0" Text="{Binding Project.Name}" VerticalAlignment="Center"></TextBlock> </DockPanel> - <DockPanel Margin="5 10 0 0"> + <DockPanel Margin="5 10 0 0" IsEnabled="{Binding ProjectRunner.CanRun}"> <controls:ToggleIconButton Width="20" Height="20" x:Name="chkReference" UncheckedIcon="ChevronRight" CheckedIcon="ChevronDown" Cursor="Hand" IsChecked="True" /> <controls:IconButton VerticalAlignment="Center" DockPanel.Dock="Right" Icon="Add" Foreground="{StaticResource FSE_GreenBrush}" Command="{Binding AddReferenceAssemblyCommand}" ToolTip="Add Reference" /> <TextBlock Margin="5 0 0 0" VerticalAlignment="Center" Text="Reference Assemblies"/> </DockPanel> - <ListBox Margin="25 5 0 0" ItemsSource="{Binding Project.ReferenceAssemblies}" Visibility="{Binding ElementName=chkReference,Path=IsChecked,Converter={StaticResource BooleanToVisibilityConverter}}"> + <ListBox IsHitTestVisible="{Binding ProjectRunner.CanRun}" Margin="25 5 0 0" ItemsSource="{Binding Project.ReferenceAssemblies}" Visibility="{Binding ElementName=chkReference,Path=IsChecked,Converter={StaticResource BooleanToVisibilityConverter}}"> <ListBox.ItemContainerStyle> <Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}"> <Setter Property="ContextMenu"> @@ -798,13 +886,13 @@ </ListBox.ItemTemplate> </ListBox> - <DockPanel Margin="5 10 0 0"> + <DockPanel Margin="5 10 0 0" IsEnabled="{Binding ProjectRunner.CanRun}"> <controls:ToggleIconButton Width="20" Height="20" x:Name="chkResources" UncheckedIcon="ChevronRight" CheckedIcon="ChevronDown" Cursor="Hand" IsChecked="True" /> <controls:IconButton VerticalAlignment="Center" DockPanel.Dock="Right" Icon="Add" Foreground="{StaticResource FSE_GreenBrush}" Command="{Binding AddResourceCommand}" ToolTip="Add Resource" /> <TextBlock Margin="5 0 0 0" VerticalAlignment="Center" Text="Resources"/> </DockPanel> - <ListBox Margin="25 5 0 0" ItemsSource="{Binding Project.Resources}" Visibility="{Binding ElementName=chkResources,Path=IsChecked,Converter={StaticResource BooleanToVisibilityConverter}}"> + <ListBox IsHitTestVisible="{Binding ProjectRunner.CanRun}" Margin="25 5 0 0" ItemsSource="{Binding Project.Resources}" Visibility="{Binding ElementName=chkResources,Path=IsChecked,Converter={StaticResource BooleanToVisibilityConverter}}"> <ListBox.ItemContainerStyle> <Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}"> <Setter Property="ContextMenu"> @@ -846,13 +934,13 @@ </ListBox.ItemTemplate> </ListBox> - <DockPanel Margin="5 10 0 0"> + <DockPanel Margin="5 10 0 0" IsEnabled="{Binding ProjectRunner.CanRun}"> <controls:ToggleIconButton Width="20" Height="20" x:Name="chkDialogs" UncheckedIcon="ChevronRight" CheckedIcon="ChevronDown" Cursor="Hand" IsChecked="True" /> <controls:IconButton VerticalAlignment="Center" DockPanel.Dock="Right" Icon="Add" Foreground="{StaticResource FSE_GreenBrush}" Command="{Binding AddDialogCommand}" ToolTip="Add Dialog" /> <TextBlock Margin="5 0 0 0" VerticalAlignment="Center" Text="Dialogs"/> </DockPanel> - <ListBox Margin="25 5 0 0" ItemsSource="{Binding Project.Dialogs}" Visibility="{Binding ElementName=chkDialogs,Path=IsChecked,Converter={StaticResource BooleanToVisibilityConverter}}"> + <ListBox IsHitTestVisible="{Binding ProjectRunner.CanRun}" Margin="25 5 0 0" ItemsSource="{Binding Project.Dialogs}" Visibility="{Binding ElementName=chkDialogs,Path=IsChecked,Converter={StaticResource BooleanToVisibilityConverter}}"> <ListBox.ItemContainerStyle> <Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}"> <EventSetter Event="PreviewMouseDoubleClick" Handler="OnDialogItemDoubleClick"></EventSetter> @@ -888,17 +976,16 @@ <ListBox.ItemContainerStyle> <Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}"> <EventSetter Event="PreviewMouseDoubleClick" Handler="OnScriptItemDoubleClick"></EventSetter> - <Setter Property="ContextMenu"> <Setter.Value> - <ContextMenu> - <MenuItem MinWidth="180" IsEnabled="{Binding IsEntryPoint,Converter={StaticResource BooleanInverseConverter}}" Header="Rename" Command="{Binding Source={StaticResource proxy},Path=Data.RenameLibraryCommand}" CommandParameter="{Binding}"> + <ContextMenu IsEnabled="{Binding Source={StaticResource proxy},Path=Data.ProjectRunner.CanRun}"> + <MenuItem MinWidth="180" IsEnabled="{Binding IsEntryPoint,Converter={StaticResource BooleanInverseConverter}}" Header="Rename" Command="{Binding Source={StaticResource proxy},Path=Data.RenameLibraryCommand}" CommandParameter="{Binding}"> <MenuItem.Icon> <material:PackIcon Kind="Rename" /> </MenuItem.Icon> </MenuItem> <Separator/> - <MenuItem MinWidth="180" IsEnabled="{Binding IsEntryPoint,Converter={StaticResource BooleanInverseConverter}}" Header="Delete" Command="{Binding Source={StaticResource proxy},Path=Data.DeleteLibraryCommand}" CommandParameter="{Binding}"> + <MenuItem MinWidth="180" IsEnabled="{Binding IsEntryPoint,Converter={StaticResource BooleanInverseConverter}}" Header="Delete" Command="{Binding Source={StaticResource proxy},Path=Data.DeleteLibraryCommand}" CommandParameter="{Binding}"> <MenuItem.Icon> <material:PackIcon Kind="Delete" Foreground="{StaticResource FSE_RedBrush}" /> </MenuItem.Icon> diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Views/ProcedureDesignerView.xaml.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Views/ProcedureDesignerView.xaml.cs index 1e5deb04a..6746e794f 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Views/ProcedureDesignerView.xaml.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Views/ProcedureDesignerView.xaml.cs @@ -14,8 +14,11 @@ using System.Windows.Navigation; using System.Windows.Shapes; using Tango.FSE.Procedures.Contracts; using Tango.FSE.Procedures.ViewModels; +using Tango.Logging; using Tango.Scripting.Basic; +using Tango.Scripting.Core; using Tango.Scripting.Editors; +using Tango.SharedUI.Helpers; namespace Tango.FSE.Procedures.Views { @@ -92,7 +95,7 @@ namespace Tango.FSE.Procedures.Views if (editor != null) { - int count = editor.ReplaceAll(text, replace); + int count = editor.ReplaceAll(text, replace); ColorizeKeyword(text); return count; } @@ -137,5 +140,126 @@ namespace Tango.FSE.Procedures.Views { GetAllEditors().ForEach(x => x.ClearErrors()); } + + public void ScrollToLine(int lineNumber) + { + GetCurrentEditor().ScrollToLine(lineNumber); + } + + public void HighlightRuntimeError(int lineNumber) + { + ScrollToLine(lineNumber); + var editor = GetCurrentEditor(); + editor.HighlightErrorLine(lineNumber); + UIHelper.DoEvents(); + Point? p = editor.GetLineVisualPosition(lineNumber); + + if (p != null) + { + Point point = p.Value; + point = new Point(point.X, point.Y - editor.VerticalOffset); + double height = editor.ActualHeight; + + if (point.Y > runTimeErrorDock.ActualHeight) + { + DockPanel.SetDock(runTimeErrorLineGrid, Dock.Bottom); + runTimeErrorLineTransform.Angle = 0; + Canvas.SetTop(runTimeErrorDock, point.Y - runTimeErrorDock.ActualHeight); + } + else + { + DockPanel.SetDock(runTimeErrorLineGrid, Dock.Top); + runTimeErrorLineTransform.Angle = 90; + Canvas.SetTop(runTimeErrorDock, point.Y); + } + + Canvas.SetLeft(runTimeErrorDock, Math.Min(point.X, editor.ActualWidth - 110 - runTimeErrorDock.ActualWidth)); + + runTimeErrorCanvas.Visibility = Visibility.Visible; + } + } + + public void CloseRunTimeError() + { + runTimeErrorCanvas.Visibility = Visibility.Hidden; + GetCurrentEditor().ResetColorizationByKeyword(); + } + + public void HighlightDebugRequest(Object toDebug, int lineNumber) + { + ScrollToLine(lineNumber); + var editor = GetCurrentEditor(); + editor.HighlightDebugLine(lineNumber); + UIHelper.DoEvents(); + Point? p = editor.GetLineVisualPosition(lineNumber); + + if (p != null) + { + Point point = p.Value; + + } + } + + public List<ScriptBreakPoint> GetBreakPoints() + { + List<ScriptBreakPoint> breakPoints = new List<ScriptBreakPoint>(); + + foreach (var editor in GetAllEditors()) + { + breakPoints.AddRange(editor.GetBreakPoints()); + } + + return breakPoints; + } + + public void HighlightBreakPointRequest(int lineNumber, List<ScriptBreakPointSymbol> symbols) + { + UIHelper.DoEvents(); + ScrollToLine(lineNumber); + var editor = GetCurrentEditor(); + editor.HighlightBreakPoint(lineNumber, symbols); + UIHelper.DoEvents(); + } + + public void ResetBreakPointRequest() + { + GetCurrentEditor().ResetBreakPointLine(); + runTimeBreakPointCanvas.Visibility = Visibility.Hidden; + } + + private void ScriptEditor_BreakPointSymbolPressed(object sender, BreakPointSymbolPressedEventArgs e) + { + try + { + var editor = GetCurrentEditor(); + var point = new Point(e.Position.X, e.Position.Y - editor.VerticalOffset); + Canvas.SetTop(runTimeBreakPointDock, point.Y); + Canvas.SetLeft(runTimeBreakPointDock, Math.Min(point.X, editor.ActualWidth - 110 - runTimeBreakPointDock.ActualWidth)); + + + try + { + runTimeBreakPointTreeView.SetExpansion(false); + } + catch { } + + runTimeBreakPointTreeView.DataContext = DebugNode.CreateNode(e.BreakPointSymbol.Name, e.BreakPointSymbol.SymbolObject); + runTimeBreakPointCanvas.Visibility = Visibility.Visible; + } + catch (Exception ex) + { + LogManager.Default.Log(ex, "Error initializing break point debug window."); + } + } + + private void RunTimeBreakPointCanvas_MouseUp(object sender, MouseButtonEventArgs e) + { + runTimeBreakPointCanvas.Visibility = Visibility.Hidden; + } + + public string GetCaretWord() + { + return GetCurrentEditor()?.GetCaretWord(); + } } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/ViewModels/ApplicationUpgradeGeneratedViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/ViewModels/ApplicationUpgradeGeneratedViewVM.cs index 003aebab0..b90d73cc4 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/ViewModels/ApplicationUpgradeGeneratedViewVM.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/ViewModels/ApplicationUpgradeGeneratedViewVM.cs @@ -136,7 +136,7 @@ namespace Tango.FSE.Upgrade.ViewModels { CanUpgradeNowError = null; - if (!IsUpgradeNowSelected) return; + //if (!IsUpgradeNowSelected) return; if (SelectedMachine == null) { @@ -181,7 +181,7 @@ namespace Tango.FSE.Upgrade.ViewModels Handler = await RemoteUpgradeManager.PerformRemoteApplicationUpgrade(TemporaryTupFile, !SuppressFirmwareUpgrade); await Handler.WaitForCompletion(); IsCompleted = true; - await MachineProvider.DisconnectAndWaitForReconnection(TimeSpan.FromSeconds(20), TimeSpan.FromMinutes(1)); + await MachineProvider.DisconnectAndWaitForReconnection(TimeSpan.FromSeconds(20), TimeSpan.FromMinutes(1), "The remote machine is now performing final upgrade operations."); } catch (OperationCanceledException) { @@ -237,7 +237,6 @@ namespace Tango.FSE.Upgrade.ViewModels IsUpgradeNow = false; IsUpgradeNowSelected = false; TemporaryTupFile = null; - InvalidateCanUpgradeNow(); InvalidateRelayCommands(); } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/ViewModels/FirmwareUpgradeGeneratedViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/ViewModels/FirmwareUpgradeGeneratedViewVM.cs index 57cd3aa43..ce01b5804 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/ViewModels/FirmwareUpgradeGeneratedViewVM.cs +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/ViewModels/FirmwareUpgradeGeneratedViewVM.cs @@ -5,10 +5,13 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using Tango.BL.Entities; +using Tango.BL.Enumerations; using Tango.Core.Commands; using Tango.FSE.Common; +using Tango.FSE.Common.FileAssociation; using Tango.FSE.Common.Navigation; using Tango.FSE.Common.RemoteUpgrade; +using Tango.FSE.Upgrade.Views; using static Tango.FSE.Upgrade.ViewModels.FirmwareUpgradeGeneratedViewVM; namespace Tango.FSE.Upgrade.ViewModels @@ -102,14 +105,14 @@ namespace Tango.FSE.Upgrade.ViewModels base.OnApplicationStarted(); MachineProvider.MachineConnected += (_, __) => InvalidateCanUpgradeNow(); MachineProvider.MachineDisconnected += (_, __) => InvalidateCanUpgradeNow(); + + FileAssociationProvider.RegisterFileAssociationHandler("tfp", HandleTfpFileAssociation); } private void InvalidateCanUpgradeNow() { CanUpgradeNowError = null; - if (!IsUpgradeNowSelected) return; - if (!MachineProvider.IsConnected) { CanUpgradeNow = false; @@ -158,7 +161,7 @@ namespace Tango.FSE.Upgrade.ViewModels Handler = await RemoteUpgradeManager.PerformRemoteFirmwareUpgrade(TfpFileLocation); await Handler.WaitForCompletion(); IsCompleted = true; - await MachineProvider.DisconnectAndWaitForReconnection(TimeSpan.FromSeconds(20), TimeSpan.FromMinutes(1)); + await MachineProvider.DisconnectAndWaitForReconnection(TimeSpan.FromSeconds(20), TimeSpan.FromMinutes(1),"The remote machine is now performing final upgrade operations."); } catch (OperationCanceledException) { @@ -201,8 +204,6 @@ namespace Tango.FSE.Upgrade.ViewModels IsCompleted = false; SelectedVersion = null; IsUpgradeNowSelected = true; - TfpFileLocation = null; - InvalidateCanUpgradeNow(); InvalidateRelayCommands(); } @@ -211,6 +212,7 @@ namespace Tango.FSE.Upgrade.ViewModels SelectedVersion = obj.SelectedVersion; TfpFileLocation = obj.TfpFileLocation; IsUsingExistingTfp = obj.IsExistingTfpFile; + InvalidateCanUpgradeNow(); } public async override Task<bool> OnNavigateBackRequest() @@ -237,5 +239,39 @@ namespace Tango.FSE.Upgrade.ViewModels return true; } + + private void HandleTfpFileAssociation(FileAssociationPackage package) + { + if (!IsFree) + { + NotificationProvider.ShowError("Cannot perform firmware upgrade while another upgrade is running."); + return; + } + + if (!CurrentUser.HasPermission(Permissions.FSE_RemoteUpgradeOnline)) + { + NotificationProvider.ShowError("Current user profile does not allow online remote upgrade."); + return; + } + + if (File.Exists(package.File)) + { + try + { + LogManager.Log("Performing remote firmware upgrade from file association..."); + Handler = new RemoteUpgradeHandler("Ready"); + IsCompleted = false; + TfpFileLocation = package.File; + IsUsingExistingTfp = true; + InvalidateCanUpgradeNow(); + NavigationManager.NavigateTo<UpgradeModule>(true); + ModularNavigationManager.NavigateTo(Navigation.RemoteUpgradeView.FirmwareUpgradeGeneratedView); + } + catch (Exception ex) + { + LogManager.Log(ex, "Error occurred while trying to handle the tfp file association."); + } + } + } } } diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/Views/ApplicationUpgradeGeneratedView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/Views/ApplicationUpgradeGeneratedView.xaml index c1f60d813..572785ee4 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/Views/ApplicationUpgradeGeneratedView.xaml +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/Views/ApplicationUpgradeGeneratedView.xaml @@ -12,11 +12,11 @@ d:DesignHeight="720" d:DesignWidth="1280" d:DataContext="{d:DesignInstance Type=vm:ApplicationUpgradeGeneratedViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.ApplicationUpgradeGeneratedViewVM}" Foreground="{StaticResource FSE_PrimaryForegroundBrush}"> <Grid IsEnabled="{Binding IsFree}"> <Grid IsEnabled="{Binding IsCompleted,Converter={StaticResource BooleanInverseConverter}}"> - <StackPanel> - <TextBlock FontSize="{StaticResource FSE_LargeFontSize}" Foreground="{StaticResource FSE_GreenBrush}">Application Upgrade Ready!</TextBlock> + <StackPanel Visibility="{Binding IsCompleted,Converter={StaticResource BooleanToVisibilityInverseConverter}}"> + <TextBlock FontSize="{StaticResource FSE_LargeFontSize}" Foreground="{StaticResource FSE_GreenBrush}">Full Upgrade Ready!</TextBlock> <TextBlock Margin="0 10 0 0" Foreground="{StaticResource FSE_GrayBrush}" LineHeight="20"> - <Run>Your machine application and firmware upgrade is ready.</Run> + <Run>Your machine application and firmware upgrade is ready.</Run> </TextBlock> <StackPanel Visibility="{Binding IsUpgradeNow,Converter={StaticResource BooleanToVisibilityConverter}}" Margin="0 5 0 0" > @@ -60,6 +60,14 @@ </StackPanel> </StackPanel> + <StackPanel Visibility="{Binding IsCompleted,Converter={StaticResource BooleanToVisibilityConverter}}"> + <TextBlock FontSize="{StaticResource FSE_LargeFontSize}" Foreground="{StaticResource FSE_GreenBrush}">Full Upgrade Completed!</TextBlock> + + <TextBlock Margin="0 10 0 0" Foreground="{StaticResource FSE_GrayBrush}" LineHeight="20"> + The remote machine should perform the final update operations and automatically reconnect. + </TextBlock> + </StackPanel> + <StackPanel VerticalAlignment="Bottom" HorizontalAlignment="Left" Width="1000"> <DockPanel> <Grid DockPanel.Dock="Right" > diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/Views/FirmwareUpgradeGeneratedView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/Views/FirmwareUpgradeGeneratedView.xaml index a9d6034fd..c40389600 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/Views/FirmwareUpgradeGeneratedView.xaml +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/Views/FirmwareUpgradeGeneratedView.xaml @@ -12,7 +12,7 @@ d:DesignHeight="720" d:DesignWidth="1280" d:DataContext="{d:DesignInstance Type=vm:FirmwareUpgradeGeneratedViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.FirmwareUpgradeGeneratedViewVM}" Foreground="{StaticResource FSE_PrimaryForegroundBrush}"> <Grid IsEnabled="{Binding IsFree}"> <Grid IsEnabled="{Binding IsCompleted,Converter={StaticResource BooleanInverseConverter}}"> - <StackPanel> + <StackPanel Visibility="{Binding IsCompleted,Converter={StaticResource BooleanToVisibilityInverseConverter}}"> <TextBlock FontSize="{StaticResource FSE_LargeFontSize}" Foreground="{StaticResource FSE_GreenBrush}">Firmware Upgrade Ready!</TextBlock> <TextBlock Margin="0 10 0 0" Foreground="{StaticResource FSE_GrayBrush}" LineHeight="20"> @@ -49,6 +49,14 @@ </StackPanel> </StackPanel> + <StackPanel Visibility="{Binding IsCompleted,Converter={StaticResource BooleanToVisibilityConverter}}"> + <TextBlock FontSize="{StaticResource FSE_LargeFontSize}" Foreground="{StaticResource FSE_GreenBrush}">Firmware Upgrade Completed!</TextBlock> + + <TextBlock Margin="0 10 0 0" Foreground="{StaticResource FSE_GrayBrush}" LineHeight="20"> + The remote machine should perform the final update operations and automatically reconnect. + </TextBlock> + </StackPanel> + <StackPanel VerticalAlignment="Bottom" HorizontalAlignment="Left" Width="1000"> <DockPanel> <Grid DockPanel.Dock="Right" > diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/Views/FirmwareUpgradeView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/Views/FirmwareUpgradeView.xaml index 6aeb0fcb1..3e93300c0 100644 --- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/Views/FirmwareUpgradeView.xaml +++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/Views/FirmwareUpgradeView.xaml @@ -60,7 +60,7 @@ </StackPanel> </DockPanel> - <RadioButton x:Name="chkExisting" IsEnabled="{Binding MachineProvider.IsConnected}" IsChecked="{Binding ElementName=chkDownload,Path=IsChecked,Converter={StaticResource BooleanInverseConverter},Mode=TwoWay}" Margin="0 20 0 0" VerticalContentAlignment="Center" Cursor="Hand" Padding="25 0 0 0"> + <RadioButton x:Name="chkExisting" IsChecked="{Binding ElementName=chkDownload,Path=IsChecked,Converter={StaticResource BooleanInverseConverter},Mode=TwoWay}" Margin="0 20 0 0" VerticalContentAlignment="Center" Cursor="Hand" Padding="25 0 0 0"> <DockPanel> <Image Source="../Images/tfp_ready.png" Width="48" Height="48" /> <TextBlock Margin="20 0 0 0" VerticalAlignment="Center">I already have the .tfp file and just want to upgrade the currently connected machine. (requires an active machine connection)</TextBlock> |
