Skip to content

Commit 1b54764

Browse files
committed
Add project files.
1 parent 00aad7f commit 1b54764

10 files changed

+433
-0
lines changed

!Releases/v1.1/SSD.ZIP

485 KB
Binary file not shown.

!Releases/v1/SSD.zip

454 KB
Binary file not shown.

App.xaml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Application x:Class="SSD.App"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:local="clr-namespace:SSD"
5+
StartupUri="MainWindow.xaml">
6+
<Application.Resources>
7+
8+
</Application.Resources>
9+
</Application>

App.xaml.cs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System.Configuration;
2+
using System.Data;
3+
using System.Windows;
4+
5+
namespace SSD
6+
{
7+
/// <summary>
8+
/// Interaction logic for App.xaml
9+
/// </summary>
10+
public partial class App : Application
11+
{
12+
}
13+
14+
}

AssemblyInfo.cs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System.Windows;
2+
3+
[assembly: ThemeInfo(
4+
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
5+
//(used if a resource is not found in the page,
6+
// or application resource dictionaries)
7+
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
8+
//(used if a resource is not found in the page,
9+
// app, or any theme specific resource dictionaries)
10+
)]

MainWindow.xaml

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<Window x:Class="SSD.MainWindow"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
5+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6+
xmlns:local="clr-namespace:SSD"
7+
mc:Ignorable="d"
8+
Title="SSD - Specs for Storage Devices" Height="250" Width="800">
9+
<Grid>
10+
<Grid.RowDefinitions>
11+
<RowDefinition Height="Auto"/>
12+
<RowDefinition Height="*"/>
13+
</Grid.RowDefinitions>
14+
15+
<Button
16+
x:Name="ScanDevicesButton"
17+
Content="Scan Devices"
18+
Click="ScanDevicesButton_Click"
19+
Margin="10"
20+
Padding="10,5"/>
21+
22+
<DataGrid
23+
x:Name="DeviceDataGrid"
24+
Grid.Row="1"
25+
Margin="10,10,10,20"
26+
IsReadOnly="True"
27+
AutoGenerateColumns="False">
28+
<DataGrid.Columns>
29+
<DataGridTextColumn Header="Device" Binding="{Binding Device}" Width="*"/>
30+
<DataGridTextColumn Header="Model" Binding="{Binding DeviceName}" Width="*"/>
31+
<DataGridTextColumn Header="Health" Binding="{Binding HealthCondition}" Width="*"/>
32+
<DataGridTextColumn Header="Temp (°C)" Binding="{Binding Temperature}" Width="*"/>
33+
<DataGridTextColumn Header="Power-On Hours" Binding="{Binding PowerOnHours}" Width="*"/>
34+
<DataGridTextColumn Header="Start/Stop Count" Binding="{Binding StartStopCount}" Width="*"/>
35+
<DataGridTextColumn Header="HWID" Binding="{Binding HardwareId}" Width="*"/>
36+
<DataGridTextColumn Header="Health %" Binding="{Binding HealthPercentage}" Width="*"/>
37+
<DataGridTextColumn Header="GB Written" Binding="{Binding LifetimeWrittenGigs}" Width="*"/>
38+
<DataGridTextColumn Header="Bad Sectors" Binding="{Binding BadSectors}" Width="*"/>
39+
</DataGrid.Columns>
40+
</DataGrid>
41+
<Label Content="Made by NDXCode" HorizontalAlignment="Left" Margin="5,0,0,0" Grid.Row="1" VerticalAlignment="Bottom"/>
42+
43+
</Grid>
44+
</Window>

MainWindow.xaml.cs

+235
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
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+
}

SSD.csproj

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>WinExe</OutputType>
5+
<TargetFramework>net4.0-windows</TargetFramework>
6+
<LangVersion>10.0</LangVersion>
7+
<Nullable>enable</Nullable>
8+
<ImplicitUsings>enable</ImplicitUsings>
9+
<UseWPF>true</UseWPF>
10+
<ApplicationManifest>app.manifest</ApplicationManifest>
11+
</PropertyGroup>
12+
13+
<ItemGroup>
14+
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
15+
</ItemGroup>
16+
17+
</Project>

SSD.sln

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.9.34616.47
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SSD", "SSD.csproj", "{38B326DC-85C9-491A-9B81-E4894EC08771}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{38B326DC-85C9-491A-9B81-E4894EC08771}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{38B326DC-85C9-491A-9B81-E4894EC08771}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{38B326DC-85C9-491A-9B81-E4894EC08771}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{38B326DC-85C9-491A-9B81-E4894EC08771}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {16DECC73-17D1-418D-81E7-90B20007E1DE}
24+
EndGlobalSection
25+
EndGlobal

0 commit comments

Comments
 (0)