Hardware/HDD/AbstractHarddrive.cs
author moel.mich
Wed, 25 Jul 2012 16:03:36 +0000
changeset 374 ea86cea126bc
parent 369 5077ed7ddca8
child 385 8f16f03797f5
permissions -rw-r--r--
Added temperature offset parameters to all HDD temperature sensors. Fixed issue 271.
     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           for (int i = 0; i < driveInfos.Length; i++) {
   214             totalSize += driveInfos[i].TotalSize;
   215             totalFreeSpace += driveInfos[i].TotalFreeSpace;
   216           }
   217           usageSensor.Value = 100.0f - (100.0f * totalFreeSpace) / totalSize;
   218         }
   219       }
   220 
   221       count++; 
   222       count %= UPDATE_DIVIDER; 
   223     }
   224 
   225     public override string GetReport() {
   226       StringBuilder r = new StringBuilder();
   227       DriveAttributeValue[] values = smart.ReadSmartData(handle, index);
   228       DriveThresholdValue[] thresholds = 
   229         smart.ReadSmartThresholds(handle, index);
   230 
   231       if (values.Length > 0) {
   232         r.AppendLine(this.GetType().Name);
   233         r.AppendLine();
   234         r.AppendLine("Drive name: " + name);
   235         r.AppendLine("Firmware version: " + firmwareRevision);
   236         r.AppendLine();    
   237         r.AppendFormat(CultureInfo.InvariantCulture, 
   238           " {0}{1}{2}{3}{4}{5}{6}{7}",
   239           ("ID").PadRight(3),
   240           ("Description").PadRight(35),
   241           ("Raw Value").PadRight(13),
   242           ("Worst").PadRight(6),
   243           ("Value").PadRight(6),
   244           ("Thres").PadRight(6),
   245           ("Physical").PadRight(8),
   246           Environment.NewLine);
   247 
   248         foreach (DriveAttributeValue value in values) {
   249           if (value.Identifier == 0x00) 
   250             break;
   251 
   252           byte? threshold = null;
   253           foreach (DriveThresholdValue t in thresholds) {
   254             if (t.Identifier == value.Identifier) {
   255               threshold = t.Threshold;
   256             }
   257           }
   258 
   259           string description = "Unknown";
   260           float? physical = null;
   261           foreach (SmartAttribute a in smartAttributes) {
   262             if (a.Identifier == value.Identifier) {
   263               description = a.Name;
   264               if (a.HasRawValueConversion | a.SensorType.HasValue)
   265                 physical = a.ConvertValue(value, null);
   266               else
   267                 physical = null;
   268             }
   269           }
   270 
   271           string raw = BitConverter.ToString(value.RawValue);
   272           r.AppendFormat(CultureInfo.InvariantCulture, 
   273             " {0}{1}{2}{3}{4}{5}{6}{7}",
   274             value.Identifier.ToString("X2").PadRight(3),
   275             description.PadRight(35),
   276             raw.Replace("-", "").PadRight(13),
   277             value.WorstValue.ToString(CultureInfo.InvariantCulture).PadRight(6),
   278             value.AttrValue.ToString(CultureInfo.InvariantCulture).PadRight(6),
   279             (threshold.HasValue ? threshold.Value.ToString(
   280               CultureInfo.InvariantCulture) : "-").PadRight(6),
   281             (physical.HasValue ? physical.Value.ToString(
   282               CultureInfo.InvariantCulture) : "-").PadRight(8),
   283             Environment.NewLine);
   284         }
   285         r.AppendLine();
   286       }
   287 
   288       foreach (DriveInfo di in driveInfos) {
   289         r.AppendLine("Logical drive name: " + di.Name);
   290         r.AppendLine("Format: " + di.DriveFormat);
   291         r.AppendLine("Total size: " + di.TotalSize);
   292         r.AppendLine("Total free space: " + di.TotalFreeSpace);
   293         r.AppendLine();
   294       }
   295 
   296       return r.ToString();
   297     }
   298 
   299     protected static float RawToInt(byte[] raw, byte value,
   300       IReadOnlyArray<IParameter> parameters) 
   301     {
   302       return (raw[3] << 24) | (raw[2] << 16) | (raw[1] << 8) | raw[0];
   303     }
   304 
   305     public override void Close() {
   306       smart.CloseHandle(handle);
   307       base.Close();
   308     }
   309 
   310     public override void Traverse(IVisitor visitor) {
   311       foreach (ISensor sensor in Sensors)
   312         sensor.Accept(visitor);
   313     }
   314   }
   315 }