1
+ using System . Diagnostics ;
2
+ using System . Text . RegularExpressions ;
3
+ using System . Windows ;
4
+
5
+ // using System;
6
+ // using System.Collections.Generic;
7
+ // using System.Diagnostics;
8
+ // using System.Text.RegularExpressions;
9
+ // using System.Windows;
10
+ // using System.Windows.Controls;
11
+
12
+ namespace SSD
13
+ {
14
+ public partial class MainWindow : Window
15
+ {
16
+ public MainWindow ( )
17
+ {
18
+ InitializeComponent ( ) ;
19
+ }
20
+
21
+ private void ScanDevicesButton_Click ( object sender , RoutedEventArgs e )
22
+ {
23
+ var devices = GetStorageDevices ( ) ;
24
+ var deviceInfo = ListStorageDevices ( devices ) ;
25
+ DeviceDataGrid . ItemsSource = deviceInfo ;
26
+ }
27
+
28
+ private List < string > GetStorageDevices ( )
29
+ {
30
+ var devices = new List < string > ( ) ;
31
+ try
32
+ {
33
+ var process = new Process
34
+ {
35
+ StartInfo = new ProcessStartInfo
36
+ {
37
+ FileName = "smartctl" ,
38
+ Arguments = "--scan" ,
39
+ UseShellExecute = false ,
40
+ RedirectStandardOutput = true ,
41
+ CreateNoWindow = true
42
+ }
43
+ } ;
44
+
45
+ process . Start ( ) ;
46
+ while ( ! process . StandardOutput . EndOfStream )
47
+ {
48
+ devices . Add ( process . StandardOutput . ReadLine ( ) ) ;
49
+ }
50
+ process . WaitForExit ( ) ;
51
+ }
52
+ catch ( Exception ex )
53
+ {
54
+ MessageBox . Show ( $ "Error retrieving devices: { ex . Message } ", "Error" , MessageBoxButton . OK , MessageBoxImage . Error ) ;
55
+ }
56
+
57
+ return devices ;
58
+ }
59
+
60
+ private string GetSmartData ( string device )
61
+ {
62
+ try
63
+ {
64
+ var process = new Process
65
+ {
66
+ StartInfo = new ProcessStartInfo
67
+ {
68
+ FileName = "smartctl.exe" ,
69
+ Arguments = $ "-x { device } ",
70
+ UseShellExecute = false ,
71
+ RedirectStandardOutput = true ,
72
+ CreateNoWindow = true
73
+ }
74
+ } ;
75
+
76
+ process . Start ( ) ;
77
+ string output = process . StandardOutput . ReadToEnd ( ) ;
78
+ process . WaitForExit ( ) ;
79
+
80
+ return output ;
81
+ }
82
+ catch ( Exception ex )
83
+ {
84
+ MessageBox . Show ( $ "Error retrieving SMART data for { device } : { ex . Message } ", "Error" , MessageBoxButton . OK , MessageBoxImage . Error ) ;
85
+ return null ;
86
+ }
87
+ }
88
+
89
+ private DeviceInfo ParseSmartData ( string smartData )
90
+ {
91
+ var stats = new DeviceInfo ( ) ;
92
+
93
+ var healthMatch = Regex . Match ( smartData , @"SMART overall-health self-assessment test result:\s*(\w+)" ) ;
94
+ stats . HealthCondition = healthMatch . Success ? healthMatch . Groups [ 1 ] . Value : null ;
95
+
96
+ var tempMatch = Regex . Match ( smartData , @"Current Temperature:\s*(\d+)\s*Celsius" ) ;
97
+ var tempMatchSsd = Regex . Match ( smartData , @"0x05\s+0x008\s+1\s+(\d+)\s+---\s+Current Temperature" ) ;
98
+
99
+ if ( tempMatch . Success )
100
+ stats . Temperature = int . Parse ( tempMatch . Groups [ 1 ] . Value ) ;
101
+ else if ( tempMatchSsd . Success )
102
+ stats . Temperature = int . Parse ( tempMatchSsd . Groups [ 1 ] . Value ) ;
103
+
104
+ var powerOnMatch = Regex . Match ( smartData , @"^\s*9 Power_On_Hours\s+[-\w]+\s+\d+\s+\d+\s+\d+\s+-\s+(\d+)" , RegexOptions . Multiline ) ;
105
+ if ( powerOnMatch . Success )
106
+ stats . PowerOnHours = int . Parse ( powerOnMatch . Groups [ 1 ] . Value ) ;
107
+
108
+ var startStopMatch = Regex . Match ( smartData , @"^\s*4 Start_Stop_Count\s+[-\w]+\s+\d+\s+\d+\s+\d+\s+-\s+(\d+)" , RegexOptions . Multiline ) ;
109
+ var writtenGbsMatch = Regex . Match ( smartData , @"^\s*241 Lifetime_Writes_GiB\s+[-\w]+\s+\d+\s+\d+\s+\d+\s+-\s+(\d+)" , RegexOptions . Multiline ) ;
110
+
111
+ if ( startStopMatch . Success )
112
+ {
113
+ stats . StartStopCount = startStopMatch . Groups [ 1 ] . Value ;
114
+ stats . LifetimeWrittenGigs = "--- (HDD)" ;
115
+ }
116
+ else if ( writtenGbsMatch . Success )
117
+ {
118
+ stats . StartStopCount = "--- (SSD)" ;
119
+ stats . LifetimeWrittenGigs = writtenGbsMatch . Groups [ 1 ] . Value ;
120
+ }
121
+
122
+ var modelMatch = Regex . Match ( smartData , @"Device Model:\s+(.+)" ) ;
123
+ if ( modelMatch . Success )
124
+ stats . DeviceName = modelMatch . Groups [ 1 ] . Value . Trim ( ) ;
125
+
126
+ var serialMatch = Regex . Match ( smartData , @"Serial Number:\s+(.+)" ) ;
127
+ if ( serialMatch . Success )
128
+ stats . HardwareId = serialMatch . Groups [ 1 ] . Value . Trim ( ) ;
129
+
130
+ var ssdLifeLeftMatch = Regex . Match ( smartData , @"^\s*231 SSD_Life_Left\s+[-\w]+\s+\d+\s+\d+\s+\d+\s+-\s+(\d+)" , RegexOptions . Multiline ) ;
131
+ if ( ssdLifeLeftMatch . Success )
132
+ {
133
+ stats . HealthPercentage = ssdLifeLeftMatch . Groups [ 1 ] . Value . Trim ( ) ;
134
+ }
135
+ else
136
+ {
137
+ var smartDataDict = ParseSmartAttributes ( smartData ) ;
138
+
139
+ stats . HealthPercentage = Math . Round ( CalculateDiskCondition ( smartDataDict ) ) . ToString ( ) ;
140
+ }
141
+
142
+ var badSectorsMatch = Regex . Match ( smartData , @"^\s*197 Current_Pending_Sector\s+[-\w]+\s+\d+\s+\d+\s+\d+\s+-\s+(\d+)" , RegexOptions . Multiline ) ;
143
+ if ( badSectorsMatch . Success )
144
+ stats . BadSectors = badSectorsMatch . Groups [ 1 ] . Value . Trim ( ) ;
145
+ else
146
+ stats . BadSectors = "--- (SSD)" ;
147
+
148
+ return stats ;
149
+ }
150
+
151
+
152
+ private Dictionary < int , int > ParseSmartAttributes ( string smartData )
153
+ {
154
+ var smartDataDict = new Dictionary < int , int > ( ) ;
155
+ var lines = smartData . Split ( new [ ] { '\n ' , '\r ' } , StringSplitOptions . RemoveEmptyEntries ) ;
156
+
157
+ foreach ( var line in lines )
158
+ {
159
+ var parts = line . Split ( new [ ] { ' ' , '\t ' } , StringSplitOptions . RemoveEmptyEntries ) ;
160
+ if ( parts . Length < 2 || ! int . TryParse ( parts [ 0 ] , out int attrId ) )
161
+ continue ;
162
+
163
+ if ( int . TryParse ( parts [ parts . Length - 1 ] , out int rawValue ) )
164
+ {
165
+ smartDataDict [ attrId ] = rawValue ;
166
+ }
167
+ }
168
+
169
+ return smartDataDict ;
170
+ }
171
+
172
+ private double CalculateDiskCondition ( Dictionary < int , int > smartData )
173
+ {
174
+ var attributes = new Dictionary < int , ( double Weight , double Limit ) >
175
+ {
176
+ { 5 , ( 1.0 , 70 ) } ,
177
+ { 7 , ( 0.5 , 20 ) } ,
178
+ { 10 , ( 3.0 , 60 ) } ,
179
+ { 196 , ( 0.6 , 30 ) } ,
180
+ { 197 , ( 0.6 , 48 ) } ,
181
+ { 198 , ( 1.0 , 70 ) } ,
182
+ } ;
183
+
184
+ double condition = 100.0 ;
185
+
186
+ foreach ( var attr in attributes )
187
+ {
188
+ if ( smartData . TryGetValue ( attr . Key , out int rawValue ) )
189
+ {
190
+ double impact = Math . Min ( attr . Value . Weight * rawValue , attr . Value . Limit ) ;
191
+ condition -= impact ;
192
+ }
193
+ }
194
+
195
+ return Math . Max ( condition , 0 ) ;
196
+ }
197
+
198
+
199
+
200
+
201
+ private List < DeviceInfo > ListStorageDevices ( List < string > devices )
202
+ {
203
+ var deviceInfo = new List < DeviceInfo > ( ) ;
204
+
205
+ foreach ( var device in devices )
206
+ {
207
+ var deviceName = device . Split ( ) [ 0 ] ;
208
+ var smartData = GetSmartData ( deviceName ) ;
209
+
210
+ if ( ! string . IsNullOrEmpty ( smartData ) )
211
+ {
212
+ var stats = ParseSmartData ( smartData ) ;
213
+ stats . Device = deviceName ;
214
+ deviceInfo . Add ( stats ) ;
215
+ }
216
+ }
217
+
218
+ return deviceInfo ;
219
+ }
220
+ }
221
+
222
+ public class DeviceInfo
223
+ {
224
+ public string Device { get ; set ; }
225
+ public string DeviceName { get ; set ; }
226
+ public string HealthCondition { get ; set ; }
227
+ public int ? Temperature { get ; set ; }
228
+ public int ? PowerOnHours { get ; set ; }
229
+ public string StartStopCount { get ; set ; }
230
+ public string HardwareId { get ; set ; }
231
+ public string HealthPercentage { get ; set ; }
232
+ public string LifetimeWrittenGigs { get ; set ; }
233
+ public string BadSectors { get ; set ; }
234
+ }
235
+ }
0 commit comments