11using Avalonia . Controls ;
22using Avalonia . Layout ;
33using Avalonia . Media ;
4- using System . Runtime . InteropServices ;
54using System ;
65using CommunityToolkit . Mvvm . Input ;
76using System . Linq ;
109using Avalonia ;
1110using Avalonia . Threading ;
1211using CommunityToolkit . Mvvm . ComponentModel ;
13- using System . Diagnostics ;
14- using Microsoft . Win32 ;
1512using ProcessorLatencyTool . Helpers . LatencyTester ;
1613using ProcessorLatencyTool . Models ;
14+ using System . IO ;
15+ using System . Text ;
16+ using Avalonia . Controls . ApplicationLifetimes ;
17+ using Avalonia . Platform . Storage ;
1718
1819namespace ProcessorLatencyTool . ViewModels
1920{
2021 public partial class MainWindowViewModel : ViewModelBase
2122 {
22- public static string CpuModel
23- {
24- get
25- {
26- if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
27- {
28- using var key = Registry . LocalMachine . OpenSubKey ( @"HARDWARE\DESCRIPTION\System\CentralProcessor\0" ) ;
29- var processorName = key ? . GetValue ( "ProcessorNameString" ) ? . ToString ( ) ? . Trim ( ) ;
30-
31- return processorName ?? "Unknown CPU" ;
32- }
33- else if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) )
34- {
35- try
36- {
37- var startInfo = new ProcessStartInfo
38- {
39- FileName = "cat" ,
40- Arguments = "/proc/cpuinfo" ,
41- RedirectStandardOutput = true ,
42- UseShellExecute = false ,
43- CreateNoWindow = true
44- } ;
45-
46- using var process = Process . Start ( startInfo ) ;
47- if ( process != null )
48- {
49- var output = process . StandardOutput . ReadToEnd ( ) ;
50- process . WaitForExit ( ) ;
51- var modelName = output . Split ( '\n ' )
52- . FirstOrDefault ( line => line . StartsWith ( "model name" ) )
53- ? . Split ( ':' )
54- . LastOrDefault ( )
55- ? . Trim ( ) ;
56- return modelName ?? "Unknown CPU" ;
57- }
58- }
59- catch
60- {
61- // Ignore
62- }
63- }
64- else if ( RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) )
65- {
66- try
67- {
68- var startInfo = new ProcessStartInfo
69- {
70- FileName = "sysctl" ,
71- Arguments = "-n machdep.cpu.brand_string" ,
72- RedirectStandardOutput = true ,
73- UseShellExecute = false ,
74- CreateNoWindow = true
75- } ;
76-
77- using var process = Process . Start ( startInfo ) ;
78- if ( process != null )
79- {
80- var output = process . StandardOutput . ReadToEnd ( ) ;
81- process . WaitForExit ( ) ;
82- return output . Trim ( ) ;
83- }
84- }
85- catch
86- {
87- // Ignore
88- }
89- }
90-
91- return "Unknown CPU" ;
92- }
93- }
23+ public static string CpuModel => SystemInfoHelper . GetProcessorName ( ) ;
9424
9525 public static string CoreThreadInfo => $ "Cores: { Environment . ProcessorCount } ";
9626
97- [ ObservableProperty ] private string _progressText = "Testing latency..." ;
98- [ ObservableProperty ] private string _statusText = "Ready" ;
99- [ ObservableProperty ] private double _progressValue ;
100- [ ObservableProperty ] private string _progressPercentage = "0%" ;
27+ [ ObservableProperty ] public partial string ProgressText { get ; private set ; } = "Testing latency..." ;
28+ [ ObservableProperty ] public partial string StatusText { get ; private set ; } = "Ready" ;
29+ [ ObservableProperty ] public partial double ProgressValue { get ; private set ; }
30+ [ ObservableProperty ] public partial string ProgressPercentage { get ; private set ; } = "0%" ;
31+ [ ObservableProperty ] public partial bool HasResults { get ; private set ; }
10132
10233 private StackPanel ? _mainPanel ;
34+ private LatencyResult [ ] [ ] ? _currentMatrix ;
10335
10436 public void SetMainPanel ( StackPanel panel )
10537 {
@@ -132,6 +64,8 @@ private void DisplayInitialGrid()
13264 }
13365 }
13466
67+ _currentMatrix = matrix ;
68+ HasResults = false ;
13569 BuildLatencyGrid ( matrix , _mainPanel , true ) ;
13670 }
13771
@@ -141,6 +75,7 @@ private async Task StartMeasurementAsync()
14175 StatusText = "Starting measurements..." ;
14276 ProgressValue = 0 ;
14377 ProgressPercentage = "0%" ;
78+ HasResults = false ;
14479
14580 var progress = new Progress < ( int , int ) > ( onProgress =>
14681 {
@@ -168,9 +103,78 @@ private async Task StartMeasurementAsync()
168103 }
169104
170105 StatusText = "Measurements completed" ;
106+ HasResults = true ;
171107 } ) ;
172108 }
173109
110+ [ RelayCommand ]
111+ private async Task ExportToCsvAsync ( )
112+ {
113+ if ( _currentMatrix == null )
114+ {
115+ StatusText = "No data to export" ;
116+ return ;
117+ }
118+
119+ if ( Application . Current ? . ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop )
120+ {
121+ StatusText = "Cannot access application window" ;
122+ return ;
123+ }
124+
125+ var mainWindow = desktop . MainWindow ;
126+ if ( mainWindow == null )
127+ {
128+ StatusText = "Cannot access main window" ;
129+ return ;
130+ }
131+
132+ var file = await mainWindow . StorageProvider . SaveFilePickerAsync ( new FilePickerSaveOptions
133+ {
134+ Title = "Save Latency Data" ,
135+ DefaultExtension = "csv" ,
136+ FileTypeChoices =
137+ [
138+ new FilePickerFileType ( "CSV Files" )
139+ {
140+ Patterns = [ "*.csv" ]
141+ }
142+ ]
143+ } ) ;
144+
145+ if ( file == null )
146+ return ;
147+
148+ try
149+ {
150+ var csv = new StringBuilder ( ) ;
151+ var coreCount = _currentMatrix . Length ;
152+
153+ // Add header row
154+ csv . AppendLine ( "Source Core,Target Core,Mean Latency (ns),Standard Deviation (ns),Min Latency (ns),Max Latency (ns),Sample Count" ) ;
155+
156+ // Add data rows
157+ for ( var i = 0 ; i < coreCount ; i ++ )
158+ {
159+ for ( var j = 0 ; j < coreCount ; j ++ )
160+ {
161+ var result = _currentMatrix [ i ] [ j ] ;
162+ csv . AppendLine ( $ "{ i } ,{ j } ,{ result . MeanLatency : F2} ,{ result . StandardDeviation : F2} ,{ result . MinLatency : F2} ,{ result . MaxLatency : F2} ,{ result . SampleCount } ") ;
163+ }
164+ }
165+
166+ await using var stream = await file . OpenWriteAsync ( ) ;
167+ await using var writer = new StreamWriter ( stream ) ;
168+ await writer . WriteAsync ( csv . ToString ( ) ) ;
169+
170+ StatusText = "Data exported successfully" ;
171+ }
172+ catch ( Exception ex )
173+ {
174+ StatusText = $ "Export failed: { ex . Message } ";
175+ }
176+ }
177+
174178 private static void BuildLatencyGrid ( LatencyResult [ ] [ ] matrix , StackPanel panel , bool isInit = false )
175179 {
176180 var grid = new Grid
@@ -329,6 +333,7 @@ private async Task<LatencyResult[][]> MeasureLatencyMatrixAsync(
329333 }
330334 }
331335
336+ _currentMatrix = matrix ;
332337 return matrix ;
333338 }
334339 }
0 commit comments