aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio
diff options
context:
space:
mode:
authorRoy Ben-Shabat <Roy@Twine-s.com>2018-08-02 15:05:44 +0300
committerRoy Ben-Shabat <Roy@Twine-s.com>2018-08-02 15:05:44 +0300
commit05fca4fe321600c4a9c0698b1e4c161e3ed79c9f (patch)
treefda984b5a9ff3a3980bf9b6b5a560b3edaa4e668 /Software/Visual_Studio
parent3499090dce4acc5b5d4bbb02f07f138950790b25 (diff)
downloadTango-05fca4fe321600c4a9c0698b1e4c161e3ed79c9f.tar.gz
Tango-05fca4fe321600c4a9c0698b1e4c161e3ed79c9f.zip
Added Tango.CSV project!
Implemented Single/Multi graph recording to CSV!
Diffstat (limited to 'Software/Visual_Studio')
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/MultiTechRecordingData.cs57
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/MultiTechRecordingValue.cs30
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/SingleTechRecordingData.cs38
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/SingleTechRecordingValue.cs18
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/TechRecordingData.cs55
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/TechRecordingValue.cs18
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/PropertiesTemplates/MultiGraphTemplate.xaml53
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/PropertiesTemplates/SingleGraphTemplate.xaml53
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Tango.MachineStudio.Technician.csproj12
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/TechItems/MultiGraphItem.cs58
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/TechItems/SingleGraphItem.cs57
-rw-r--r--Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/ViewModels/MachineTechViewVM.cs121
-rw-r--r--Software/Visual_Studio/Tango.CSV/CsvDefinition.cs59
-rw-r--r--Software/Visual_Studio/Tango.CSV/CsvDestination.cs77
-rw-r--r--Software/Visual_Studio/Tango.CSV/CsvFile.cs430
-rw-r--r--Software/Visual_Studio/Tango.CSV/CsvFileLinqExtensions.cs46
-rw-r--r--Software/Visual_Studio/Tango.CSV/CsvFileReader.cs425
-rw-r--r--Software/Visual_Studio/Tango.CSV/CsvIgnoreAttribute.cs27
-rw-r--r--Software/Visual_Studio/Tango.CSV/CsvSource.cs95
-rw-r--r--Software/Visual_Studio/Tango.CSV/Properties/AssemblyInfo.cs9
-rw-r--r--Software/Visual_Studio/Tango.CSV/Tango.CSV.csproj99
-rw-r--r--Software/Visual_Studio/Tango.sln54
22 files changed, 1868 insertions, 23 deletions
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/MultiTechRecordingData.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/MultiTechRecordingData.cs
new file mode 100644
index 000000000..d6acd3873
--- /dev/null
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/MultiTechRecordingData.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Core;
+using Tango.Core.IO;
+using Tango.CSV;
+using Tango.MachineStudio.Technician.TechItems;
+
+namespace Tango.MachineStudio.Technician.Models
+{
+ public class MultiTechRecordingData<T> : TechRecordingData<T, MultiTechRecordingValue> where T : TechItem
+ {
+ public int ChannelCount { get; set; }
+
+ public MultiTechRecordingData(T techItem, int channel_count) : base(techItem)
+ {
+ ChannelCount = channel_count;
+ Init();
+ }
+
+ public void PushData(List<List<double>> data)
+ {
+ TimeSpan delta_base = DateTime.Now - _start_time;
+ double delta_mili = (DateTime.Now - _last_time).TotalMilliseconds;
+
+ _last_time = DateTime.Now;
+
+ Task.Factory.StartNew(() =>
+ {
+ var width = data.Count;
+ var height = data.First().Count;
+
+ for (int row = 0; row < height; row++)
+ {
+ String time = (delta_base.Add(TimeSpan.FromMilliseconds((delta_mili / data.Count) * row))).ToString(@"hh\:mm\:ss\.fff");
+
+ List<double> row_values = new List<double>();
+
+ for (int column = 0; column < width; column++)
+ {
+ row_values.Add(data[column][row]);
+ }
+
+ CsvFile.Append(new MultiTechRecordingValue(time, row_values));
+ }
+ });
+ }
+
+ protected override List<string> GetColumnNames()
+ {
+ return Enumerable.Range(1, ChannelCount).Select(x => TechItem.TechName + " " + x).ToList();
+ }
+ }
+}
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/MultiTechRecordingValue.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/MultiTechRecordingValue.cs
new file mode 100644
index 000000000..c451c02a2
--- /dev/null
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/MultiTechRecordingValue.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.MachineStudio.Technician.Models
+{
+ public class MultiTechRecordingValue : TechRecordingValue
+ {
+ public double Value1 { get; set; }
+ public double Value2 { get; set; }
+ public double Value3 { get; set; }
+ public double Value4 { get; set; }
+ public double Value5 { get; set; }
+ public double Value6 { get; set; }
+ public double Value7 { get; set; }
+ public double Value8 { get; set; }
+ public double Value9 { get; set; }
+ public double Value10 { get; set; }
+
+ public MultiTechRecordingValue(string time, List<double> values) : base(time)
+ {
+ for (int i = 0; i < values.Count; i++)
+ {
+ typeof(MultiTechRecordingValue).GetProperty("Value" + (i + 1)).SetValue(this, values[i]);
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/SingleTechRecordingData.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/SingleTechRecordingData.cs
new file mode 100644
index 000000000..3c5426ed9
--- /dev/null
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/SingleTechRecordingData.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.MachineStudio.Technician.TechItems;
+
+namespace Tango.MachineStudio.Technician.Models
+{
+ public class SingleTechRecordingData<T> : TechRecordingData<T, SingleTechRecordingValue> where T : TechItem
+ {
+ public SingleTechRecordingData(T techItem) : base(techItem)
+ {
+ Init();
+ }
+
+ public void PushData(List<double> data)
+ {
+ TimeSpan delta_base = DateTime.Now - _start_time;
+ double delta_mili = (DateTime.Now - _last_time).TotalMilliseconds;
+
+ _last_time = DateTime.Now;
+
+ Task.Factory.StartNew(() =>
+ {
+ for (int i = 0; i < data.Count; i++)
+ {
+ CsvFile.Append(new SingleTechRecordingValue((delta_base.Add(TimeSpan.FromMilliseconds((delta_mili / data.Count) * i))).ToString(@"hh\:mm\:ss\.fff"), data[i]));
+ }
+ });
+ }
+
+ protected override List<string> GetColumnNames()
+ {
+ return new List<string>() { TechItem.TechName };
+ }
+ }
+}
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/SingleTechRecordingValue.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/SingleTechRecordingValue.cs
new file mode 100644
index 000000000..f9aa8623a
--- /dev/null
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/SingleTechRecordingValue.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.MachineStudio.Technician.Models
+{
+ public class SingleTechRecordingValue : TechRecordingValue
+ {
+ public double Value { get; set; }
+
+ public SingleTechRecordingValue(String time, double value) : base(time)
+ {
+ Value = value;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/TechRecordingData.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/TechRecordingData.cs
new file mode 100644
index 000000000..cb4b10db1
--- /dev/null
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/TechRecordingData.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tango.Core;
+using Tango.Core.IO;
+using Tango.CSV;
+using Tango.MachineStudio.Technician.TechItems;
+
+namespace Tango.MachineStudio.Technician.Models
+{
+ public abstract class TechRecordingData<T, TValue> : ExtendedObject, IDisposable where T : TechItem where TValue : TechRecordingValue
+ {
+ protected DateTime _last_time;
+ protected DateTime _start_time;
+
+ public CsvFile<TValue> CsvFile { get; set; }
+
+ public TemporaryFile TemporaryFile { get; set; }
+
+ public T TechItem { get; set; }
+
+ public TechRecordingData(T techItem)
+ {
+ _start_time = DateTime.Now;
+ _last_time = DateTime.Now;
+ TechItem = techItem;
+ TemporaryFile = TemporaryManager.CreateFile(".csv");
+ }
+
+ protected void Init()
+ {
+ CsvDefinition definition = new CsvDefinition();
+ definition.Columns = new List<String>() { "Time" }.Concat(GetColumnNames());
+
+ CsvFile = new CsvFile<TValue>(new CsvDestination(TemporaryFile), definition);
+ }
+
+ public void Save(String fileName)
+ {
+ CsvFile.Dispose();
+ File.Copy(TemporaryFile.Path, fileName, true);
+ }
+
+ public void Dispose()
+ {
+ CsvFile.Dispose();
+ TemporaryFile.Delete();
+ }
+
+ protected abstract List<String> GetColumnNames();
+ }
+}
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/TechRecordingValue.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/TechRecordingValue.cs
new file mode 100644
index 000000000..4812b5017
--- /dev/null
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Models/TechRecordingValue.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.MachineStudio.Technician.Models
+{
+ public abstract class TechRecordingValue
+ {
+ public String Time { get; set; }
+
+ public TechRecordingValue(String time)
+ {
+ Time = time;
+ }
+ }
+}
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/PropertiesTemplates/MultiGraphTemplate.xaml b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/PropertiesTemplates/MultiGraphTemplate.xaml
index da5818a6b..0e2afcf53 100644
--- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/PropertiesTemplates/MultiGraphTemplate.xaml
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/PropertiesTemplates/MultiGraphTemplate.xaml
@@ -4,13 +4,14 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mahapps="http://metro.mahapps.com/winfx/xaml/controls"
+ xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:colorPicker="clr-namespace:Tango;assembly=Tango.ColorPicker"
xmlns:sharedConverters="clr-namespace:Tango.SharedUI.Converters;assembly=Tango.SharedUI"
xmlns:converters="clr-namespace:Tango.MachineStudio.Technician.Converters"
xmlns:items="clr-namespace:Tango.MachineStudio.Technician.TechItems"
xmlns:local="clr-namespace:Tango.MachineStudio.Technician.PropertiesTemplates"
mc:Ignorable="d"
- d:DesignHeight="300" d:DesignWidth="200" d:DataContext="{d:DesignInstance Type=items:SingleGraphItem, IsDesignTimeCreatable=False}">
+ d:DesignHeight="600" d:DesignWidth="300" d:DataContext="{d:DesignInstance Type=items:SingleGraphItem, IsDesignTimeCreatable=False}">
<UserControl.Resources>
<converters:MonitorsToMultiChannleMonitorsConverter x:Key="MonitorsToMultiChannleMonitorsConverter" />
@@ -53,6 +54,56 @@
<Button Margin="0 10" Command="{Binding ClearCommand}">CLEAR</Button>
</StackPanel>
</GroupBox>
+
+ <GroupBox Header="CSV RECORDING">
+ <StackPanel Margin="0 10">
+ <DockPanel>
+ <Button HorizontalAlignment="Left" ToolTip="Record this graph data. When stopped, the data will be saved to a csv file." Command="{Binding ToggleRecordingCommand}">
+ <StackPanel Orientation="Horizontal">
+ <materialDesign:PackIcon VerticalAlignment="Center" Kind="Record" Foreground="#FF6D6D">
+ <materialDesign:PackIcon.Style>
+ <Style TargetType="materialDesign:PackIcon">
+ <Setter Property="Opacity" Value="1"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding IsRecording}" Value="True">
+ <DataTrigger.EnterActions>
+ <BeginStoryboard Name="blink">
+ <Storyboard>
+ <DoubleAnimationUsingKeyFrames RepeatBehavior="Forever" Storyboard.TargetProperty="Opacity" FillBehavior="Stop">
+ <DiscreteDoubleKeyFrame KeyTime="00:00:00" Value="0" />
+ <DiscreteDoubleKeyFrame KeyTime="00:00:0.5" Value="1" />
+ <DiscreteDoubleKeyFrame KeyTime="00:00:1" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </BeginStoryboard>
+ </DataTrigger.EnterActions>
+ <DataTrigger.ExitActions>
+ <RemoveStoryboard BeginStoryboardName="blink" />
+ </DataTrigger.ExitActions>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </materialDesign:PackIcon.Style>
+ </materialDesign:PackIcon>
+ <TextBlock Margin="10 0 0 0">
+ <TextBlock.Style>
+ <Style TargetType="TextBlock">
+ <Setter Property="Text" Value="Start Recording"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding IsRecording}" Value="True">
+ <Setter Property="Text" Value="Stop Recording"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </TextBlock.Style>
+ </TextBlock>
+ </StackPanel>
+ </Button>
+
+ <TextBlock DockPanel.Dock="Right" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="digital-7" FontSize="24" Text="{Binding RecordingTime,StringFormat=hh\\:mm\\:ss,TargetNullValue='00:00:00'}"></TextBlock>
+ </DockPanel>
+ </StackPanel>
+ </GroupBox>
</StackPanel>
</Grid>
</UserControl>
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/PropertiesTemplates/SingleGraphTemplate.xaml b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/PropertiesTemplates/SingleGraphTemplate.xaml
index d7e40f2e0..5c090d4b5 100644
--- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/PropertiesTemplates/SingleGraphTemplate.xaml
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/PropertiesTemplates/SingleGraphTemplate.xaml
@@ -4,13 +4,14 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mahapps="http://metro.mahapps.com/winfx/xaml/controls"
+ xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:colorPicker="clr-namespace:Tango;assembly=Tango.ColorPicker"
xmlns:converters="clr-namespace:Tango.MachineStudio.Technician.Converters"
xmlns:sharedConverters="clr-namespace:Tango.SharedUI.Converters;assembly=Tango.SharedUI"
xmlns:items="clr-namespace:Tango.MachineStudio.Technician.TechItems"
xmlns:local="clr-namespace:Tango.MachineStudio.Technician.PropertiesTemplates"
mc:Ignorable="d"
- d:DesignHeight="400" d:DesignWidth="200" d:DataContext="{d:DesignInstance Type=items:SingleGraphItem, IsDesignTimeCreatable=False}">
+ d:DesignHeight="600" d:DesignWidth="300" d:DataContext="{d:DesignInstance Type=items:SingleGraphItem, IsDesignTimeCreatable=False}">
<UserControl.Resources>
<converters:MonitorsToSingleChannleMonitorsConverter x:Key="MonitorsToSingleChannleMonitorsConverter" />
@@ -54,6 +55,56 @@
</StackPanel>
</GroupBox>
+ <GroupBox Header="CSV RECORDING">
+ <StackPanel Margin="0 10">
+ <DockPanel>
+ <Button HorizontalAlignment="Left" ToolTip="Record this graph data. When stopped, the data will be saved to a csv file." Command="{Binding ToggleRecordingCommand}">
+ <StackPanel Orientation="Horizontal">
+ <materialDesign:PackIcon VerticalAlignment="Center" Kind="Record" Foreground="#FF6D6D">
+ <materialDesign:PackIcon.Style>
+ <Style TargetType="materialDesign:PackIcon">
+ <Setter Property="Opacity" Value="1"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding IsRecording}" Value="True">
+ <DataTrigger.EnterActions>
+ <BeginStoryboard Name="blink">
+ <Storyboard>
+ <DoubleAnimationUsingKeyFrames RepeatBehavior="Forever" Storyboard.TargetProperty="Opacity" FillBehavior="Stop">
+ <DiscreteDoubleKeyFrame KeyTime="00:00:00" Value="0" />
+ <DiscreteDoubleKeyFrame KeyTime="00:00:0.5" Value="1" />
+ <DiscreteDoubleKeyFrame KeyTime="00:00:1" Value="1" />
+ </DoubleAnimationUsingKeyFrames>
+ </Storyboard>
+ </BeginStoryboard>
+ </DataTrigger.EnterActions>
+ <DataTrigger.ExitActions>
+ <RemoveStoryboard BeginStoryboardName="blink" />
+ </DataTrigger.ExitActions>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </materialDesign:PackIcon.Style>
+ </materialDesign:PackIcon>
+ <TextBlock Margin="10 0 0 0">
+ <TextBlock.Style>
+ <Style TargetType="TextBlock">
+ <Setter Property="Text" Value="Start Recording"></Setter>
+ <Style.Triggers>
+ <DataTrigger Binding="{Binding IsRecording}" Value="True">
+ <Setter Property="Text" Value="Stop Recording"></Setter>
+ </DataTrigger>
+ </Style.Triggers>
+ </Style>
+ </TextBlock.Style>
+ </TextBlock>
+ </StackPanel>
+ </Button>
+
+ <TextBlock DockPanel.Dock="Right" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="digital-7" FontSize="24" Text="{Binding RecordingTime,StringFormat=hh\\:mm\\:ss,TargetNullValue='00:00:00'}"></TextBlock>
+ </DockPanel>
+ </StackPanel>
+ </GroupBox>
+
<GroupBox Margin="0 10 0 0" Header="COLOR">
<StackPanel>
<Viewbox Margin="0 5 0 0">
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Tango.MachineStudio.Technician.csproj b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Tango.MachineStudio.Technician.csproj
index 1943780b4..c9e30457e 100644
--- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Tango.MachineStudio.Technician.csproj
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/Tango.MachineStudio.Technician.csproj
@@ -145,6 +145,12 @@
<DependentUpon>MonitorElementEditor.xaml</DependentUpon>
</Compile>
<Compile Include="Helpers\GraphsHelper.cs" />
+ <Compile Include="Models\MultiTechRecordingValue.cs" />
+ <Compile Include="Models\MultiTechRecordingData.cs" />
+ <Compile Include="Models\SingleTechRecordingData.cs" />
+ <Compile Include="Models\SingleTechRecordingValue.cs" />
+ <Compile Include="Models\TechRecordingData.cs" />
+ <Compile Include="Models\TechRecordingValue.cs" />
<Compile Include="Project\MachineTechViewProject.cs" />
<Compile Include="PropertiesTemplates\JobRunnerTemplate.xaml.cs">
<DependentUpon>JobRunnerTemplate.xaml</DependentUpon>
@@ -416,6 +422,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.DragAndDrop\Tango.DragAndDrop.csproj">
<Project>{b112d89a-a106-41ae-a0c1-4abc84c477f5}</Project>
<Name>Tango.DragAndDrop</Name>
@@ -562,7 +572,7 @@
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
<VisualStudio>
- <UserProperties BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UseGlobalSettings="False" BuildVersion_StartDate="2000/1/1" />
+ <UserProperties BuildVersion_StartDate="2000/1/1" BuildVersion_UseGlobalSettings="False" BuildVersion_BuildVersioningStyle="None.None.Increment.TimeStamp" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_AssemblyInfoFilename="Properties\AssemblyInfo.cs" />
</VisualStudio>
</ProjectExtensions>
</Project> \ No newline at end of file
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/TechItems/MultiGraphItem.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/TechItems/MultiGraphItem.cs
index 52925ee59..d4bdfb7b2 100644
--- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/TechItems/MultiGraphItem.cs
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/TechItems/MultiGraphItem.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using System.Windows.Threading;
using System.Xml.Serialization;
using Tango.BL.Entities;
using Tango.Core.Commands;
@@ -19,6 +20,12 @@ namespace Tango.MachineStudio.Technician.TechItems
[TechItem(5)]
public class MultiGraphItem : TechItem
{
+ private DispatcherTimer _timer;
+ private DateTime _recording_start_time;
+
+ public event Action RecordingStarted;
+ public event Action RecordingStopped;
+
private TechMonitor _techMonitor;
/// <summary>
/// Gets or sets the db tech monitor.
@@ -111,14 +118,40 @@ namespace Tango.MachineStudio.Technician.TechItems
}
}
+ private bool _isRecording;
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance is recording.
+ /// </summary>
+ [XmlIgnore]
+ public bool IsRecording
+ {
+ get { return _isRecording; }
+ set { _isRecording = value; RaisePropertyChangedAuto(); }
+ }
+
+ private TimeSpan _recordingTime;
+ [XmlIgnore]
+ public TimeSpan RecordingTime
+ {
+ get { return _recordingTime; }
+ set { _recordingTime = value; RaisePropertyChangedAuto(); }
+ }
+
[XmlIgnore]
public RelayCommand ClearCommand { get; set; }
+ [XmlIgnore]
+ public RelayCommand ToggleRecordingCommand { get; set; }
+
/// <summary>
/// Initializes a new instance of the <see cref="MultiGraphItem"/> class.
/// </summary>
public MultiGraphItem() : base()
{
+ _timer = new DispatcherTimer();
+ _timer.Tick += _timer_Tick;
+ _timer.Interval = TimeSpan.FromSeconds(1);
+
Max = 100;
Name = "Multi Channel Graph";
Description = "Multi channel real-time graph";
@@ -131,6 +164,31 @@ namespace Tango.MachineStudio.Technician.TechItems
Editor.InnerGraph.Controller.Clear();
}
});
+
+ ToggleRecordingCommand = new RelayCommand(ToggleRecording);
+ }
+
+ private void _timer_Tick(object sender, EventArgs e)
+ {
+ RecordingTime = DateTime.Now - _recording_start_time;
+ }
+
+ private void ToggleRecording()
+ {
+ if (!IsRecording)
+ {
+ _recording_start_time = DateTime.Now;
+ IsRecording = true;
+ _timer.Start();
+ RecordingStarted?.Invoke();
+ }
+ else
+ {
+ _timer.Stop();
+ IsRecording = false;
+ RecordingTime = TimeSpan.FromSeconds(0);
+ RecordingStopped?.Invoke();
+ }
}
/// <summary>
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/TechItems/SingleGraphItem.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/TechItems/SingleGraphItem.cs
index f2bad5349..c175f1138 100644
--- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/TechItems/SingleGraphItem.cs
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/TechItems/SingleGraphItem.cs
@@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
+using System.Windows.Threading;
using System.Xml.Serialization;
using Tango.BL.Entities;
using Tango.Core.Commands;
@@ -20,6 +21,12 @@ namespace Tango.MachineStudio.Technician.TechItems
[TechItem(4)]
public class SingleGraphItem : TechItem
{
+ private DispatcherTimer _timer;
+ private DateTime _recording_start_time;
+
+ public event Action RecordingStarted;
+ public event Action RecordingStopped;
+
private TechMonitor _techMonitor;
/// <summary>
/// Gets or sets the db tech monitor.
@@ -111,14 +118,39 @@ namespace Tango.MachineStudio.Technician.TechItems
}
}
+ private bool _isRecording;
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance is recording.
+ /// </summary>
+ [XmlIgnore]
+ public bool IsRecording
+ {
+ get { return _isRecording; }
+ set { _isRecording = value; RaisePropertyChangedAuto(); }
+ }
+
+ private TimeSpan _recordingTime;
+ [XmlIgnore]
+ public TimeSpan RecordingTime
+ {
+ get { return _recordingTime; }
+ set { _recordingTime = value; RaisePropertyChangedAuto(); }
+ }
+
[XmlIgnore]
public RelayCommand ClearCommand { get; set; }
+ [XmlIgnore]
+ public RelayCommand ToggleRecordingCommand { get; set; }
+
/// <summary>
/// Initializes a new instance of the <see cref="SingleGraphItem"/> class.
/// </summary>
public SingleGraphItem() : base()
{
+ _timer = new DispatcherTimer();
+ _timer.Tick += _timer_Tick;
+ _timer.Interval = TimeSpan.FromSeconds(1);
Max = 100;
Name = "Single Channel Graph";
Description = "Single channel real-time graph";
@@ -132,6 +164,31 @@ namespace Tango.MachineStudio.Technician.TechItems
Editor.InnerGraph.Controller.Clear();
}
});
+
+ ToggleRecordingCommand = new RelayCommand(ToggleRecording);
+ }
+
+ private void _timer_Tick(object sender, EventArgs e)
+ {
+ RecordingTime = DateTime.Now - _recording_start_time;
+ }
+
+ private void ToggleRecording()
+ {
+ if (!IsRecording)
+ {
+ _recording_start_time = DateTime.Now;
+ IsRecording = true;
+ _timer.Start();
+ RecordingStarted?.Invoke();
+ }
+ else
+ {
+ _timer.Stop();
+ IsRecording = false;
+ RecordingTime = TimeSpan.FromSeconds(0);
+ RecordingStopped?.Invoke();
+ }
}
/// <summary>
diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/ViewModels/MachineTechViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/ViewModels/MachineTechViewVM.cs
index 3dff65fbc..cd7582fe9 100644
--- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/ViewModels/MachineTechViewVM.cs
+++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Technician/ViewModels/MachineTechViewVM.cs
@@ -32,6 +32,7 @@ using Tango.MachineStudio.Common.EventLogging;
using Tango.MachineStudio.Common;
using Tango.Core.Commands;
using Tango.MachineStudio.Technician.Helpers;
+using Tango.MachineStudio.Technician.Models;
namespace Tango.MachineStudio.Technician.ViewModels
{
@@ -54,6 +55,9 @@ namespace Tango.MachineStudio.Technician.ViewModels
private const int MIN_DIAGNOSTICS_UPDATE_MILI = 500;
private TechnicianModuleSettings _settings;
+ private List<SingleTechRecordingData<SingleGraphItem>> _single_graphs_recordings;
+ private List<MultiTechRecordingData<MultiGraphItem>> _multi_graph_recordings;
+
#region Properties
@@ -221,6 +225,9 @@ namespace Tango.MachineStudio.Technician.ViewModels
{
_settings = SettingsManager.Default.GetOrCreate<TechnicianModuleSettings>();
+ _single_graphs_recordings = new List<SingleTechRecordingData<SingleGraphItem>>();
+ _multi_graph_recordings = new List<MultiTechRecordingData<MultiGraphItem>>();
+
GraphsDurationSeconds = _settings.GraphsDuration;
TempGraphsDurationSeconds = GraphsDurationSeconds;
@@ -261,7 +268,7 @@ namespace Tango.MachineStudio.Technician.ViewModels
UploadHardwareConfigurationCommand = new RelayCommand(UploadHardwareConfiguration);
SyncHardwareConfigurationCommand = new RelayCommand(SyncHardwareConfiguration);
ResetHardwareConfigurationCommand = new RelayCommand(() => ResetHardwareConfiguration());
- UpdateGraphsDurationCommand = new RelayCommand(() =>
+ UpdateGraphsDurationCommand = new RelayCommand(() =>
{
GraphsDurationSeconds = TempGraphsDurationSeconds;
_settings.GraphsDuration = GraphsDurationSeconds;
@@ -372,6 +379,12 @@ namespace Tango.MachineStudio.Technician.ViewModels
});
controller.PushData(points);
+
+ var _graph_recording = _single_graphs_recordings.SingleOrDefault(x => x.TechItem == graphItem);
+ if (_graph_recording != null)
+ {
+ _graph_recording.PushData(points);
+ }
}
}
}
@@ -400,6 +413,12 @@ namespace Tango.MachineStudio.Technician.ViewModels
}
controller.PushData(points);
+
+ var _graph_recording = _multi_graph_recordings.SingleOrDefault(x => x.TechItem == graphItem);
+ if (_graph_recording != null)
+ {
+ _graph_recording.PushData(points);
+ }
}
}
}
@@ -745,19 +764,27 @@ namespace Tango.MachineStudio.Technician.ViewModels
/// <param name="elements">The elements.</param>
public void OnElementsRemoved(List<IElementEditor> elements)
{
- //foreach (var element in elements)
- //{
- // if (element.HostedElement is SingleGraphItem)
- // {
- // _singleControllers.Remove(element.HostedElement as SingleGraphItem);
- // (element.HostedElement as SingleGraphItem).Editor.InnerGraph.InnerGraph.Dispose();
- // }
- // else if (element.HostedElement is MultiGraphItem)
- // {
- // _multiControllers.Remove(element.HostedElement as MultiGraphItem);
- // (element.HostedElement as MultiGraphItem).Editor.InnerGraph.InnerGraph.Dispose();
- // }
- //}
+ foreach (var element in elements)
+ {
+ if (element.HostedElement is SingleGraphItem)
+ {
+ var _graph_recording = _single_graphs_recordings.SingleOrDefault(x => x.TechItem == element.HostedElement);
+
+ if (_graph_recording != null)
+ {
+ _single_graphs_recordings.Remove(_graph_recording);
+ }
+ }
+ else if (element.HostedElement is MultiGraphItem)
+ {
+ var _graph_recording = _multi_graph_recordings.SingleOrDefault(x => x.TechItem == element.HostedElement);
+
+ if (_graph_recording != null)
+ {
+ _multi_graph_recordings.Remove(_graph_recording);
+ }
+ }
+ }
}
/// <summary>
@@ -1023,6 +1050,39 @@ namespace Tango.MachineStudio.Technician.ViewModels
editor.InnerGraph.Controller = controller;
_singleControllers.Add(item, controller);
+
+ item.RecordingStarted += () =>
+ {
+ _single_graphs_recordings.Add(new SingleTechRecordingData<SingleGraphItem>(item));
+ };
+
+ item.RecordingStopped += () =>
+ {
+ try
+ {
+ var graph_recording = _single_graphs_recordings.SingleOrDefault(x => x.TechItem == item);
+
+ if (graph_recording != null)
+ {
+ _single_graphs_recordings.Remove(graph_recording);
+
+ SaveFileDialog dlg = new SaveFileDialog();
+ dlg.Title = "Save graph data as csv file";
+ dlg.Filter = "CSV Files|*.csv";
+ dlg.DefaultExt = ".csv";
+ dlg.FileName = item.TechName;
+ if (dlg.ShowDialog().Value)
+ {
+ graph_recording.Save(dlg.FileName);
+ graph_recording.Dispose();
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ _notification.ShowError(LogManager.Log(ex).Message);
+ }
+ };
}
/// <summary>
@@ -1050,6 +1110,39 @@ namespace Tango.MachineStudio.Technician.ViewModels
editor.InnerGraph.Controller = controller;
_multiControllers.Add(item, controller);
+
+ item.RecordingStarted += () =>
+ {
+ _multi_graph_recordings.Add(new MultiTechRecordingData<MultiGraphItem>(item, item.TechMonitor.ChannelCount));
+ };
+
+ item.RecordingStopped += () =>
+ {
+ try
+ {
+ var graph_recording = _multi_graph_recordings.SingleOrDefault(x => x.TechItem == item);
+
+ if (graph_recording != null)
+ {
+ _multi_graph_recordings.Remove(graph_recording);
+
+ SaveFileDialog dlg = new SaveFileDialog();
+ dlg.Title = "Save graph data as csv file";
+ dlg.Filter = "CSV Files|*.csv";
+ dlg.DefaultExt = ".csv";
+ dlg.FileName = item.TechName;
+ if (dlg.ShowDialog().Value)
+ {
+ graph_recording.Save(dlg.FileName);
+ graph_recording.Dispose();
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ _notification.ShowError(LogManager.Log(ex).Message);
+ }
+ };
}
/// <summary>
diff --git a/Software/Visual_Studio/Tango.CSV/CsvDefinition.cs b/Software/Visual_Studio/Tango.CSV/CsvDefinition.cs
new file mode 100644
index 000000000..350c5ed8e
--- /dev/null
+++ b/Software/Visual_Studio/Tango.CSV/CsvDefinition.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Collections.Concurrent;
+using System.Threading.Tasks;
+
+namespace Tango.CSV
+{
+ /// <summary>
+ /// Represents a <see cref="CsvFile"/> configuration.
+ /// </summary>
+ public class CsvDefinition
+ {
+ /// <summary>
+ /// Gets or sets the header.
+ /// </summary>
+ public string Header { get; set; }
+
+ /// <summary>
+ /// Gets or sets the field separator.
+ /// </summary>
+ public char FieldSeparator { get; set; }
+
+ /// <summary>
+ /// Gets or sets the text qualifier.
+ /// </summary>
+ public char TextQualifier { get; set; }
+
+ /// <summary>
+ /// Gets or sets the columns.
+ /// </summary>
+ public IEnumerable<String> Columns { get; set; }
+
+ /// <summary>
+ /// Gets or sets the end of line.
+ /// </summary>
+ public string EndOfLine { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CsvDefinition"/> class.
+ /// </summary>
+ public CsvDefinition()
+ {
+ if (CsvFile.DefaultCsvDefinition != null)
+ {
+ FieldSeparator = CsvFile.DefaultCsvDefinition.FieldSeparator;
+ TextQualifier = CsvFile.DefaultCsvDefinition.TextQualifier;
+ EndOfLine = CsvFile.DefaultCsvDefinition.EndOfLine;
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.CSV/CsvDestination.cs b/Software/Visual_Studio/Tango.CSV/CsvDestination.cs
new file mode 100644
index 000000000..d88a34684
--- /dev/null
+++ b/Software/Visual_Studio/Tango.CSV/CsvDestination.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Collections.Concurrent;
+using System.Threading.Tasks;
+
+namespace Tango.CSV
+{
+ /// <summary>
+ /// Represents a <see cref="CsvFile"/> destination.
+ /// </summary>
+ public class CsvDestination
+ {
+ public StreamWriter StreamWriter;
+
+ /// <summary>
+ /// Performs an implicit conversion from <see cref="System.String"/> to <see cref="CsvDestination"/>.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <returns>
+ /// The result of the conversion.
+ /// </returns>
+ public static implicit operator CsvDestination(string path)
+ {
+ return new CsvDestination(path);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CsvDestination"/> class.
+ /// </summary>
+ /// <param name="streamWriter">The stream writer.</param>
+ private CsvDestination(StreamWriter streamWriter)
+ {
+ this.StreamWriter = streamWriter;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CsvDestination"/> class.
+ /// </summary>
+ /// <param name="stream">The stream.</param>
+ private CsvDestination(Stream stream)
+ {
+ this.StreamWriter = new StreamWriter(stream);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CsvDestination"/> class.
+ /// </summary>
+ /// <param name="fullName">The full name.</param>
+ public CsvDestination(string fullName)
+ {
+ FixCsvFileName(ref fullName);
+ this.StreamWriter = new StreamWriter(fullName);
+ }
+
+ /// <summary>
+ /// Fixes the name of the CSV file.
+ /// </summary>
+ /// <param name="fullName">The full name.</param>
+ private static void FixCsvFileName(ref string fullName)
+ {
+ fullName = Path.GetFullPath(fullName);
+ var path = Path.GetDirectoryName(fullName);
+ if (path != null && !Directory.Exists(path))
+ Directory.CreateDirectory(path);
+ if (!String.Equals(Path.GetExtension(fullName), ".csv"))
+ fullName += ".csv";
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.CSV/CsvFile.cs b/Software/Visual_Studio/Tango.CSV/CsvFile.cs
new file mode 100644
index 000000000..ab5466da7
--- /dev/null
+++ b/Software/Visual_Studio/Tango.CSV/CsvFile.cs
@@ -0,0 +1,430 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Collections.Concurrent;
+using System.Threading.Tasks;
+
+namespace Tango.CSV
+{
+ /// <summary>
+ /// Represents a component for reading and writing CSV files.
+ /// </summary>
+ /// <seealso cref="System.IDisposable" />
+ public class CsvFile : IDisposable
+ {
+ internal protected Stream BaseStream;
+ protected static DateTime DateTimeZero = new DateTime();
+
+ /// <summary>
+ /// Initializes the <see cref="CsvFile"/> class.
+ /// </summary>
+ static CsvFile()
+ {
+ DefaultCsvDefinition = new CsvDefinition
+ {
+ EndOfLine = "\r\n",
+ FieldSeparator = ',',
+ TextQualifier = '"'
+ };
+ UseLambdas = true;
+ UseTasks = true;
+ FastIndexOfAny = true;
+ }
+
+ /// <summary>
+ /// Gets or sets the default CSV definition.
+ /// </summary>
+ public static CsvDefinition DefaultCsvDefinition { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether [use lambdas].
+ /// </summary>
+ public static bool UseLambdas { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether [use tasks].
+ /// </summary>
+ public static bool UseTasks { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether [fast index of any].
+ /// </summary>
+ public static bool FastIndexOfAny { get; set; }
+
+ /// <summary>
+ /// Reads the specified CSV source.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="csvSource">The CSV source.</param>
+ /// <returns></returns>
+ public static IEnumerable<T> Read<T>(CsvSource csvSource) where T : new()
+ {
+ var csvFileReader = new CsvFileReader<T>(csvSource);
+ return (IEnumerable<T>)csvFileReader;
+ }
+
+ /// <summary>
+ /// Gets the field separator.
+ /// </summary>
+ /// <value>
+ /// The field separator.
+ /// </value>
+ public char FieldSeparator { get; private set; }
+
+ /// <summary>
+ /// Gets the text qualifier.
+ /// </summary>
+ /// <value>
+ /// The text qualifier.
+ /// </value>
+ public char TextQualifier { get; private set; }
+
+ /// <summary>
+ /// Gets the columns.
+ /// </summary>
+ /// <value>
+ /// The columns.
+ /// </value>
+ public IEnumerable<String> Columns { get; private set; }
+
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// </summary>
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources.
+ /// </summary>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool disposing)
+ {
+ // overriden in derived classes
+ }
+ }
+
+ /// <summary>
+ /// Represents a component for reading and writing CSV files from and to a collection of objects.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <seealso cref="System.IDisposable" />
+ public class CsvFile<T> : CsvFile
+ {
+ private readonly char fieldSeparator;
+ private readonly string fieldSeparatorAsString;
+ private readonly char[] invalidCharsInFields;
+ private readonly StreamWriter streamWriter;
+ private readonly char textQualifier;
+ private readonly String[] columns;
+ private Func<T, object>[] getters;
+ readonly bool[] isInvalidCharInFields;
+ private int linesToWrite;
+ private readonly BlockingCollection<string> csvLinesToWrite = new BlockingCollection<string>(5000);
+ private readonly Thread writeCsvLinesTask;
+ private Task addAsyncTask;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CsvFile{T}"/> class.
+ /// </summary>
+ /// <param name="csvDestination">The CSV destination.</param>
+ public CsvFile(CsvDestination csvDestination)
+ : this(csvDestination, null)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CsvFile{T}"/> class.
+ /// </summary>
+ public CsvFile()
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CsvFile{T}"/> class.
+ /// </summary>
+ /// <param name="csvDestination">The CSV destination.</param>
+ /// <param name="csvDefinition">The CSV definition.</param>
+ public CsvFile(CsvDestination csvDestination, CsvDefinition csvDefinition)
+ {
+ if (csvDefinition == null)
+ csvDefinition = DefaultCsvDefinition;
+ this.columns = (csvDefinition.Columns ?? InferColumns(typeof(T))).ToArray();
+ this.fieldSeparator = csvDefinition.FieldSeparator;
+ this.fieldSeparatorAsString = this.fieldSeparator.ToString(CultureInfo.InvariantCulture);
+ this.textQualifier = csvDefinition.TextQualifier;
+ this.streamWriter = csvDestination.StreamWriter;
+
+ this.invalidCharsInFields = new[] { '\r', '\n', this.textQualifier, this.fieldSeparator };
+ this.isInvalidCharInFields = new bool[256];
+
+ foreach (var c in this.invalidCharsInFields)
+ {
+ this.isInvalidCharInFields[c] = true;
+ }
+ this.WriteHeader();
+
+ this.CreateGetters();
+ if (CsvFile.UseTasks)
+ {
+ writeCsvLinesTask = new Thread((o) => this.WriteCsvLines());
+ writeCsvLinesTask.Start();
+ }
+ this.addAsyncTask = Task.Factory.StartNew(() => { });
+
+ }
+
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources.
+ /// </summary>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ // free managed resources
+ addAsyncTask.Wait();
+ if (csvLinesToWrite != null)
+ {
+ csvLinesToWrite.CompleteAdding();
+ }
+ if (writeCsvLinesTask != null)
+ writeCsvLinesTask.Join();
+ this.streamWriter.Close();
+ }
+ }
+
+ /// <summary>
+ /// Infers the columns.
+ /// </summary>
+ /// <param name="recordType">Type of the record.</param>
+ /// <returns></returns>
+ protected static IEnumerable<string> InferColumns(Type recordType)
+ {
+ var columns = recordType
+ .GetProperties(BindingFlags.Public | BindingFlags.Instance)
+ .Where(pi => pi.GetIndexParameters().Length == 0
+ && pi.GetSetMethod() != null
+ && !Attribute.IsDefined(pi, typeof(CsvIgnoreAttribute)))
+ .Select(pi => pi.Name)
+ .Concat(recordType
+ .GetFields(BindingFlags.Public | BindingFlags.Instance)
+ .Where(fi => !Attribute.IsDefined(fi, typeof(CsvIgnoreAttribute)))
+ .Select(fi => fi.Name))
+ .ToList();
+ return columns;
+ }
+
+ /// <summary>
+ /// Writes the CSV lines.
+ /// </summary>
+ private void WriteCsvLines()
+ {
+ int written = 0;
+ foreach (var csvLine in csvLinesToWrite.GetConsumingEnumerable())
+ {
+ this.streamWriter.WriteLine(csvLine);
+ written++;
+ }
+ Interlocked.Add(ref this.linesToWrite, -written);
+ }
+
+ /// <summary>
+ /// Appends the specified record.
+ /// </summary>
+ /// <param name="record">The record.</param>
+ public void Append(T record)
+ {
+
+ if (CsvFile.UseTasks)
+ {
+
+ var linesWaiting = Interlocked.Increment(ref this.linesToWrite);
+ Action<Task> addRecord = (t) =>
+ {
+ var csvLine = this.ToCsv(record);
+ this.csvLinesToWrite.Add(csvLine);
+ };
+
+ if (linesWaiting < 10000)
+ this.addAsyncTask = this.addAsyncTask.ContinueWith(addRecord);
+ else
+ addRecord(null);
+ }
+ else
+ {
+ var csvLine = this.ToCsv(record);
+ this.streamWriter.WriteLine(csvLine);
+ }
+ }
+
+ /// <summary>
+ /// Finds the getter.
+ /// </summary>
+ /// <param name="c">The c.</param>
+ /// <param name="staticMember">if set to <c>true</c> [static member].</param>
+ /// <returns></returns>
+ private static Func<T, object> FindGetter(string c, int index, bool staticMember)
+ {
+ var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase | (staticMember ? BindingFlags.Static : BindingFlags.Instance);
+ Func<T, object> func = null;
+
+ PropertyInfo pi = null;
+ pi = typeof(T).GetProperty(c, flags);
+
+ if (pi == null) //Then try get by column index,
+ {
+ pi = typeof(T).GetProperties(flags)[index];
+ }
+
+ if (CsvFile.UseLambdas)
+ {
+ Expression expr = null;
+ ParameterExpression parameter = Expression.Parameter(typeof(T), "r");
+ Type type = null;
+
+ if (pi != null)
+ {
+ type = pi.PropertyType;
+ expr = Expression.Property(parameter, pi.Name);
+ }
+ if (expr != null)
+ {
+ Expression<Func<T, object>> lambda;
+ if (type.IsValueType)
+ {
+ lambda = Expression.Lambda<Func<T, object>>(Expression.TypeAs(expr, typeof(object)), parameter);
+ }
+ else
+ {
+ lambda = Expression.Lambda<Func<T, object>>(expr, parameter);
+ }
+ func = lambda.Compile();
+ }
+ }
+ else
+ {
+ if (pi != null)
+ func = o => pi.GetValue(o, null);
+ }
+ return func;
+ }
+
+ /// <summary>
+ /// Creates the getters.
+ /// </summary>
+ private void CreateGetters()
+ {
+ var list = new List<Func<T, object>>();
+
+ for (int i = 0; i < columns.Length; i++)
+ {
+ Func<T, Object> func = null;
+ var propertyName = (columns[i].IndexOf(' ') < 0 ? columns[i] : columns[i].Replace(" ", ""));
+ func = FindGetter(columns[i], i, false) ?? FindGetter(columns[i], i, true);
+
+ list.Add(func);
+ }
+ this.getters = list.ToArray();
+ }
+
+ /// <summary>
+ /// To the CSV.
+ /// </summary>
+ /// <param name="record">The record.</param>
+ /// <returns></returns>
+ /// <exception cref="System.ArgumentException">Cannot be null;record</exception>
+ private string ToCsv(T record)
+ {
+ if (record == null)
+ throw new ArgumentException("Cannot be null", "record");
+
+ string[] csvStrings = new string[getters.Length];
+
+ for (int i = 0; i < getters.Length; i++)
+ {
+ var getter = getters[i];
+ object fieldValue = getter == null ? null : getter(record);
+ csvStrings[i] = this.ToCsvString(fieldValue);
+ }
+ return string.Join(this.fieldSeparatorAsString, csvStrings);
+
+ }
+
+ /// <summary>
+ /// To the CSV string.
+ /// </summary>
+ /// <param name="o">The o.</param>
+ /// <returns></returns>
+ private string ToCsvString(object o)
+ {
+ if (o != null)
+ {
+ string valueString = o as string ?? Convert.ToString(o, CultureInfo.CurrentUICulture);
+ if (RequireQuotes(valueString))
+ {
+ var csvLine = new StringBuilder();
+ csvLine.Append(this.textQualifier);
+ foreach (char c in valueString)
+ {
+ if (c == this.textQualifier)
+ csvLine.Append(c); // double the double quotes
+ csvLine.Append(c);
+ }
+ csvLine.Append(this.textQualifier);
+ return csvLine.ToString();
+ }
+ else
+ return valueString;
+ }
+ return string.Empty;
+ }
+
+ /// <summary>
+ /// Requires the quotes.
+ /// </summary>
+ /// <param name="valueString">The value string.</param>
+ /// <returns></returns>
+ private bool RequireQuotes(string valueString)
+ {
+ if (CsvFile.FastIndexOfAny)
+ {
+ var len = valueString.Length;
+ for (int i = 0; i < len; i++)
+ {
+ char c = valueString[i];
+ if (c <= 255 && this.isInvalidCharInFields[c])
+ return true;
+ }
+ return false;
+ }
+ else
+ {
+ return valueString.IndexOfAny(this.invalidCharsInFields) >= 0;
+ }
+ }
+
+ /// <summary>
+ /// Writes the header.
+ /// </summary>
+ private void WriteHeader()
+ {
+ var csvLine = new StringBuilder();
+ for (int i = 0; i < this.columns.Length; i++)
+ {
+ if (i > 0)
+ csvLine.Append(this.fieldSeparator);
+ csvLine.Append(this.ToCsvString(this.columns[i]));
+ }
+ this.streamWriter.WriteLine(csvLine.ToString());
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.CSV/CsvFileLinqExtensions.cs b/Software/Visual_Studio/Tango.CSV/CsvFileLinqExtensions.cs
new file mode 100644
index 000000000..8f99b2014
--- /dev/null
+++ b/Software/Visual_Studio/Tango.CSV/CsvFileLinqExtensions.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Collections.Concurrent;
+using System.Threading.Tasks;
+using Tango.CSV;
+
+
+public static class CsvFileLinqExtensions
+{
+ /// <summary>
+ /// Export the collection to CSV file.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="source">The source.</param>
+ /// <param name="csvDestination">The CSV destination.</param>
+ public static void ToCsv<T>(this IEnumerable<T> source, CsvDestination csvDestination)
+ {
+ source.ToCsv(csvDestination, null);
+ }
+
+ /// <summary>
+ /// Export the collection to CSV file.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="source">The source.</param>
+ /// <param name="csvDestination">The CSV destination.</param>
+ /// <param name="csvDefinition">The CSV definition.</param>
+ public static void ToCsv<T>(this IEnumerable<T> source, CsvDestination csvDestination, CsvDefinition csvDefinition)
+ {
+ using (var csvFile = new CsvFile<T>(csvDestination, csvDefinition))
+ {
+ foreach (var record in source)
+ {
+ csvFile.Append(record);
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.CSV/CsvFileReader.cs b/Software/Visual_Studio/Tango.CSV/CsvFileReader.cs
new file mode 100644
index 000000000..1ae9404a7
--- /dev/null
+++ b/Software/Visual_Studio/Tango.CSV/CsvFileReader.cs
@@ -0,0 +1,425 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Collections.Concurrent;
+using System.Threading.Tasks;
+
+namespace Tango.CSV
+{
+ /// <summary>
+ /// Represents a CSV file reader.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <seealso cref="Tango.CSV.CsvFile" />
+ /// <seealso cref="System.Collections.Generic.IEnumerable{T}" />
+ /// <seealso cref="System.Collections.Generic.IEnumerator{T}" />
+ internal class CsvFileReader<T> : CsvFile, IEnumerable<T>, IEnumerator<T>
+ where T : new()
+ {
+ private readonly Dictionary<Type, List<Action<T, String>>> allSetters = new Dictionary<Type, List<Action<T, String>>>();
+ private string[] columns;
+ private char curChar;
+ private int len;
+ private string line;
+ private int pos;
+ private T record;
+ private readonly char fieldSeparator;
+ private readonly TextReader textReader;
+ private readonly char textQualifier;
+ private readonly StringBuilder parseFieldResult = new StringBuilder();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CsvFileReader{T}"/> class.
+ /// </summary>
+ /// <param name="csvSource">The CSV source.</param>
+ public CsvFileReader(CsvSource csvSource)
+ : this(csvSource, null)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CsvFileReader{T}"/> class.
+ /// </summary>
+ /// <param name="csvSource">The CSV source.</param>
+ /// <param name="csvDefinition">The CSV definition.</param>
+ public CsvFileReader(CsvSource csvSource, CsvDefinition csvDefinition)
+ {
+ var streamReader = csvSource.TextReader as StreamReader;
+ if (streamReader != null)
+ this.BaseStream = streamReader.BaseStream;
+ if (csvDefinition == null)
+ csvDefinition = DefaultCsvDefinition;
+ this.fieldSeparator = csvDefinition.FieldSeparator;
+ this.textQualifier = csvDefinition.TextQualifier;
+
+ this.textReader = csvSource.TextReader;// new FileStream(csvSource.TextReader, FileMode.Open);
+
+ this.ReadHeader(csvDefinition.Header);
+
+ }
+
+ /// <summary>
+ /// Gets the element in the collection at the current position of the enumerator.
+ /// </summary>
+ public T Current
+ {
+ get { return this.record; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this <see cref="CsvFileReader{T}"/> is EOF.
+ /// </summary>
+ public bool Eof
+ {
+ get { return this.line == null; }
+ }
+
+ /// <summary>
+ /// Gets the element in the collection at the current position of the enumerator.
+ /// </summary>
+ object IEnumerator.Current
+ {
+ get { return this.Current; }
+ }
+
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources.
+ /// </summary>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ // free managed resources
+ this.textReader.Dispose();
+ }
+ }
+
+ /// <summary>
+ /// Returns an enumerator that iterates through a collection.
+ /// </summary>
+ /// <returns>
+ /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
+ /// </returns>
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+
+ /// <summary>
+ /// Returns an enumerator that iterates through the collection.
+ /// </summary>
+ /// <returns>
+ /// An enumerator that can be used to iterate through the collection.
+ /// </returns>
+ public IEnumerator<T> GetEnumerator()
+ {
+ return this;
+ }
+
+ /// <summary>
+ /// Advances the enumerator to the next element of the collection.
+ /// </summary>
+ /// <returns>
+ /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
+ /// </returns>
+ public bool MoveNext()
+ {
+ this.ReadNextLine();
+ if (this.line == null && (this.line = this.textReader.ReadLine()) == null)
+ {
+ this.record = default(T);
+ }
+ else
+ {
+ this.record = new T();
+ Type recordType = typeof(T);
+ List<Action<T, String>> setters;
+ if (!this.allSetters.TryGetValue(recordType, out setters))
+ {
+ setters = this.CreateSetters();
+ this.allSetters[recordType] = setters;
+ }
+
+ var fieldValues = new string[setters.Count];
+ for (int i = 0; i < setters.Count; i++)
+ {
+ fieldValues[i] = this.ParseField();
+ if (this.curChar == this.fieldSeparator)
+ this.NextChar();
+ else
+ break;
+ }
+ for (int i = 0; i < setters.Count; i++)
+ {
+ var setter = setters[i];
+ if (setter != null)
+ {
+ setter(this.record, fieldValues[i]);
+ }
+ }
+ }
+ return (this.record != null);
+ }
+
+ /// <summary>
+ /// Sets the enumerator to its initial position, which is before the first element in the collection.
+ /// </summary>
+ /// <exception cref="System.NotImplementedException">Cannot reset CsvFileReader enumeration.</exception>
+ public void Reset()
+ {
+ throw new NotImplementedException("Cannot reset CsvFileReader enumeration.");
+ }
+
+ /// <summary>
+ /// Emits the set value action.
+ /// </summary>
+ /// <param name="mi">The mi.</param>
+ /// <param name="func">The function.</param>
+ /// <returns></returns>
+ /// <exception cref="System.NotImplementedException"></exception>
+ private static Action<T, string> EmitSetValueAction(MemberInfo mi, Func<string, object> func)
+ {
+ ParameterExpression paramExpObj = Expression.Parameter(typeof(object), "obj");
+ ParameterExpression paramExpT = Expression.Parameter(typeof(T), "instance");
+
+ {
+ var pi = mi as PropertyInfo;
+ if (pi != null)
+ {
+ if (CsvFile.UseLambdas)
+ {
+ var callExpr = Expression.Call(
+ paramExpT,
+ pi.GetSetMethod(),
+ Expression.ConvertChecked(paramExpObj, pi.PropertyType));
+ var setter = Expression.Lambda<Action<T, object>>(
+ callExpr,
+ paramExpT,
+ paramExpObj).Compile();
+ return (o, s) => setter(o, func(s));
+ }
+ return (o, v) => pi.SetValue(o, (object)func(v), null);
+
+ }
+ }
+ {
+ var fi = mi as FieldInfo;
+ if (fi != null)
+ {
+ if (CsvFile.UseLambdas)
+ {
+ //ParameterExpression valueExp = Expression.Parameter(typeof(string), "value");
+ var valueExp = Expression.ConvertChecked(paramExpObj, fi.FieldType);
+
+ // Expression.Property can be used here as well
+ MemberExpression fieldExp = Expression.Field(paramExpT, fi);
+ BinaryExpression assignExp = Expression.Assign(fieldExp, valueExp);
+
+ var setter = Expression.Lambda<Action<T, object>>
+ (assignExp, paramExpT, paramExpObj).Compile();
+
+ return (o, s) => setter(o, func(s));
+ }
+ return ((o, v) => fi.SetValue(o, func(v)));
+ }
+ }
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Finds the setter.
+ /// </summary>
+ /// <param name="c">The c.</param>
+ /// <param name="staticMember">if set to <c>true</c> [static member].</param>
+ /// <returns></returns>
+ private static Action<T, string> FindSetter(string c, bool staticMember)
+ {
+ var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase | (staticMember ? BindingFlags.Static : BindingFlags.Instance);
+ Action<T, string> action = null;
+ PropertyInfo pi = typeof(T).GetProperty(c, flags);
+ if (pi != null)
+ {
+ var pFunc = StringToObject(pi.PropertyType);
+ action = EmitSetValueAction(pi, pFunc);
+ }
+ FieldInfo fi = typeof(T).GetField(c, flags);
+ if (fi != null)
+ {
+ var fFunc = StringToObject(fi.FieldType);
+ action = EmitSetValueAction(fi, fFunc);
+ }
+ return action;
+ }
+
+ /// <summary>
+ /// Strings to object.
+ /// </summary>
+ /// <param name="propertyType">Type of the property.</param>
+ /// <returns></returns>
+ /// <exception cref="System.NotImplementedException"></exception>
+ private static Func<string, object> StringToObject(Type propertyType)
+ {
+ if (propertyType == typeof(string))
+ return (s) => s ?? String.Empty;
+ else if (propertyType == typeof(Int32))
+ return (s) => String.IsNullOrEmpty(s) ? 0 : Int32.Parse(s);
+ if (propertyType == typeof(DateTime))
+ return (s) => String.IsNullOrEmpty(s) ? DateTimeZero : DateTime.Parse(s);
+ else
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Creates the setters.
+ /// </summary>
+ /// <returns></returns>
+ private List<Action<T, string>> CreateSetters()
+ {
+ var list = new List<Action<T, string>>();
+ for (int i = 0; i < this.columns.Length; i++)
+ {
+ string columnName = this.columns[i];
+ Action<T, string> action = null;
+ if (columnName.IndexOf(' ') >= 0)
+ columnName = columnName.Replace(" ", "");
+ action = FindSetter(columnName, false) ?? FindSetter(columnName, true);
+
+ list.Add(action);
+ }
+ return list;
+ }
+
+ /// <summary>
+ /// Next character.
+ /// </summary>
+ private void NextChar()
+ {
+ if (this.pos < this.len)
+ {
+ this.pos++;
+ this.curChar = this.pos < this.len ? this.line[this.pos] : '\0';
+ }
+ }
+
+ /// <summary>
+ /// Parses the end of line.
+ /// </summary>
+ /// <exception cref="System.NotImplementedException"></exception>
+ private void ParseEndOfLine()
+ {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Parses the field.
+ /// </summary>
+ /// <returns></returns>
+ private string ParseField()
+ {
+ parseFieldResult.Length = 0;
+ if (this.line == null || this.pos >= this.len)
+ return null;
+ while (this.curChar == ' ' || this.curChar == '\t')
+ {
+ this.NextChar();
+ }
+ if (this.curChar == this.textQualifier)
+ {
+ this.NextChar();
+ while (this.curChar != 0)
+ {
+ if (this.curChar == this.textQualifier)
+ {
+ this.NextChar();
+ if (this.curChar == this.textQualifier)
+ {
+ this.NextChar();
+ parseFieldResult.Append(this.textQualifier);
+ }
+ else
+ return parseFieldResult.ToString();
+ }
+ else if (this.curChar == '\0')
+ {
+ if (this.line == null)
+ return parseFieldResult.ToString();
+ this.ReadNextLine();
+ }
+ else
+ {
+ parseFieldResult.Append(this.curChar);
+ this.NextChar();
+ }
+ }
+ }
+ else
+ {
+ while (this.curChar != 0 && this.curChar != this.fieldSeparator && this.curChar != '\r' && this.curChar != '\n')
+ {
+ parseFieldResult.Append(this.curChar);
+ this.NextChar();
+ }
+ }
+ return parseFieldResult.ToString();
+ }
+
+ /// <summary>
+ /// Reads the header.
+ /// </summary>
+ /// <param name="header">The header.</param>
+ private void ReadHeader(string header)
+ {
+ if (header == null)
+ {
+ this.ReadNextLine();
+ }
+ else
+ {
+ // we read the first line from the given header
+ this.line = header;
+ this.pos = -1;
+ this.len = this.line.Length;
+ this.NextChar();
+ }
+
+ var readColumns = new List<string>();
+ string columnName;
+ while ((columnName = this.ParseField()) != null)
+ {
+ readColumns.Add(columnName);
+ if (this.curChar == this.fieldSeparator)
+ this.NextChar();
+ else
+ break;
+ }
+ this.columns = readColumns.ToArray();
+ }
+
+ /// <summary>
+ /// Reads the next line.
+ /// </summary>
+ private void ReadNextLine()
+ {
+ this.line = this.textReader.ReadLine();
+ this.pos = -1;
+ if (this.line == null)
+ {
+ this.len = 0;
+ this.curChar = '\0';
+ }
+ else
+ {
+ this.len = this.line.Length;
+ this.NextChar();
+ }
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.CSV/CsvIgnoreAttribute.cs b/Software/Visual_Studio/Tango.CSV/CsvIgnoreAttribute.cs
new file mode 100644
index 000000000..87ae77516
--- /dev/null
+++ b/Software/Visual_Studio/Tango.CSV/CsvIgnoreAttribute.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Collections.Concurrent;
+using System.Threading.Tasks;
+
+namespace Tango.CSV
+{
+ /// <summary>
+ /// Represents an attribute for decorating properties to exclude from the CSV file.
+ /// </summary>
+ /// <seealso cref="System.Attribute" />
+ public class CsvIgnoreAttribute : Attribute
+ {
+ public override string ToString()
+ {
+ return "Ignore Property";
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.CSV/CsvSource.cs b/Software/Visual_Studio/Tango.CSV/CsvSource.cs
new file mode 100644
index 000000000..ba7e458e6
--- /dev/null
+++ b/Software/Visual_Studio/Tango.CSV/CsvSource.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Collections.Concurrent;
+using System.Threading.Tasks;
+
+namespace Tango.CSV
+{
+ /// <summary>
+ /// Represents a <see cref="CsvFile"/> source.
+ /// </summary>
+ public class CsvSource
+ {
+ public readonly TextReader TextReader;
+
+ /// <summary>
+ /// Performs an implicit conversion from <see cref="CsvFile"/> to <see cref="CsvSource"/>.
+ /// </summary>
+ /// <param name="csvFile">The CSV file.</param>
+ /// <returns>
+ /// The result of the conversion.
+ /// </returns>
+ public static implicit operator CsvSource(CsvFile csvFile)
+ {
+ return new CsvSource(csvFile);
+ }
+
+ /// <summary>
+ /// Performs an implicit conversion from <see cref="System.String"/> to <see cref="CsvSource"/>.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <returns>
+ /// The result of the conversion.
+ /// </returns>
+ public static implicit operator CsvSource(string path)
+ {
+ return new CsvSource(path);
+ }
+
+ /// <summary>
+ /// Performs an implicit conversion from <see cref="TextReader"/> to <see cref="CsvSource"/>.
+ /// </summary>
+ /// <param name="textReader">The text reader.</param>
+ /// <returns>
+ /// The result of the conversion.
+ /// </returns>
+ public static implicit operator CsvSource(TextReader textReader)
+ {
+ return new CsvSource(textReader);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CsvSource"/> class.
+ /// </summary>
+ /// <param name="textReader">The text reader.</param>
+ public CsvSource(TextReader textReader)
+ {
+ this.TextReader = textReader;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CsvSource"/> class.
+ /// </summary>
+ /// <param name="stream">The stream.</param>
+ public CsvSource(Stream stream)
+ {
+ this.TextReader = new StreamReader(stream);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CsvSource"/> class.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ public CsvSource(string path)
+ {
+ this.TextReader = new StreamReader(path);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CsvSource"/> class.
+ /// </summary>
+ /// <param name="csvFile">The CSV file.</param>
+ public CsvSource(CsvFile csvFile)
+ {
+ this.TextReader = new StreamReader(csvFile.BaseStream);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/Tango.CSV/Properties/AssemblyInfo.cs b/Software/Visual_Studio/Tango.CSV/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..9696745ee
--- /dev/null
+++ b/Software/Visual_Studio/Tango.CSV/Properties/AssemblyInfo.cs
@@ -0,0 +1,9 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+[assembly: AssemblyTitle("Tango - CSV Library")]
+[assembly: AssemblyVersion("2.0.17.1657")]
+[assembly: ComVisible(false)] \ No newline at end of file
diff --git a/Software/Visual_Studio/Tango.CSV/Tango.CSV.csproj b/Software/Visual_Studio/Tango.CSV/Tango.CSV.csproj
new file mode 100644
index 000000000..48bda8bdb
--- /dev/null
+++ b/Software/Visual_Studio/Tango.CSV/Tango.CSV.csproj
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{58E8825F-0C96-449C-B320-1E82B0AA876B}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Tango.CSV</RootNamespace>
+ <AssemblyName>Tango.CSV</AssemblyName>
+ <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <TargetFrameworkProfile />
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>..\Build\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>..\Build\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\x64\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <DebugType>full</DebugType>
+ <PlatformTarget>x64</PlatformTarget>
+ <ErrorReport>prompt</ErrorReport>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+ <OutputPath>bin\x64\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <Optimize>true</Optimize>
+ <DebugType>pdbonly</DebugType>
+ <PlatformTarget>x64</PlatformTarget>
+ <ErrorReport>prompt</ErrorReport>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\x86\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <DebugType>full</DebugType>
+ <PlatformTarget>x86</PlatformTarget>
+ <ErrorReport>prompt</ErrorReport>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
+ <OutputPath>bin\x86\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <Optimize>true</Optimize>
+ <DebugType>pdbonly</DebugType>
+ <PlatformTarget>x86</PlatformTarget>
+ <ErrorReport>prompt</ErrorReport>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="..\Versioning\GlobalVersionInfo.cs">
+ <Link>GlobalVersionInfo.cs</Link>
+ </Compile>
+ <Compile Include="CsvDefinition.cs" />
+ <Compile Include="CsvDestination.cs" />
+ <Compile Include="CsvFile.cs" />
+ <Compile Include="CsvFileLinqExtensions.cs" />
+ <Compile Include="CsvFileReader.cs" />
+ <Compile Include="CsvIgnoreAttribute.cs" />
+ <Compile Include="CsvSource.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ No newline at end of file
diff --git a/Software/Visual_Studio/Tango.sln b/Software/Visual_Studio/Tango.sln
index ed2b5f28e..65b1ca8e8 100644
--- a/Software/Visual_Studio/Tango.sln
+++ b/Software/Visual_Studio/Tango.sln
@@ -210,6 +210,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tango.AnimatedGif", "Tango.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tango.PPC.MachineSettings", "PPC\Modules\Tango.PPC.MachineSettings\Tango.PPC.MachineSettings.csproj", "{91B70E9B-66A7-4873-AE10-400E71CF404F}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tango.CSV", "Tango.CSV\Tango.CSV.csproj", "{58E8825F-0C96-449C-B320-1E82B0AA876B}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
AppVeyor|Any CPU = AppVeyor|Any CPU
@@ -3649,6 +3651,46 @@ Global
{91B70E9B-66A7-4873-AE10-400E71CF404F}.Release|x64.Build.0 = Release|Any CPU
{91B70E9B-66A7-4873-AE10-400E71CF404F}.Release|x86.ActiveCfg = Release|Any CPU
{91B70E9B-66A7-4873-AE10-400E71CF404F}.Release|x86.Build.0 = Release|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.AppVeyor|Any CPU.ActiveCfg = Release|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.AppVeyor|Any CPU.Build.0 = Release|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.AppVeyor|ARM.ActiveCfg = Release|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.AppVeyor|ARM.Build.0 = Release|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.AppVeyor|ARM64.ActiveCfg = Release|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.AppVeyor|ARM64.Build.0 = Release|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.AppVeyor|x64.ActiveCfg = Release|x64
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.AppVeyor|x64.Build.0 = Release|x64
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.AppVeyor|x86.ActiveCfg = Release|x86
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.AppVeyor|x86.Build.0 = Release|x86
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Debug|ARM.Build.0 = Debug|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Debug|x64.ActiveCfg = Debug|x64
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Debug|x64.Build.0 = Debug|x64
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Debug|x86.ActiveCfg = Debug|x86
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Debug|x86.Build.0 = Debug|x86
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.DefaultBuild|Any CPU.ActiveCfg = Debug|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.DefaultBuild|Any CPU.Build.0 = Debug|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.DefaultBuild|ARM.ActiveCfg = Debug|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.DefaultBuild|ARM.Build.0 = Debug|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.DefaultBuild|ARM64.ActiveCfg = Debug|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.DefaultBuild|ARM64.Build.0 = Debug|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.DefaultBuild|x64.ActiveCfg = Debug|x64
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.DefaultBuild|x64.Build.0 = Debug|x64
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.DefaultBuild|x86.ActiveCfg = Debug|x86
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.DefaultBuild|x86.Build.0 = Debug|x86
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Release|ARM.ActiveCfg = Release|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Release|ARM.Build.0 = Release|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Release|ARM64.Build.0 = Release|Any CPU
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Release|x64.ActiveCfg = Release|x64
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Release|x64.Build.0 = Release|x64
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Release|x86.ActiveCfg = Release|x86
+ {58E8825F-0C96-449C-B320-1E82B0AA876B}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -3715,12 +3757,12 @@ Global
{91B70E9B-66A7-4873-AE10-400E71CF404F} = {0048447D-1D94-4E60-9DAD-7349C777CB4E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
- BuildVersion_UseGlobalSettings = False
- BuildVersion_AssemblyInfoFilename = Properties\AssemblyInfo.cs
- BuildVersion_StartDate = 2000/1/1
- BuildVersion_UpdateFileVersion = False
- BuildVersion_UpdateAssemblyVersion = True
- BuildVersion_BuildVersioningStyle = None.None.Increment.DeltaBaseYearDayOfYear
SolutionGuid = {7986F7F4-A86A-4994-B1B6-0988D7F057B6}
+ BuildVersion_BuildVersioningStyle = None.None.Increment.DeltaBaseYearDayOfYear
+ BuildVersion_UpdateAssemblyVersion = True
+ BuildVersion_UpdateFileVersion = False
+ BuildVersion_StartDate = 2000/1/1
+ BuildVersion_AssemblyInfoFilename = Properties\AssemblyInfo.cs
+ BuildVersion_UseGlobalSettings = False
EndGlobalSection
EndGlobal