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