Hardware/HDD/AbstractHarddrive.cs
author moel.mich
Sat, 31 Dec 2011 17:31:04 +0000
changeset 324 c6ee430d6995
child 325 4c31341a4800
permissions -rw-r--r--
Modified and extended version of the patch v4 by Roland Reinl (see Issue 256). Main differences to the original patch: DeviceIoControl refactorings removed, SmartAttribute is now descriptive only and does not hold any state, report is written as one 80 columns table, sensors are created only for meaningful values and without duplicates (remaining life, temperatures, host writes and reads). Also the current implementation should really preserve all the functionality of the old system. Additionally there is now a simple SMART devices emulation class (DebugSmart) that can be used in place of WindowsSmart for testing with reported data.
     1 /*
     2   
     3   Version: MPL 1.1/GPL 2.0/LGPL 2.1
     4 
     5   The contents of this file are subject to the Mozilla Public License Version
     6   1.1 (the "License"); you may not use this file except in compliance with
     7   the License. You may obtain a copy of the License at
     8  
     9   http://www.mozilla.org/MPL/
    10 
    11   Software distributed under the License is distributed on an "AS IS" basis,
    12   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
    13   for the specific language governing rights and limitations under the License.
    14 
    15   The Original Code is the Open Hardware Monitor code.
    16 
    17   The Initial Developer of the Original Code is 
    18   Michael Möller <m.moeller@gmx.ch>.
    19   Portions created by the Initial Developer are Copyright (C) 2009-2011
    20   the Initial Developer. All Rights Reserved.
    21 
    22   Contributor(s): 
    23     Paul Werelds
    24     Roland Reinl <roland-reinl@gmx.de>
    25 
    26   Alternatively, the contents of this file may be used under the terms of
    27   either the GNU General Public License Version 2 or later (the "GPL"), or
    28   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
    29   in which case the provisions of the GPL or the LGPL are applicable instead
    30   of those above. If you wish to allow use of your version of this file only
    31   under the terms of either the GPL or the LGPL, and not to allow others to
    32   use your version of this file under the terms of the MPL, indicate your
    33   decision by deleting the provisions above and replace them with the notice
    34   and other provisions required by the GPL or the LGPL. If you do not delete
    35   the provisions above, a recipient may use your version of this file under
    36   the terms of any one of the MPL, the GPL or the LGPL.
    37  
    38 */
    39 
    40 using System;
    41 using System.Collections.Generic;
    42 using System.Globalization;
    43 using System.Text;
    44 using OpenHardwareMonitor.Collections;
    45 
    46 namespace OpenHardwareMonitor.Hardware.HDD {
    47   internal abstract class AbstractHarddrive : Hardware {
    48 
    49     private const int UPDATE_DIVIDER = 30; // update only every 30s
    50 
    51     // array of all harddrive types, matching type is searched in this order
    52     private static Type[] hddTypes = {       
    53       typeof(SSDPlextor),
    54       typeof(SSDIntel),
    55       typeof(SSDSandforce),
    56       typeof(SSDIndilinx),
    57       typeof(GenericHarddisk)
    58     };
    59 
    60     private readonly ISmart smart;
    61 
    62     private readonly IntPtr handle;
    63     private readonly int index;
    64     private int count;
    65 
    66     private IList<SmartAttribute> smartAttributes;
    67     private IDictionary<SmartAttribute, Sensor> sensors;
    68 
    69     protected AbstractHarddrive(ISmart smart, string name, int index, 
    70       IEnumerable<SmartAttribute> smartAttributes, ISettings settings) 
    71       : base(name, new Identifier("hdd",
    72         index.ToString(CultureInfo.InvariantCulture)), settings)
    73     {
    74       this.smart = smart;
    75       handle = smart.OpenDrive(index);
    76 
    77       smart.EnableSmart(handle, index);
    78 
    79       this.index = index;
    80       this.count = 0;
    81 
    82       this.smartAttributes = new List<SmartAttribute>(smartAttributes);
    83 
    84       CreateSensors();
    85     }
    86 
    87     public static AbstractHarddrive CreateInstance(ISmart smart, 
    88       int driveIndex, ISettings settings) 
    89     {
    90       IntPtr deviceHandle = smart.OpenDrive(driveIndex);
    91 
    92       if (deviceHandle == smart.InvalidHandle) 
    93         return null;
    94 
    95       string name = smart.ReadName(deviceHandle, driveIndex);
    96       bool smartEnabled = smart.EnableSmart(deviceHandle, driveIndex);
    97 
    98       DriveAttributeValue[] values = {};
    99       if (smartEnabled)
   100         values = smart.ReadSmartData(deviceHandle, driveIndex);
   101 
   102       smart.CloseHandle(deviceHandle);
   103 
   104       if (string.IsNullOrEmpty(name)) 
   105         return null;
   106 
   107       foreach (Type type in hddTypes) {
   108         // get the array of name prefixes for the current type
   109         NamePrefixAttribute[] namePrefixes = type.GetCustomAttributes(
   110           typeof(NamePrefixAttribute), true) as NamePrefixAttribute[];
   111 
   112         // get the array of the required SMART attributes for the current type
   113         RequireSmartAttribute[] requiredAttributes = type.GetCustomAttributes(
   114           typeof(RequireSmartAttribute), true) as RequireSmartAttribute[];
   115 
   116         // check if all required attributes are present
   117         bool allRequiredAttributesFound = true;
   118         foreach (var requireAttribute in requiredAttributes) {
   119           bool adttributeFound = false;
   120           foreach (DriveAttributeValue value in values) {
   121             if (value.Identifier == requireAttribute.AttributeId) {
   122               adttributeFound = true;
   123               break;
   124             }
   125           }
   126           if (!adttributeFound) {
   127             allRequiredAttributesFound = false;
   128             break;
   129           }
   130         }
   131 
   132         // if an attribute is missing, then try the next type
   133         if (!allRequiredAttributesFound)
   134           continue;        
   135 
   136         // check if there is a matching name prefix for this type
   137         foreach (NamePrefixAttribute prefix in namePrefixes) {
   138           if (name.StartsWith(prefix.Prefix, StringComparison.InvariantCulture)) 
   139             return Activator.CreateInstance(type, smart, name, driveIndex, 
   140               settings) as AbstractHarddrive;
   141         }
   142       }
   143 
   144       // no matching type has been found
   145       return null;
   146     }
   147 
   148     private void CreateSensors() {
   149       sensors = new Dictionary<SmartAttribute, Sensor>();
   150 
   151       IList<Pair<SensorType, int>> sensorTypeAndChannels = 
   152         new List<Pair<SensorType, int>>();
   153 
   154       DriveAttributeValue[] values = smart.ReadSmartData(handle, index);
   155 
   156       foreach (SmartAttribute attribute in smartAttributes) {
   157         if (!attribute.SensorType.HasValue) 
   158           continue;
   159 
   160         bool found = false;
   161         foreach (DriveAttributeValue value in values) {
   162           if (value.Identifier == attribute.Identifier) {
   163             found = true;
   164             break;
   165           }
   166         }
   167         if (!found)
   168           continue;
   169 
   170         Pair<SensorType, int> pair = new Pair<SensorType, int>(
   171           attribute.SensorType.Value, attribute.SensorChannel);
   172 
   173         if (!sensorTypeAndChannels.Contains(pair)) {
   174           Sensor sensor = new Sensor(attribute.Name, 
   175             attribute.SensorChannel, attribute.SensorType.Value, this, 
   176             settings);
   177 
   178           sensors.Add(attribute, sensor);
   179           sensorTypeAndChannels.Add(pair);
   180         }     
   181       }
   182     }
   183 
   184     public override HardwareType HardwareType {
   185       get { return HardwareType.HDD; }
   186     }
   187 
   188     public override ISensor[] Sensors {
   189       get {
   190         Sensor[] array = new Sensor[sensors.Count];
   191         sensors.Values.CopyTo(array, 0);
   192         return array;
   193       }
   194     }
   195 
   196     public override void Update() {
   197       if (count == 0) {
   198         DriveAttributeValue[] values = smart.ReadSmartData(handle, index);
   199 
   200         foreach (KeyValuePair<SmartAttribute, Sensor> keyValuePair in sensors) {
   201           SmartAttribute attribute = keyValuePair.Key;          
   202           foreach (DriveAttributeValue value in values) {
   203             if (value.Identifier == attribute.Identifier) {
   204               Sensor sensor = keyValuePair.Value;
   205               sensor.Value = attribute.ConvertValue(value);
   206             }
   207           }
   208         }        
   209       }
   210 
   211       count++; 
   212       count %= UPDATE_DIVIDER; 
   213     }
   214 
   215     public override string GetReport() {
   216       StringBuilder r = new StringBuilder();
   217       DriveAttributeValue[] values = smart.ReadSmartData(handle, index);
   218       DriveThresholdValue[] thresholds = 
   219         smart.ReadSmartThresholds(handle, index);
   220 
   221       if (values.Length > 0) {
   222         r.AppendLine(this.GetType().Name);
   223         r.AppendLine();
   224         r.AppendLine("Drive name: " + name);
   225         r.AppendLine();
   226         r.AppendFormat(CultureInfo.InvariantCulture, 
   227           " {0}{1}{2}{3}{4}{5}{6}{7}",
   228           ("ID").PadRight(3),
   229           ("Description").PadRight(32),
   230           ("Raw Value").PadRight(13),
   231           ("Worst").PadRight(6),
   232           ("Value").PadRight(6),
   233           ("Thres").PadRight(6),
   234           ("Physical").PadRight(8),
   235           Environment.NewLine);
   236 
   237         foreach (DriveAttributeValue value in values) {
   238           if (value.Identifier == 0x00) 
   239             break;
   240 
   241           byte? threshold = null;
   242           foreach (DriveThresholdValue t in thresholds) {
   243             if (t.Identifier == value.Identifier) {
   244               threshold = t.Threshold;
   245             }
   246           }
   247 
   248           string description = "Unknown";
   249           float? physical = null;
   250           foreach (SmartAttribute a in smartAttributes) {
   251             if (a.Identifier == value.Identifier) {
   252               description = a.Name;
   253               if (a.HasRawValueConversion | a.SensorType.HasValue)
   254                 physical = a.ConvertValue(value);
   255               else
   256                 physical = null;
   257             }
   258           }
   259 
   260           string raw = BitConverter.ToString(value.RawValue);
   261           r.AppendFormat(CultureInfo.InvariantCulture, 
   262             " {0}{1}{2}{3}{4}{5}{6}{7}",
   263             value.Identifier.ToString("X2").PadRight(3),
   264             description.PadRight(32),
   265             raw.Replace("-", "").PadRight(13),
   266             value.WorstValue.ToString(CultureInfo.InvariantCulture).PadRight(6),
   267             value.AttrValue.ToString(CultureInfo.InvariantCulture).PadRight(6),
   268             (threshold.HasValue ? threshold.Value.ToString(
   269               CultureInfo.InvariantCulture) : "-").PadRight(6),
   270             (physical.HasValue ? physical.Value.ToString(
   271               CultureInfo.InvariantCulture) : "-").PadRight(8),
   272             Environment.NewLine);
   273         }
   274         r.AppendLine();
   275       }
   276 
   277       return r.ToString();
   278     }
   279 
   280     protected static float RawToInt(byte[] raw, byte value) {
   281       return (raw[3] << 24) | (raw[2] << 16) | (raw[1] << 8) | raw[0];
   282     }
   283 
   284     public override void Close() {
   285       smart.CloseHandle(handle);
   286       base.Close();
   287     }
   288 
   289     public override void Traverse(IVisitor visitor) {
   290       foreach (ISensor sensor in Sensors)
   291         sensor.Accept(visitor);
   292     }
   293   }
   294 }