aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/FSE/Modules
diff options
context:
space:
mode:
authorAvi Levkovich <avi@twine-s.com>2020-08-25 10:08:01 +0300
committerAvi Levkovich <avi@twine-s.com>2020-08-25 10:08:01 +0300
commit338edba081dba2a2aefb634811be1cc84ec93d64 (patch)
tree0021538796c254a8eab8527e8461a2e831e68c1c /Software/Visual_Studio/FSE/Modules
parent49ddda1cc22d6cbb72f499b37e5db32c95252dfa (diff)
downloadTango-338edba081dba2a2aefb634811be1cc84ec93d64.tar.gz
Tango-338edba081dba2a2aefb634811be1cc84ec93d64.zip
merge
Diffstat (limited to 'Software/Visual_Studio/FSE/Modules')
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Diagnostics/ViewModels/DiagnosticsViewVM.cs35
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Diagnostics/diagnostics.tdp2
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Controls/RemoteDesktopControl.xaml2
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.PPCConsole/Views/RemoteDesktopView.xaml7
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Content/VersionHistory/VersionHistory.aml33
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Content/VersionHistory/v1.0.0.0.aml28
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Content/Welcome.aml111
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/ContentLayout.content8
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Help/proc-doc.chmbin0 -> 212099 bytes
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Help/proc-doc.chwbin0 -> 45575 bytes
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Tango.FSE.Procedures.Documentation.shfbproj229
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/icons/Help.pngbin0 -> 4942 bytes
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/icons/machine_48px.pngbin0 -> 3171 bytes
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ArrayParsingStyle.cs10
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/BreakPointRequestEventArgs.cs31
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Contracts/IProcedureDesignerView.cs9
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Controls/ObjectInTreeView.xaml27
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Controls/ObjectInTreeView.xaml.cs50
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Controls/TreeNode.cs98
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/DebugNode.cs256
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Dialogs/BreakPointValueDialogView.xaml24
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Dialogs/BreakPointValueDialogView.xaml.cs28
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Dialogs/BreakPointValueDialogViewVM.cs60
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/AddResult/Program.cs91
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Connection/Program.cs38
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Csv/Program.cs63
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Diagnostics/Program.cs44
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/RequestUserInputFor/Program.cs48
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Send/Program.cs47
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/SendContinuous/Program.cs66
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Sql/Program.cs78
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ExtensionMethods/TreeViewExtensions.cs40
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Helpers/ProcedureExceptionHelper.cs36
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/IDialogController.cs17
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/IProcedureContext.cs458
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Navigation/RunProcedureNavigationObject.cs14
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ProcedureContext.cs74
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Resources/main_template.csx1
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Result.cs14
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ResultType.cs12
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/SqlResult.cs19
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Tango.FSE.Procedures.csproj37
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Themes/Generic.xaml221
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/UserInput.cs11
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ViewModels/ProcedureDesignerViewVM.cs294
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ViewModels/ProcedureRunnerViewVM.cs86
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Views/ProcedureDesignerView.xaml145
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Views/ProcedureDesignerView.xaml.cs126
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/ViewModels/ApplicationUpgradeGeneratedViewVM.cs5
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/ViewModels/FirmwareUpgradeGeneratedViewVM.cs46
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/Views/ApplicationUpgradeGeneratedView.xaml14
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/Views/FirmwareUpgradeGeneratedView.xaml10
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Upgrade/Views/FirmwareUpgradeView.xaml2
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
new file mode 100644
index 000000000..1fb821112
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Help/proc-doc.chm
Binary files differ
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
new file mode 100644
index 000000000..f3921f60d
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Help/proc-doc.chw
Binary files differ
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
new file mode 100644
index 000000000..945e89fb9
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/icons/Help.png
Binary files differ
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
new file mode 100644
index 000000000..95cf615c5
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/icons/machine_48px.png
Binary files differ
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>