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