Hardware/HDD/AbstractHarddrive.cs
author moel.mich
Tue, 30 Dec 2014 21:04:54 +0000
changeset 430 6b24e39f1b84
parent 404 1201889d413a
permissions -rw-r--r--
Fixed Issue 651.
     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) {
    75         } catch (IOException) {
    76         } catch (UnauthorizedAccessException) {
    77         }
    78       }
    79       driveInfos = driveInfoList.ToArray();
    80 
    81       CreateSensors();
    82     }
    83 
    84     public static AbstractHarddrive CreateInstance(ISmart smart, 
    85       int driveIndex, ISettings settings) 
    86     {
    87       IntPtr deviceHandle = smart.OpenDrive(driveIndex);
    88 
    89       string name = null;
    90       string firmwareRevision = null;
    91       DriveAttributeValue[] values = { };
    92 
    93       if (deviceHandle != smart.InvalidHandle) {
    94         bool nameValid = smart.ReadNameAndFirmwareRevision(deviceHandle,
    95         driveIndex, out name, out firmwareRevision);
    96         bool smartEnabled = smart.EnableSmart(deviceHandle, driveIndex);
    97 
    98         if (smartEnabled)
    99           values = smart.ReadSmartData(deviceHandle, driveIndex);
   100 
   101         smart.CloseHandle(deviceHandle);
   102 
   103         if (!nameValid) {
   104           name = null;
   105           firmwareRevision = null;
   106         }
   107       } else {
   108         string[] logicalDrives = smart.GetLogicalDrives(driveIndex);
   109         if (logicalDrives == null || logicalDrives.Length == 0)
   110           return null;
   111 
   112         bool hasNonZeroSizeDrive = false;
   113         foreach (string logicalDrive in logicalDrives) {
   114           try {
   115             DriveInfo di = new DriveInfo(logicalDrive);
   116             if (di.TotalSize > 0) {
   117               hasNonZeroSizeDrive = true;
   118               break;
   119             }
   120           } catch (ArgumentException) { 
   121           } catch (IOException) { 
   122           } catch (UnauthorizedAccessException) {
   123           }
   124         }
   125 
   126         if (!hasNonZeroSizeDrive)
   127           return null;
   128       }
   129 
   130       if (string.IsNullOrEmpty(name))
   131         name = "Generic Hard Disk";
   132 
   133       if (string.IsNullOrEmpty(firmwareRevision))
   134         firmwareRevision = "Unknown";
   135 
   136       foreach (Type type in hddTypes) {
   137         // get the array of name prefixes for the current type
   138         NamePrefixAttribute[] namePrefixes = type.GetCustomAttributes(
   139           typeof(NamePrefixAttribute), true) as NamePrefixAttribute[];
   140 
   141         // get the array of the required SMART attributes for the current type
   142         RequireSmartAttribute[] requiredAttributes = type.GetCustomAttributes(
   143           typeof(RequireSmartAttribute), true) as RequireSmartAttribute[];
   144 
   145         // check if all required attributes are present
   146         bool allRequiredAttributesFound = true;
   147         foreach (var requireAttribute in requiredAttributes) {
   148           bool adttributeFound = false;
   149           foreach (DriveAttributeValue value in values) {
   150             if (value.Identifier == requireAttribute.AttributeId) {
   151               adttributeFound = true;
   152               break;
   153             }
   154           }
   155           if (!adttributeFound) {
   156             allRequiredAttributesFound = false;
   157             break;
   158           }
   159         }
   160 
   161         // if an attribute is missing, then try the next type
   162         if (!allRequiredAttributesFound)
   163           continue;        
   164 
   165         // check if there is a matching name prefix for this type
   166         foreach (NamePrefixAttribute prefix in namePrefixes) {
   167           if (name.StartsWith(prefix.Prefix, StringComparison.InvariantCulture)) 
   168             return Activator.CreateInstance(type, smart, name, firmwareRevision,
   169               driveIndex, settings) as AbstractHarddrive;
   170         }
   171       }
   172 
   173       // no matching type has been found
   174       return null;
   175     }
   176 
   177     private void CreateSensors() {
   178       sensors = new Dictionary<SmartAttribute, Sensor>();
   179 
   180       if (handle != smart.InvalidHandle) {
   181         IList<Pair<SensorType, int>> sensorTypeAndChannels =
   182           new List<Pair<SensorType, int>>();
   183 
   184         DriveAttributeValue[] values = smart.ReadSmartData(handle, index);
   185 
   186         foreach (SmartAttribute attribute in smartAttributes) {
   187           if (!attribute.SensorType.HasValue)
   188             continue;
   189 
   190           bool found = false;
   191           foreach (DriveAttributeValue value in values) {
   192             if (value.Identifier == attribute.Identifier) {
   193               found = true;
   194               break;
   195             }
   196           }
   197           if (!found)
   198             continue;
   199 
   200           Pair<SensorType, int> pair = new Pair<SensorType, int>(
   201             attribute.SensorType.Value, attribute.SensorChannel);
   202 
   203           if (!sensorTypeAndChannels.Contains(pair)) {
   204             Sensor sensor = new Sensor(attribute.Name,
   205               attribute.SensorChannel, attribute.DefaultHiddenSensor,
   206               attribute.SensorType.Value, this, attribute.ParameterDescriptions,
   207               settings);
   208 
   209             sensors.Add(attribute, sensor);
   210             ActivateSensor(sensor);
   211             sensorTypeAndChannels.Add(pair);
   212           }
   213         }
   214       }
   215 
   216       if (driveInfos.Length > 0) {
   217         usageSensor = 
   218           new Sensor("Used Space", 0, SensorType.Load, this, settings);
   219         ActivateSensor(usageSensor);
   220       }
   221     }
   222 
   223     public override HardwareType HardwareType {
   224       get { return HardwareType.HDD; }
   225     }
   226 
   227     public virtual void UpdateAdditionalSensors(DriveAttributeValue[] values) {}
   228 
   229     public override void Update() {
   230       if (count == 0) {
   231         if (handle != smart.InvalidHandle) {
   232           DriveAttributeValue[] values = smart.ReadSmartData(handle, index);
   233 
   234           foreach (KeyValuePair<SmartAttribute, Sensor> keyValuePair in sensors) 
   235           {
   236             SmartAttribute attribute = keyValuePair.Key;
   237             foreach (DriveAttributeValue value in values) {
   238               if (value.Identifier == attribute.Identifier) {
   239                 Sensor sensor = keyValuePair.Value;
   240                 sensor.Value = attribute.ConvertValue(value, sensor.Parameters);
   241               }
   242             }
   243           }
   244 
   245           UpdateAdditionalSensors(values);
   246         }
   247 
   248         if (usageSensor != null) {
   249           long totalSize = 0;
   250           long totalFreeSpace = 0;
   251 
   252           for (int i = 0; i < driveInfos.Length; i++) {
   253             if (!driveInfos[i].IsReady)
   254               continue;
   255             try {
   256               totalSize += driveInfos[i].TotalSize;
   257               totalFreeSpace += driveInfos[i].TotalFreeSpace;
   258             } catch (IOException) { } catch (UnauthorizedAccessException) { }
   259           }
   260           if (totalSize > 0) {
   261             usageSensor.Value = 100.0f - (100.0f * totalFreeSpace) / totalSize;
   262           } else {
   263             usageSensor.Value = null;
   264           }
   265         }
   266       }
   267 
   268       count++; 
   269       count %= UPDATE_DIVIDER; 
   270     }
   271 
   272     public override string GetReport() {
   273       StringBuilder r = new StringBuilder();
   274 
   275       r.AppendLine(this.GetType().Name);
   276       r.AppendLine();
   277       r.AppendLine("Drive name: " + name);
   278       r.AppendLine("Firmware version: " + firmwareRevision);
   279       r.AppendLine();
   280 
   281       if (handle != smart.InvalidHandle) {
   282         DriveAttributeValue[] values = smart.ReadSmartData(handle, index);
   283         DriveThresholdValue[] thresholds =
   284           smart.ReadSmartThresholds(handle, index);
   285 
   286         if (values.Length > 0) {
   287           r.AppendFormat(CultureInfo.InvariantCulture,
   288             " {0}{1}{2}{3}{4}{5}{6}{7}",
   289             ("ID").PadRight(3),
   290             ("Description").PadRight(35),
   291             ("Raw Value").PadRight(13),
   292             ("Worst").PadRight(6),
   293             ("Value").PadRight(6),
   294             ("Thres").PadRight(6),
   295             ("Physical").PadRight(8),
   296             Environment.NewLine);
   297 
   298           foreach (DriveAttributeValue value in values) {
   299             if (value.Identifier == 0x00)
   300               break;
   301 
   302             byte? threshold = null;
   303             foreach (DriveThresholdValue t in thresholds) {
   304               if (t.Identifier == value.Identifier) {
   305                 threshold = t.Threshold;
   306               }
   307             }
   308 
   309             string description = "Unknown";
   310             float? physical = null;
   311             foreach (SmartAttribute a in smartAttributes) {
   312               if (a.Identifier == value.Identifier) {
   313                 description = a.Name;
   314                 if (a.HasRawValueConversion | a.SensorType.HasValue)
   315                   physical = a.ConvertValue(value, null);
   316                 else
   317                   physical = null;
   318               }
   319             }
   320 
   321             string raw = BitConverter.ToString(value.RawValue);
   322             r.AppendFormat(CultureInfo.InvariantCulture,
   323               " {0}{1}{2}{3}{4}{5}{6}{7}",
   324               value.Identifier.ToString("X2").PadRight(3),
   325               description.PadRight(35),
   326               raw.Replace("-", "").PadRight(13),
   327               value.WorstValue.ToString(CultureInfo.InvariantCulture).PadRight(6),
   328               value.AttrValue.ToString(CultureInfo.InvariantCulture).PadRight(6),
   329               (threshold.HasValue ? threshold.Value.ToString(
   330                 CultureInfo.InvariantCulture) : "-").PadRight(6),
   331               (physical.HasValue ? physical.Value.ToString(
   332                 CultureInfo.InvariantCulture) : "-").PadRight(8),
   333               Environment.NewLine);
   334           }
   335           r.AppendLine();
   336         }
   337       }
   338 
   339       foreach (DriveInfo di in driveInfos) {
   340         if (!di.IsReady)
   341           continue;
   342         try {
   343           r.AppendLine("Logical drive name: " + di.Name);
   344           r.AppendLine("Format: " + di.DriveFormat);
   345           r.AppendLine("Total size: " + di.TotalSize);
   346           r.AppendLine("Total free space: " + di.TotalFreeSpace);
   347           r.AppendLine();
   348         } catch (IOException) { } catch (UnauthorizedAccessException) { }
   349       }
   350 
   351       return r.ToString();
   352     }
   353 
   354     protected static float RawToInt(byte[] raw, byte value,
   355       IReadOnlyArray<IParameter> parameters) 
   356     {
   357       return (raw[3] << 24) | (raw[2] << 16) | (raw[1] << 8) | raw[0];
   358     }
   359 
   360     public override void Close() {
   361       if (handle != smart.InvalidHandle)
   362         smart.CloseHandle(handle);
   363 
   364       base.Close();
   365     }
   366 
   367     public override void Traverse(IVisitor visitor) {
   368       foreach (ISensor sensor in Sensors)
   369         sensor.Accept(visitor);
   370     }
   371   }
   372 }