aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio
diff options
context:
space:
mode:
Diffstat (limited to 'Software/Visual_Studio')
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/ExportToCsvDialogView.xaml65
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/ExportToCsvDialogView.xaml.cs28
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/ExportToCsvDialogViewVM.cs131
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/excel.pngbin0 -> 1551 bytes
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Tango.FSE.Insights.csproj15
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ViewModels/InsightsViewVM.cs81
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Views/InsightsView.xaml9
-rw-r--r--Software/Visual_Studio/Tango.CSV/DynamicCsvFile.cs100
-rw-r--r--Software/Visual_Studio/Tango.CSV/DynamicCsvFileColumn.cs13
-rw-r--r--Software/Visual_Studio/Tango.CSV/Tango.CSV.csproj2
10 files changed, 437 insertions, 7 deletions
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/ExportToCsvDialogView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/ExportToCsvDialogView.xaml
new file mode 100644
index 000000000..10af90ce3
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/ExportToCsvDialogView.xaml
@@ -0,0 +1,65 @@
+<UserControl x:Class="Tango.FSE.Insights.Dialogs.ExportToCsvDialogView"
+ 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:commonControls="clr-namespace:Tango.FSE.Common.Controls;assembly=Tango.FSE.Common"
+ xmlns:local="clr-namespace:Tango.FSE.Insights.Dialogs"
+ mc:Ignorable="d"
+ Width="700" Height="500" d:DataContext="{d:DesignInstance Type=local:ExportToCsvDialogViewVM, IsDesignTimeCreatable=False}" Background="{StaticResource FSE_PrimaryBackgroundLightBrush}" Foreground="{StaticResource FSE_PrimaryForegroundBrush}">
+ <Grid Margin="10">
+ <DockPanel>
+ <StackPanel DockPanel.Dock="Top" >
+ <StackPanel Orientation="Horizontal">
+ <Image Source="../Images/excel.png" Stretch="Uniform" Width="50" RenderOptions.BitmapScalingMode="Fant" />
+ <TextBlock Margin="10 0 0 0" FontSize="{StaticResource FSE_LargerFontSize}" VerticalAlignment="Center">Export Monitors To CSV</TextBlock>
+ </StackPanel>
+
+ <TextBlock Margin="0 10 0 0" TextWrapping="Wrap" FontSize="{StaticResource FSE_SmallFontSize}">
+ <Run>Please select from the below monitors and press 'EXPORT'.</Run>
+ <LineBreak/>
+ <Run Foreground="{StaticResource FSE_GrayBrush}">Gaps in monitors samples due to machine disconnection etc, will be represented by a row with empty values.</Run>
+ </TextBlock>
+
+ <DockPanel Margin="0 10 0 0">
+ <material:PackIcon Kind="Search" VerticalAlignment="Center" Width="20" Height="20" />
+ <TextBox Margin="5 0 0 0" Padding="1" material:HintAssist.Hint="Find Monitor" Text="{Binding Filter,UpdateSourceTrigger=PropertyChanged,Delay=300}" Width="300" HorizontalAlignment="Left"></TextBox>
+ </DockPanel>
+ </StackPanel>
+
+ <DockPanel>
+ <StackPanel DockPanel.Dock="Bottom" Margin="0 10 0 0">
+ <DockPanel>
+ <commonControls:IconButton Command="{Binding SelectFileCommand}" Margin="5 0 0 0" Padding="0" DockPanel.Dock="Right" Icon="FolderOpen" Width="30" Height="30" VerticalAlignment="Bottom" />
+ <TextBox Style="{StaticResource FSE_Rounded_Corners_TextBox}" Background="{StaticResource FSE_PrimaryBackgroundBrush}" material:HintAssist.Hint="Browse File Location" Text="{Binding SelectedFile,Mode=OneWay}" IsReadOnly="True"></TextBox>
+ </DockPanel>
+ </StackPanel>
+ <Border Margin="0 10 0 0" BorderThickness="1" BorderBrush="{StaticResource FSE_BorderBrush}" Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}" CornerRadius="5">
+ <ScrollViewer Margin="2" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
+ <StackPanel>
+ <StackPanel Orientation="Horizontal" Margin="5 5 0 0">
+ <CheckBox VerticalAlignment="Center" IsChecked="{Binding SelectAll,Mode=TwoWay}" />
+ <material:PackIcon Margin="5 0 0 0" VerticalAlignment="Center" Kind="ChartHistogram" Width="20" Height="20" />
+ <TextBlock Margin="10 0 0 0" Text="Select All" VerticalAlignment="Center"></TextBlock>
+ </StackPanel>
+ <ItemsControl ItemsSource="{Binding SelectedCharts}">
+ <ItemsControl.ItemTemplate>
+ <DataTemplate>
+ <Border Padding="5">
+ <StackPanel Orientation="Horizontal">
+ <CheckBox VerticalAlignment="Center" IsChecked="{Binding IsSelected,Mode=TwoWay}" />
+ <material:PackIcon Margin="5 0 0 0" VerticalAlignment="Center" Kind="ChartLine" Width="20" Height="20" />
+ <TextBlock Margin="10 0 0 0" Text="{Binding Data.Description}" VerticalAlignment="Center"></TextBlock>
+ </StackPanel>
+ </Border>
+ </DataTemplate>
+ </ItemsControl.ItemTemplate>
+ </ItemsControl>
+ </StackPanel>
+ </ScrollViewer>
+ </Border>
+ </DockPanel>
+ </DockPanel>
+ </Grid>
+</UserControl>
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/ExportToCsvDialogView.xaml.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/ExportToCsvDialogView.xaml.cs
new file mode 100644
index 000000000..6df209bb1
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/ExportToCsvDialogView.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.Insights.Dialogs
+{
+ /// <summary>
+ /// Interaction logic for ExportToCsvDialogView.xaml
+ /// </summary>
+ public partial class ExportToCsvDialogView : UserControl
+ {
+ public ExportToCsvDialogView()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/ExportToCsvDialogViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/ExportToCsvDialogViewVM.cs
new file mode 100644
index 000000000..4b75ea6fd
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Dialogs/ExportToCsvDialogViewVM.cs
@@ -0,0 +1,131 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+using Tango.Core.Commands;
+using Tango.Core.DI;
+using Tango.FSE.Common;
+using Tango.FSE.Common.Storage;
+using Tango.FSE.Insights.SciChart;
+using Tango.SharedUI.Components;
+
+namespace Tango.FSE.Insights.Dialogs
+{
+ public class ExportToCsvDialogViewVM : FSEDialogViewVM
+ {
+ private ICollectionView _view;
+ private bool _preventSelectAllChange;
+
+ [TangoInject]
+ private IStorageProvider StorageProvider { get; set; }
+
+ public SelectedObjectCollection<InsightsChart> SelectedCharts { get; set; }
+
+ private String _filter;
+ public String Filter
+ {
+ get { return _filter; }
+ set { _filter = value; RaisePropertyChangedAuto(); OnFilterChanged(); }
+ }
+
+ private bool? _selectAll;
+ public bool? SelectAll
+ {
+ get { return _selectAll; }
+ set
+ {
+ if (_selectAll != value)
+ {
+ _selectAll = value;
+ RaisePropertyChangedAuto();
+ OnSelectAllChanged();
+ }
+ }
+ }
+
+ private String _selectedFile;
+ public String SelectedFile
+ {
+ get { return _selectedFile; }
+ set { _selectedFile = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); }
+ }
+
+ public RelayCommand SelectFileCommand { get; set; }
+
+ public ExportToCsvDialogViewVM(List<InsightsChart> availableCharts, List<InsightsChart> visibleCharts)
+ {
+ TangoIOC.Default.Inject(this);
+
+ OKText = "EXPORT";
+ SelectedCharts = new SelectedObjectCollection<InsightsChart>(availableCharts.OrderByDescending(x => visibleCharts.Contains(x)).ToObservableCollection(), visibleCharts.ToObservableCollection());
+ SelectedCharts.SelectionChanged += SelectedCharts_SelectionChanged;
+
+ _view = CollectionViewSource.GetDefaultView(SelectedCharts);
+ _view.Filter = FilterCharts;
+
+ SelectFileCommand = new RelayCommand(SelectFile);
+ }
+
+ private async void SelectFile()
+ {
+ var result = await StorageProvider.SaveFile("Export Monitors CSV", "CSV Files|*.csv", null, ".csv");
+
+ if (result.Confirmed)
+ {
+ SelectedFile = result.SelectedItem;
+ }
+ }
+
+ private bool FilterCharts(object obj)
+ {
+ if (String.IsNullOrWhiteSpace(Filter)) return true;
+ return (obj as SelectedObject<InsightsChart>).Data.Description.ToLower().Contains(Filter.ToLower());
+ }
+
+ private void SelectedCharts_SelectionChanged(object sender, EventArgs e)
+ {
+ if (!_preventSelectAllChange)
+ {
+ if (SelectedCharts.All(x => x.IsSelected))
+ {
+ _selectAll = true;
+ }
+ else if (SelectedCharts.Any(x => x.IsSelected))
+ {
+ _selectAll = null;
+ }
+ else
+ {
+ _selectAll = false;
+ }
+ }
+
+ RaisePropertyChanged(nameof(SelectAll));
+
+ InvalidateRelayCommands();
+ }
+
+ protected override bool CanOK()
+ {
+ return base.CanOK() && SelectedCharts.SynchedSource.Count > 0 && SelectedFile != null;
+ }
+
+ private void OnFilterChanged()
+ {
+ _view?.Refresh();
+ }
+
+ private void OnSelectAllChanged()
+ {
+ if (SelectAll != null)
+ {
+ _preventSelectAllChange = true;
+ SelectedCharts.ToList().ForEach(x => x.IsSelected = SelectAll.Value);
+ _preventSelectAllChange = false;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/excel.png b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/excel.png
new file mode 100644
index 000000000..74f67b9df
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Images/excel.png
Binary files differ
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Tango.FSE.Insights.csproj b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Tango.FSE.Insights.csproj
index 3130e4c3b..63ba49c7a 100644
--- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Tango.FSE.Insights.csproj
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Tango.FSE.Insights.csproj
@@ -167,6 +167,10 @@
<DependentUpon>AnomaliesDialogView.xaml</DependentUpon>
</Compile>
<Compile Include="Dialogs\AnomaliesDialogViewVM.cs" />
+ <Compile Include="Dialogs\ExportToCsvDialogView.xaml.cs">
+ <DependentUpon>ExportToCsvDialogView.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="Dialogs\ExportToCsvDialogViewVM.cs" />
<Compile Include="InsightsSettings.cs" />
<Compile Include="ML\AnomaliesDetectionProgress.cs" />
<Compile Include="ML\AnomaliesDetector.cs" />
@@ -227,6 +231,10 @@
<Project>{a34ee0f0-649d-41c8-8489-b6f1cc6924ee}</Project>
<Name>Tango.Core</Name>
</ProjectReference>
+ <ProjectReference Include="..\..\..\Tango.CSV\Tango.CSV.csproj">
+ <Project>{58e8825f-0c96-449c-b320-1e82b0aa876b}</Project>
+ <Name>Tango.CSV</Name>
+ </ProjectReference>
<ProjectReference Include="..\..\..\Tango.Insights\Tango.Insights.csproj">
<Project>{4a55c185-3f8d-41b0-8815-c15f6213a14a}</Project>
<Name>Tango.Insights</Name>
@@ -273,6 +281,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
+ <Page Include="Dialogs\ExportToCsvDialogView.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
<Page Include="Themes\Generic.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
@@ -323,6 +335,9 @@
<ItemGroup>
<Resource Include="Images\update_package.png" />
</ItemGroup>
+ <ItemGroup>
+ <Resource Include="Images\excel.png" />
+ </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.Insights/ViewModels/InsightsViewVM.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ViewModels/InsightsViewVM.cs
index 8f14534a3..af2060db8 100644
--- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ViewModels/InsightsViewVM.cs
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/ViewModels/InsightsViewVM.cs
@@ -19,6 +19,7 @@ using System.Windows.Media;
using Tango.BL.Entities;
using Tango.BL.Enumerations;
using Tango.Core.Commands;
+using Tango.CSV;
using Tango.FSE.Common;
using Tango.FSE.Common.Dialogs;
using Tango.FSE.Common.Insights;
@@ -177,7 +178,6 @@ namespace Tango.FSE.Insights.ViewModels
set { _isDetecting = value; RaisePropertyChangedAuto(); }
}
-
public AnnotationCollection Annotations { get; set; }
#endregion
@@ -194,6 +194,8 @@ namespace Tango.FSE.Insights.ViewModels
public RelayCommand AnalyzeCommand { get; set; }
+ public RelayCommand ExportToCsvCommand { get; set; }
+
#endregion
#region Constructors
@@ -204,11 +206,11 @@ namespace Tango.FSE.Insights.ViewModels
{
MinDate = DateTime.Now.AddDays(-30);
}
- MaxDate = DateTime.Now;
+ MaxDate = DateTime.Now;
IsSearchBarOpened = true;
- DisplayAnnotations = true;
- DisplayAnomalies = true;
+ DisplayAnnotations = false;
+ DisplayAnomalies = false;
Charts = new ObservableCollection<InsightsChart>();
VisibleCharts = new ObservableCollection<InsightsChart>();
Annotations = new AnnotationCollection();
@@ -218,6 +220,7 @@ namespace Tango.FSE.Insights.ViewModels
OpenBugReportCommand = new RelayCommand<InsightsReadyBug>(OpenAnnotationBugReport);
ViewApplicationExceptionCommand = new RelayCommand<InsightsReadyApplicationException>(ViewAnnotationApplicationException);
AnalyzeCommand = new RelayCommand(Analyze, () => InsightsPackage != null);
+ ExportToCsvCommand = new RelayCommand(ExportToCsv, () => InsightsPackage != null);
}
#endregion
@@ -732,6 +735,7 @@ namespace Tango.FSE.Insights.ViewModels
});
View.ZoomExtents();
+ DisplayAnomalies = true;
}
catch (Exception ex)
{
@@ -749,6 +753,75 @@ namespace Tango.FSE.Insights.ViewModels
#endregion
+ #region Export CSV
+
+ private async void ExportToCsv()
+ {
+ var vm = await NotificationProvider.ShowDialog(new ExportToCsvDialogViewVM(Charts.ToList(), VisibleCharts.ToList()));
+ if (!vm.DialogResult) return;
+
+ var snackbar = NotificationProvider.PushProgressSnackbar("Exporting insights", "Exporting insights to csv...");
+
+ try
+ {
+ await Task.Factory.StartNew(() =>
+ {
+ List<InsightsChart> chartsToExport = vm.SelectedCharts.SynchedSource.ToList();
+
+ List<Func<InsightsMonitors, double>> delegates = new List<Func<InsightsMonitors, double>>();
+
+ foreach (var chart in chartsToExport)
+ {
+ var del =
+ Delegate.CreateDelegate(typeof(Func<InsightsMonitors, double>),
+ chart.InsightsMonitorsProperty.GetGetMethod())
+ as Func<InsightsMonitors, double>;
+
+ delegates.Add(del);
+ }
+
+ using (DynamicCsvFile csvFile = new DynamicCsvFile(vm.SelectedFile))
+ {
+ csvFile.Columns = chartsToExport.Select(x => new DynamicCsvFileColumn() { Name = x.Description }).ToList();
+ csvFile.Columns.Insert(0, new DynamicCsvFileColumn() { Name = "Time" });
+
+ int progress = 0;
+
+ foreach (var frame in InsightsPackage.Frames)
+ {
+ List<String> values = new List<string>();
+ values.Add(frame.Frame.Time.ToLocalTime().ToString("MM/dd/yyyy HH:mm:ss.fff"));
+
+ foreach (var del in delegates)
+ {
+ double value = del(frame.Monitors);
+ String str = double.IsNaN(value) ? String.Empty : value.ToString();
+ values.Add(str);
+ }
+
+ csvFile.Append<String>(values);
+
+ progress++;
+
+ snackbar.Message = $"Exporting insights to csv ({(int)((double)progress / (double)InsightsPackage.Frames.Count * 100d)})...";
+ }
+ }
+
+ snackbar.ProgressCompleted("File exported successfully.\nTap here to view the file.", TimeSpan.FromSeconds(10), () =>
+ {
+ StorageProvider.ShowInExplorer(vm.SelectedFile);
+ });
+ });
+ }
+ catch (Exception ex)
+ {
+ LogManager.Log(ex, "Error exporting insights csv file.");
+ snackbar.ProgressFailed($"Error exporting csv file.\n{ex.Message}");
+ }
+ }
+
+ #endregion
+
#region Private Methods
private void OnMonitorsFilterChanged()
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Views/InsightsView.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Views/InsightsView.xaml
index 844e2adee..a0df61d5b 100644
--- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Views/InsightsView.xaml
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Insights/Views/InsightsView.xaml
@@ -241,18 +241,21 @@
</MenuItem>
</Menu>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" HorizontalAlignment="Center" Margin="5 20 5 5">
- <ToggleButton material:ToggleButtonAssist.SwitchTrackOnBackground="{StaticResource FSE_GreenBrush}" IsChecked="{Binding DisplayAnnotations}" Width="32" Height="32" Style="{StaticResource FSE_Toolbar_ToggleButton}" ToolTip="Display Annotations">
- <material:PackIcon Kind="MagnifyScan" />
+ <ToggleButton material:ToggleButtonAssist.SwitchTrackOnBackground="{StaticResource FSE_YellowBrush}" IsChecked="{Binding DisplayAnnotations}" Width="32" Height="32" Style="{StaticResource FSE_Toolbar_ToggleButton}" ToolTip="Display Annotations">
+ <material:PackIcon Kind="Lightbulb" />
</ToggleButton>
<ToggleButton material:ToggleButtonAssist.SwitchTrackOnBackground="{StaticResource FSE_RedBrush}" Margin="5 0 0 0" IsChecked="{Binding DisplayAnomalies}" Width="32" Height="32" Style="{StaticResource FSE_Toolbar_ToggleButton}" ToolTip="Display Anomalies">
<material:PackIcon Kind="Spider" />
</ToggleButton>
- <Button Cursor="Hand" Command="{Binding AnalyzeCommand}" Margin="10 0 0 0" Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}" material:ButtonAssist.CornerRadius="3" Style="{StaticResource FSE_RaisedButton_Dark_Hover}" BorderBrush="{StaticResource FSE_RedBrush}" BorderThickness="1">
+ <Button Cursor="Hand" Command="{Binding AnalyzeCommand}" Margin="5 0 0 0" Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}" material:ButtonAssist.CornerRadius="3" Style="{StaticResource FSE_RaisedButton_Dark_Hover}" BorderBrush="{StaticResource FSE_RedBrush}" BorderThickness="1">
<DockPanel>
<material:PackIcon VerticalAlignment="Center" Kind="Spider" Foreground="{StaticResource FSE_RedBrush}" />
<TextBlock Margin="10 0 0 0">Detect Anomalies</TextBlock>
</DockPanel>
</Button>
+ <Button ToolTip="Export Monitors Data To CSV" Margin="10 0 0 0" BorderBrush="{StaticResource FSE_GreenBrush}" Background="{StaticResource FSE_PrimaryBackgroundDarkBrush}" Width="32" Padding="0" FocusVisualStyle="{x:Null}" material:ButtonAssist.CornerRadius="3" Command="{Binding ExportToCsvCommand}">
+ <material:PackIcon Kind="FileExcelOutline" Foreground="{StaticResource FSE_GreenBrush}" Width="18" Height="18" />
+ </Button>
</StackPanel>
<Grid DockPanel.Dock="Top" Width="330" Margin="0 10 0 0" HorizontalAlignment="Center">
<Polygon Stroke="{StaticResource FSE_PrimaryAccentBrush}" StrokeThickness="1" Stretch="Fill" Height="50" Points="0,15 15,0 285,0 300,15 285,30 15,30"></Polygon>
diff --git a/Software/Visual_Studio/Tango.CSV/DynamicCsvFile.cs b/Software/Visual_Studio/Tango.CSV/DynamicCsvFile.cs
new file mode 100644
index 000000000..0c4d79949
--- /dev/null
+++ b/Software/Visual_Studio/Tango.CSV/DynamicCsvFile.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.CSV
+{
+ public class DynamicCsvFile : IDisposable
+ {
+ private bool _isDisposed;
+ private Stream _stream;
+ private StreamWriter _writer;
+ private bool _disposeStream;
+ private bool _columnsWritten;
+
+ public List<DynamicCsvFileColumn> Columns { get; set; }
+
+ public DynamicCsvFile()
+ {
+ Columns = new List<DynamicCsvFileColumn>();
+ }
+
+ public DynamicCsvFile(Stream stream) : this()
+ {
+ _stream = stream;
+ _writer = new StreamWriter(_stream);
+ }
+
+ public DynamicCsvFile(String file) : this(new FileStream(file, FileMode.Create))
+ {
+ _disposeStream = true;
+ }
+
+ private void WriteColumns()
+ {
+ if (!_columnsWritten)
+ {
+ _columnsWritten = true;
+ _writer.WriteLine(String.Join(",", Columns.Select(x => x.Name)));
+ }
+ }
+
+ public void Append(params Object[] values)
+ {
+ Append((IEnumerable)values);
+ }
+
+ public void Append<T>(IEnumerable<T> values, Func<T, Object> modifier = null)
+ {
+ if (modifier != null)
+ {
+ Append((IEnumerable)values, (x) => { return modifier((T)x); });
+ }
+ else
+ {
+ Append((IEnumerable)values, null);
+ }
+ }
+
+ public void Append(IEnumerable values, Func<Object, Object> modifier = null)
+ {
+ if (!_columnsWritten)
+ {
+ WriteColumns();
+ }
+
+ List<String> valuesStr = new List<string>();
+
+ foreach (var value in values)
+ {
+ Object finalValue = value;
+
+ if (modifier != null)
+ {
+ finalValue = modifier(finalValue);
+ }
+
+ valuesStr.Add(finalValue != null ? finalValue.ToString() : String.Empty);
+ }
+
+ _writer.WriteLine(String.Join(",", valuesStr));
+ }
+
+ public void Dispose()
+ {
+ if (!_isDisposed)
+ {
+ _isDisposed = true;
+
+ if (_disposeStream)
+ {
+ _stream?.Dispose();
+ }
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.CSV/DynamicCsvFileColumn.cs b/Software/Visual_Studio/Tango.CSV/DynamicCsvFileColumn.cs
new file mode 100644
index 000000000..c8fd850e7
--- /dev/null
+++ b/Software/Visual_Studio/Tango.CSV/DynamicCsvFileColumn.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.CSV
+{
+ public class DynamicCsvFileColumn
+ {
+ public String Name { get; set; }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.CSV/Tango.CSV.csproj b/Software/Visual_Studio/Tango.CSV/Tango.CSV.csproj
index c3e455ff1..b6743bede 100644
--- a/Software/Visual_Studio/Tango.CSV/Tango.CSV.csproj
+++ b/Software/Visual_Studio/Tango.CSV/Tango.CSV.csproj
@@ -87,6 +87,8 @@
<Compile Include="CsvIgnoreAttribute.cs" />
<Compile Include="CsvOrderAttribute.cs" />
<Compile Include="CsvSource.cs" />
+ <Compile Include="DynamicCsvFile.cs" />
+ <Compile Include="DynamicCsvFileColumn.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />