Hardware/HDD/AbstractHarddrive.cs
author moel.mich
Sun, 09 Jun 2013 19:57:00 +0000
changeset 403 c540cf36b7ce
parent 385 8f16f03797f5
child 404 1201889d413a
permissions -rw-r--r--
Changed the HDD detection to list hard drives without SMART support as well (at least if they have any visible partitions on them).
     1 /*
     2  
     3   This Source Code Form is subject to the terms of the Mozilla Public
     4   License, v. 2.0. If a copy of the MPL was not distributed with this
     5   file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6  
     7   Copyright (C) 2009-2013 Michael Möller <mmoeller@openhardwaremonitor.org>
     8 	Copyright (C) 2010 Paul Werelds
     9   Copyright (C) 2011 Roland Reinl <roland-reinl@gmx.de>
    10 	
    11 */
    12 
    13 using System;
    14 using System.Collections.Generic;
    15 using System.Globalization;
    16 using System.IO;
    17 using System.Text;
    18 using OpenHardwareMonitor.Collections;
    19 
    20 namespace OpenHardwareMonitor.Hardware.HDD {
    21   internal abstract class AbstractHarddrive : Hardware {
    22 
    23     private const int UPDATE_DIVIDER = 30; // update only every 30s
    24 
    25     // array of all harddrive types, matching type is searched in this order
    26     private static Type[] hddTypes = {       
    27       typeof(SSDPlextor),
    28       typeof(SSDIntel),
    29       typeof(SSDSandforce),
    30       typeof(SSDIndilinx),
    31       typeof(SSDSamsung),
    32       typeof(SSDMicron),
    33       typeof(GenericHarddisk)
    34     };
    35 
    36     private string firmwareRevision;
    37     private readonly ISmart smart;
    38 
    39     private readonly IntPtr handle;
    40     private readonly int index;
    41     private int count;
    42 
    43     private IList<SmartAttribute> smartAttributes;
    44     private IDictionary<SmartAttribute, Sensor> sensors;
    45 
    46     private DriveInfo[] driveInfos;
    47     private Sensor usageSensor;
    48 
    49     protected AbstractHarddrive(ISmart smart, string name, 
    50       string firmwareRevision, int index, 
    51       IEnumerable<SmartAttribute> smartAttributes, ISettings settings) 
    52       : base(name, new Identifier("hdd",
    53         index.ToString(CultureInfo.InvariantCulture)), settings)
    54     {
    55       this.firmwareRevision = firmwareRevision;
    56       this.smart = smart;
    57       handle = smart.OpenDrive(index);
    58 
    59       if (handle != smart.InvalidHandle)
    60         smart.EnableSmart(handle, index);
    61 
    62       this.index = index;
    63       this.count = 0;
    64 
    65       this.smartAttributes = new List<SmartAttribute>(smartAttributes);
    66 
    67       string[] logicalDrives = smart.GetLogicalDrives(index);
    68       List<DriveInfo> driveInfoList = new List<DriveInfo>(logicalDrives.Length);
    69       foreach (string logicalDrive in logicalDrives) {
    70         try {
    71           DriveInfo di = new DriveInfo(logicalDrive);
    72           if (di.TotalSize > 0) 
    73             driveInfoList.Add(new DriveInfo(logicalDrive));
    74         } catch (ArgumentException) { } catch (IOException) { }
    75       }
    76       driveInfos = driveInfoList.ToArray();
    77 
    78       CreateSensors();
    79     }
    80 
    81     public static AbstractHarddrive CreateInstance(ISmart smart, 
    82       int driveIndex, ISettings settings) 
    83     {
    84       IntPtr deviceHandle = smart.OpenDrive(driveIndex);
    85 
    86       string name = null;
    87       string firmwareRevision = null;
    88       DriveAttributeValue[] values = { };
    89 
    90       if (deviceHandle != smart.InvalidHandle) {
    91         bool nameValid = smart.ReadNameAndFirmwareRevision(deviceHandle,
    92         driveIndex, out name, out firmwareRevision);
    93         bool smartEnabled = smart.EnableSmart(deviceHandle, driveIndex);
    94 
    95         if (smartEnabled)
    96           values = smart.ReadSmartData(deviceHandle, driveIndex);
    97 
    98         smart.CloseHandle(deviceHandle);
    99 
   100         if (!nameValid) {
   101           name = null;
   102           firmwareRevision = null;
   103         }
   104       } else {
   105         string[] logicalDrives = smart.GetLogicalDrives(driveIndex);
   106         if (logicalDrives == null || logicalDrives.Length == 0)
   107           return null;
   108       }
   109 
   110       if (string.IsNullOrEmpty(name))
   111         name = "Generic Hard Disk";
   112 
   113       if (string.IsNullOrEmpty(firmwareRevision))
   114         firmwareRevision = "Unknown";
   115 
   116       foreach (Type type in hddTypes) {
   117         // get the array of name prefixes for the current type
   118         NamePrefixAttribute[] namePrefixes = type.GetCustomAttributes(
   119           typeof(NamePrefixAttribute), true) as NamePrefixAttribute[];
   120 
   121         // get the array of the required SMART attributes for the current type
   122         RequireSmartAttribute[] requiredAttributes = type.GetCustomAttributes(
   123           typeof(RequireSmartAttribute), true) as RequireSmartAttribute[];
   124 
   125         // check if all required attributes are present
   126         bool allRequiredAttributesFound = true;
   127         foreach (var requireAttribute in requiredAttributes) {
   128           bool adttributeFound = false;
   129           foreach (DriveAttributeValue value in values) {
   130             if (value.Identifier == requireAttribute.AttributeId) {
   131               adttributeFound = true;
   132               break;
   133             }
   134           }
   135           if (!adttributeFound) {
   136             allRequiredAttributesFound = false;
   137             break;
   138           }
   139         }
   140 
   141         // if an attribute is missing, then try the next type
   142         if (!allRequiredAttributesFound)
   143           continue;        
   144 
   145         // check if there is a matching name prefix for this type
   146         foreach (NamePrefixAttribute prefix in namePrefixes) {
   147           if (name.StartsWith(prefix.Prefix, StringComparison.InvariantCulture)) 
   148             return Activator.CreateInstance(type, smart, name, firmwareRevision,
   149               driveIndex, settings) as AbstractHarddrive;
   150         }
   151       }
   152 
   153       // no matching type has been found
   154       return null;
   155     }
   156 
   157     private void CreateSensors() {
   158       sensors = new Dictionary<SmartAttribute, Sensor>();
   159 
   160       if (handle != smart.InvalidHandle) {
   161         IList<Pair<SensorType, int>> sensorTypeAndChannels =
   162           new List<Pair<SensorType, int>>();
   163 
   164         DriveAttributeValue[] values = smart.ReadSmartData(handle, index);
   165 
   166         foreach (SmartAttribute attribute in smartAttributes) {
   167           if (!attribute.SensorType.HasValue)
   168             continue;
   169 
   170           bool found = false;
   171           foreach (DriveAttributeValue value in values) {
   172             if (value.Identifier == attribute.Identifier) {
   173               found = true;
   174               break;
   175             }
   176           }
   177           if (!found)
   178             continue;
   179 
   180           Pair<SensorType, int> pair = new Pair<SensorType, int>(
   181             attribute.SensorType.Value, attribute.SensorChannel);
   182 
   183           if (!sensorTypeAndChannels.Contains(pair)) {
   184             Sensor sensor = new Sensor(attribute.Name,
   185               attribute.SensorChannel, attribute.DefaultHiddenSensor,
   186               attribute.SensorType.Value, this, attribute.ParameterDescriptions,
   187               settings);
   188 
   189             sensors.Add(attribute, sensor);
   190             ActivateSensor(sensor);
   191             sensorTypeAndChannels.Add(pair);
   192           }
   193         }
   194       }
   195 
   196       if (driveInfos.Length > 0) {
   197         usageSensor = 
   198           new Sensor("Used Space", 0, SensorType.Load, this, settings);
   199         ActivateSensor(usageSensor);
   200       }
   201     }
   202 
   203     public override HardwareType HardwareType {
   204       get { return HardwareType.HDD; }
   205     }
   206 
   207     public virtual void UpdateAdditionalSensors(DriveAttributeValue[] values) {}
   208 
   209     public override void Update() {
   210       if (count == 0) {
   211         if (handle != smart.InvalidHandle) {
   212           DriveAttributeValue[] values = smart.ReadSmartData(handle, index);
   213 
   214           foreach (KeyValuePair<SmartAttribute, Sensor> keyValuePair in sensors) 
   215           {
   216             SmartAttribute attribute = keyValuePair.Key;
   217             foreach (DriveAttributeValue value in values) {
   218               if (value.Identifier == attribute.Identifier) {
   219                 Sensor sensor = keyValuePair.Value;
   220                 sensor.Value = attribute.ConvertValue(value, sensor.Parameters);
   221               }
   222             }
   223           }
   224 
   225           UpdateAdditionalSensors(values);
   226         }
   227 
   228         if (usageSensor != null) {
   229           long totalSize = 0;
   230           long totalFreeSpace = 0;
   231 
   232           for (int i = 0; i < driveInfos.Length; i++) {
   233             if (!driveInfos[i].IsReady)
   234               continue;
   235             try {
   236               totalSize += driveInfos[i].TotalSize;
   237               totalFreeSpace += driveInfos[i].TotalFreeSpace;
   238             } catch (IOException) { } catch (UnauthorizedAccessException) { }
   239           }
   240           if (totalSize > 0) {
   241             usageSensor.Value = 100.0f - (100.0f * totalFreeSpace) / totalSize;
   242           } else {
   243             usageSensor.Value = null;
   244           }
   245         }
   246       }
   247 
   248       count++; 
   249       count %= UPDATE_DIVIDER; 
   250     }
   251 
   252     public override string GetReport() {
   253       StringBuilder r = new StringBuilder();
   254 
   255       r.AppendLine(this.GetType().Name);
   256       r.AppendLine();
   257       r.AppendLine("Drive name: " + name);
   258       r.AppendLine("Firmware version: " + firmwareRevision);
   259       r.AppendLine();
   260 
   261       if (handle != smart.InvalidHandle) {
   262         DriveAttributeValue[] values = smart.ReadSmartData(handle, index);
   263         DriveThresholdValue[] thresholds =
   264           smart.ReadSmartThresholds(handle, index);
   265 
   266         if (values.Length > 0) {
   267           r.AppendFormat(CultureInfo.InvariantCulture,
   268             " {0}{1}{2}{3}{4}{5}{6}{7}",
   269             ("ID").PadRight(3),
   270             ("Description").PadRight(35),
   271             ("Raw Value").PadRight(13),
   272             ("Worst").PadRight(6),
   273             ("Value").PadRight(6),
   274             ("Thres").PadRight(6),
   275             ("Physical").PadRight(8),
   276             Environment.NewLine);
   277 
   278           foreach (DriveAttributeValue value in values) {
   279             if (value.Identifier == 0x00)
   280               break;
   281 
   282             byte? threshold = null;
   283             foreach (DriveThresholdValue t in thresholds) {
   284               if (t.Identifier == value.Identifier) {
   285                 threshold = t.Threshold;
   286               }
   287             }
   288 
   289             string description = "Unknown";
   290             float? physical = null;
   291             foreach (SmartAttribute a in smartAttributes) {
   292               if (a.Identifier == value.Identifier) {
   293                 description = a.Name;
   294                 if (a.HasRawValueConversion | a.SensorType.HasValue)
   295                   physical = a.ConvertValue(value, null);
   296                 else
   297                   physical = null;
   298               }
   299             }
   300 
   301             string raw = BitConverter.ToString(value.RawValue);
   302             r.AppendFormat(CultureInfo.InvariantCulture,
   303               " {0}{1}{2}{3}{4}{5}{6}{7}",
   304               value.Identifier.ToString("X2").PadRight(3),
   305               description.PadRight(35),
   306               raw.Replace("-", "").PadRight(13),
   307               value.WorstValue.ToString(CultureInfo.InvariantCulture).PadRight(6),
   308               value.AttrValue.ToString(CultureInfo.InvariantCulture).PadRight(6),
   309               (threshold.HasValue ? threshold.Value.ToString(
   310                 CultureInfo.InvariantCulture) : "-").PadRight(6),
   311               (physical.HasValue ? physical.Value.ToString(
   312                 CultureInfo.InvariantCulture) : "-").PadRight(8),
   313               Environment.NewLine);
   314           }
   315           r.AppendLine();
   316         }
   317       }
   318 
   319       foreach (DriveInfo di in driveInfos) {
   320         r.AppendLine("Logical drive name: " + di.Name);
   321         r.AppendLine("Format: " + di.DriveFormat);
   322         r.AppendLine("Total size: " + di.TotalSize);
   323         r.AppendLine("Total free space: " + di.TotalFreeSpace);
   324         r.AppendLine();
   325       }
   326 
   327       return r.ToString();
   328     }
   329 
   330     protected static float RawToInt(byte[] raw, byte value,
   331       IReadOnlyArray<IParameter> parameters) 
   332     {
   333       return (raw[3] << 24) | (raw[2] << 16) | (raw[1] << 8) | raw[0];
   334     }
   335 
   336     public override void Close() {
   337       if (handle != smart.InvalidHandle)
   338         smart.CloseHandle(handle);
   339 
   340       base.Close();
   341     }
   342 
   343     public override void Traverse(IVisitor visitor) {
   344       foreach (ISensor sensor in Sensors)
   345         sensor.Accept(visitor);
   346     }
   347   }
   348 }