diff options
| author | Roy Ben-Shabat <Roy@Twine-s.com> | 2018-08-02 15:05:44 +0300 |
|---|---|---|
| committer | Roy Ben-Shabat <Roy@Twine-s.com> | 2018-08-02 15:05:44 +0300 |
| commit | 05fca4fe321600c4a9c0698b1e4c161e3ed79c9f (patch) | |
| tree | fda984b5a9ff3a3980bf9b6b5a560b3edaa4e668 /Software/Visual_Studio | |
| parent | 3499090dce4acc5b5d4bbb02f07f138950790b25 (diff) | |
| download | Tango-05fca4fe321600c4a9c0698b1e4c161e3ed79c9f.tar.gz Tango-05fca4fe321600c4a9c0698b1e4c161e3ed79c9f.zip | |
Added Tango.CSV project!
Implemented Single/Multi graph recording to CSV!
Diffstat (limited to 'Software/Visual_Studio')
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 |
