Hardware/HDD/AbstractHarddrive.cs
author moel.mich
Sun, 30 Jun 2013 16:24:52 +0000
changeset 409 2ce8da367490
parent 404 1201889d413a
permissions -rw-r--r--
Changed the HDD enumeration code to drop drives without smart support and without any non-zero-sized logical drives.
moel@324
     1
/*
moel@324
     2
 
moel@344
     3
  This Source Code Form is subject to the terms of the Mozilla Public
moel@344
     4
  License, v. 2.0. If a copy of the MPL was not distributed with this
moel@344
     5
  file, You can obtain one at http://mozilla.org/MPL/2.0/.
moel@324
     6
 
moel@403
     7
  Copyright (C) 2009-2013 Michael Möller <mmoeller@openhardwaremonitor.org>
moel@344
     8
	Copyright (C) 2010 Paul Werelds
moel@344
     9
  Copyright (C) 2011 Roland Reinl <roland-reinl@gmx.de>
moel@344
    10
	
moel@324
    11
*/
moel@324
    12
moel@324
    13
using System;
moel@324
    14
using System.Collections.Generic;
moel@324
    15
using System.Globalization;
moel@369
    16
using System.IO;
moel@324
    17
using System.Text;
moel@324
    18
using OpenHardwareMonitor.Collections;
moel@324
    19
moel@324
    20
namespace OpenHardwareMonitor.Hardware.HDD {
moel@324
    21
  internal abstract class AbstractHarddrive : Hardware {
moel@324
    22
moel@324
    23
    private const int UPDATE_DIVIDER = 30; // update only every 30s
moel@324
    24
moel@324
    25
    // array of all harddrive types, matching type is searched in this order
moel@324
    26
    private static Type[] hddTypes = {       
moel@324
    27
      typeof(SSDPlextor),
moel@324
    28
      typeof(SSDIntel),
moel@324
    29
      typeof(SSDSandforce),
moel@324
    30
      typeof(SSDIndilinx),
moel@328
    31
      typeof(SSDSamsung),
moel@358
    32
      typeof(SSDMicron),
moel@324
    33
      typeof(GenericHarddisk)
moel@324
    34
    };
moel@324
    35
moel@325
    36
    private string firmwareRevision;
moel@324
    37
    private readonly ISmart smart;
moel@324
    38
moel@324
    39
    private readonly IntPtr handle;
moel@324
    40
    private readonly int index;
moel@324
    41
    private int count;
moel@324
    42
moel@324
    43
    private IList<SmartAttribute> smartAttributes;
moel@324
    44
    private IDictionary<SmartAttribute, Sensor> sensors;
moel@324
    45
moel@369
    46
    private DriveInfo[] driveInfos;
moel@369
    47
    private Sensor usageSensor;
moel@369
    48
moel@325
    49
    protected AbstractHarddrive(ISmart smart, string name, 
moel@325
    50
      string firmwareRevision, int index, 
moel@324
    51
      IEnumerable<SmartAttribute> smartAttributes, ISettings settings) 
moel@324
    52
      : base(name, new Identifier("hdd",
moel@324
    53
        index.ToString(CultureInfo.InvariantCulture)), settings)
moel@324
    54
    {
moel@325
    55
      this.firmwareRevision = firmwareRevision;
moel@324
    56
      this.smart = smart;
moel@324
    57
      handle = smart.OpenDrive(index);
moel@324
    58
moel@403
    59
      if (handle != smart.InvalidHandle)
moel@403
    60
        smart.EnableSmart(handle, index);
moel@324
    61
moel@324
    62
      this.index = index;
moel@324
    63
      this.count = 0;
moel@324
    64
moel@324
    65
      this.smartAttributes = new List<SmartAttribute>(smartAttributes);
moel@324
    66
moel@369
    67
      string[] logicalDrives = smart.GetLogicalDrives(index);
moel@369
    68
      List<DriveInfo> driveInfoList = new List<DriveInfo>(logicalDrives.Length);
moel@369
    69
      foreach (string logicalDrive in logicalDrives) {
moel@369
    70
        try {
moel@369
    71
          DriveInfo di = new DriveInfo(logicalDrive);
moel@409
    72
          if (di.TotalSize > 0)
moel@369
    73
            driveInfoList.Add(new DriveInfo(logicalDrive));
moel@409
    74
        } catch (ArgumentException) {
moel@409
    75
        } catch (IOException) {
moel@409
    76
        } catch (UnauthorizedAccessException) {
moel@409
    77
        }
moel@369
    78
      }
moel@369
    79
      driveInfos = driveInfoList.ToArray();
moel@369
    80
moel@324
    81
      CreateSensors();
moel@324
    82
    }
moel@324
    83
moel@324
    84
    public static AbstractHarddrive CreateInstance(ISmart smart, 
moel@324
    85
      int driveIndex, ISettings settings) 
moel@324
    86
    {
moel@324
    87
      IntPtr deviceHandle = smart.OpenDrive(driveIndex);
moel@324
    88
moel@403
    89
      string name = null;
moel@403
    90
      string firmwareRevision = null;
moel@403
    91
      DriveAttributeValue[] values = { };
moel@324
    92
moel@403
    93
      if (deviceHandle != smart.InvalidHandle) {
moel@403
    94
        bool nameValid = smart.ReadNameAndFirmwareRevision(deviceHandle,
moel@325
    95
        driveIndex, out name, out firmwareRevision);
moel@403
    96
        bool smartEnabled = smart.EnableSmart(deviceHandle, driveIndex);
moel@324
    97
moel@403
    98
        if (smartEnabled)
moel@403
    99
          values = smart.ReadSmartData(deviceHandle, driveIndex);
moel@324
   100
moel@403
   101
        smart.CloseHandle(deviceHandle);
moel@324
   102
moel@403
   103
        if (!nameValid) {
moel@403
   104
          name = null;
moel@403
   105
          firmwareRevision = null;
moel@403
   106
        }
moel@403
   107
      } else {
moel@403
   108
        string[] logicalDrives = smart.GetLogicalDrives(driveIndex);
moel@403
   109
        if (logicalDrives == null || logicalDrives.Length == 0)
moel@403
   110
          return null;
moel@409
   111
moel@409
   112
        bool hasNonZeroSizeDrive = false;
moel@409
   113
        foreach (string logicalDrive in logicalDrives) {
moel@409
   114
          try {
moel@409
   115
            DriveInfo di = new DriveInfo(logicalDrive);
moel@409
   116
            if (di.TotalSize > 0) {
moel@409
   117
              hasNonZeroSizeDrive = true;
moel@409
   118
              break;
moel@409
   119
            }
moel@409
   120
          } catch (ArgumentException) { 
moel@409
   121
          } catch (IOException) { 
moel@409
   122
          } catch (UnauthorizedAccessException) {
moel@409
   123
          }
moel@409
   124
        }
moel@409
   125
moel@409
   126
        if (!hasNonZeroSizeDrive)
moel@409
   127
          return null;
moel@403
   128
      }
moel@403
   129
moel@403
   130
      if (string.IsNullOrEmpty(name))
moel@403
   131
        name = "Generic Hard Disk";
moel@403
   132
moel@403
   133
      if (string.IsNullOrEmpty(firmwareRevision))
moel@403
   134
        firmwareRevision = "Unknown";
moel@324
   135
moel@324
   136
      foreach (Type type in hddTypes) {
moel@324
   137
        // get the array of name prefixes for the current type
moel@324
   138
        NamePrefixAttribute[] namePrefixes = type.GetCustomAttributes(
moel@324
   139
          typeof(NamePrefixAttribute), true) as NamePrefixAttribute[];
moel@324
   140
moel@324
   141
        // get the array of the required SMART attributes for the current type
moel@324
   142
        RequireSmartAttribute[] requiredAttributes = type.GetCustomAttributes(
moel@324
   143
          typeof(RequireSmartAttribute), true) as RequireSmartAttribute[];
moel@324
   144
moel@324
   145
        // check if all required attributes are present
moel@324
   146
        bool allRequiredAttributesFound = true;
moel@324
   147
        foreach (var requireAttribute in requiredAttributes) {
moel@324
   148
          bool adttributeFound = false;
moel@324
   149
          foreach (DriveAttributeValue value in values) {
moel@324
   150
            if (value.Identifier == requireAttribute.AttributeId) {
moel@324
   151
              adttributeFound = true;
moel@324
   152
              break;
moel@324
   153
            }
moel@324
   154
          }
moel@324
   155
          if (!adttributeFound) {
moel@324
   156
            allRequiredAttributesFound = false;
moel@324
   157
            break;
moel@324
   158
          }
moel@324
   159
        }
moel@324
   160
moel@324
   161
        // if an attribute is missing, then try the next type
moel@324
   162
        if (!allRequiredAttributesFound)
moel@324
   163
          continue;        
moel@324
   164
moel@324
   165
        // check if there is a matching name prefix for this type
moel@324
   166
        foreach (NamePrefixAttribute prefix in namePrefixes) {
moel@324
   167
          if (name.StartsWith(prefix.Prefix, StringComparison.InvariantCulture)) 
moel@325
   168
            return Activator.CreateInstance(type, smart, name, firmwareRevision,
moel@325
   169
              driveIndex, settings) as AbstractHarddrive;
moel@324
   170
        }
moel@324
   171
      }
moel@324
   172
moel@324
   173
      // no matching type has been found
moel@324
   174
      return null;
moel@324
   175
    }
moel@324
   176
moel@324
   177
    private void CreateSensors() {
moel@324
   178
      sensors = new Dictionary<SmartAttribute, Sensor>();
moel@324
   179
moel@403
   180
      if (handle != smart.InvalidHandle) {
moel@403
   181
        IList<Pair<SensorType, int>> sensorTypeAndChannels =
moel@403
   182
          new List<Pair<SensorType, int>>();
moel@324
   183
moel@403
   184
        DriveAttributeValue[] values = smart.ReadSmartData(handle, index);
moel@324
   185
moel@403
   186
        foreach (SmartAttribute attribute in smartAttributes) {
moel@403
   187
          if (!attribute.SensorType.HasValue)
moel@403
   188
            continue;
moel@324
   189
moel@403
   190
          bool found = false;
moel@403
   191
          foreach (DriveAttributeValue value in values) {
moel@403
   192
            if (value.Identifier == attribute.Identifier) {
moel@403
   193
              found = true;
moel@403
   194
              break;
moel@403
   195
            }
moel@403
   196
          }
moel@403
   197
          if (!found)
moel@403
   198
            continue;
moel@403
   199
moel@403
   200
          Pair<SensorType, int> pair = new Pair<SensorType, int>(
moel@403
   201
            attribute.SensorType.Value, attribute.SensorChannel);
moel@403
   202
moel@403
   203
          if (!sensorTypeAndChannels.Contains(pair)) {
moel@403
   204
            Sensor sensor = new Sensor(attribute.Name,
moel@403
   205
              attribute.SensorChannel, attribute.DefaultHiddenSensor,
moel@403
   206
              attribute.SensorType.Value, this, attribute.ParameterDescriptions,
moel@403
   207
              settings);
moel@403
   208
moel@403
   209
            sensors.Add(attribute, sensor);
moel@403
   210
            ActivateSensor(sensor);
moel@403
   211
            sensorTypeAndChannels.Add(pair);
moel@324
   212
          }
moel@324
   213
        }
moel@324
   214
      }
moel@369
   215
moel@369
   216
      if (driveInfos.Length > 0) {
moel@369
   217
        usageSensor = 
moel@369
   218
          new Sensor("Used Space", 0, SensorType.Load, this, settings);
moel@369
   219
        ActivateSensor(usageSensor);
moel@369
   220
      }
moel@324
   221
    }
moel@324
   222
moel@324
   223
    public override HardwareType HardwareType {
moel@324
   224
      get { return HardwareType.HDD; }
moel@324
   225
    }
moel@324
   226
moel@339
   227
    public virtual void UpdateAdditionalSensors(DriveAttributeValue[] values) {}
moel@324
   228
moel@324
   229
    public override void Update() {
moel@324
   230
      if (count == 0) {
moel@403
   231
        if (handle != smart.InvalidHandle) {
moel@403
   232
          DriveAttributeValue[] values = smart.ReadSmartData(handle, index);
moel@324
   233
moel@403
   234
          foreach (KeyValuePair<SmartAttribute, Sensor> keyValuePair in sensors) 
moel@403
   235
          {
moel@403
   236
            SmartAttribute attribute = keyValuePair.Key;
moel@403
   237
            foreach (DriveAttributeValue value in values) {
moel@403
   238
              if (value.Identifier == attribute.Identifier) {
moel@403
   239
                Sensor sensor = keyValuePair.Value;
moel@403
   240
                sensor.Value = attribute.ConvertValue(value, sensor.Parameters);
moel@403
   241
              }
moel@324
   242
            }
moel@324
   243
          }
moel@403
   244
moel@403
   245
          UpdateAdditionalSensors(values);
moel@339
   246
        }
moel@339
   247
moel@369
   248
        if (usageSensor != null) {
moel@369
   249
          long totalSize = 0;
moel@369
   250
          long totalFreeSpace = 0;
moel@385
   251
moel@369
   252
          for (int i = 0; i < driveInfos.Length; i++) {
moel@385
   253
            if (!driveInfos[i].IsReady)
moel@385
   254
              continue;
moel@385
   255
            try {
moel@385
   256
              totalSize += driveInfos[i].TotalSize;
moel@385
   257
              totalFreeSpace += driveInfos[i].TotalFreeSpace;
moel@385
   258
            } catch (IOException) { } catch (UnauthorizedAccessException) { }
moel@369
   259
          }
moel@385
   260
          if (totalSize > 0) {
moel@385
   261
            usageSensor.Value = 100.0f - (100.0f * totalFreeSpace) / totalSize;
moel@385
   262
          } else {
moel@385
   263
            usageSensor.Value = null;
moel@385
   264
          }
moel@369
   265
        }
moel@324
   266
      }
moel@324
   267
moel@324
   268
      count++; 
moel@324
   269
      count %= UPDATE_DIVIDER; 
moel@324
   270
    }
moel@324
   271
moel@324
   272
    public override string GetReport() {
moel@324
   273
      StringBuilder r = new StringBuilder();
moel@324
   274
moel@403
   275
      r.AppendLine(this.GetType().Name);
moel@403
   276
      r.AppendLine();
moel@403
   277
      r.AppendLine("Drive name: " + name);
moel@403
   278
      r.AppendLine("Firmware version: " + firmwareRevision);
moel@403
   279
      r.AppendLine();
moel@324
   280
moel@403
   281
      if (handle != smart.InvalidHandle) {
moel@403
   282
        DriveAttributeValue[] values = smart.ReadSmartData(handle, index);
moel@403
   283
        DriveThresholdValue[] thresholds =
moel@403
   284
          smart.ReadSmartThresholds(handle, index);
moel@324
   285
moel@403
   286
        if (values.Length > 0) {
moel@403
   287
          r.AppendFormat(CultureInfo.InvariantCulture,
moel@403
   288
            " {0}{1}{2}{3}{4}{5}{6}{7}",
moel@403
   289
            ("ID").PadRight(3),
moel@403
   290
            ("Description").PadRight(35),
moel@403
   291
            ("Raw Value").PadRight(13),
moel@403
   292
            ("Worst").PadRight(6),
moel@403
   293
            ("Value").PadRight(6),
moel@403
   294
            ("Thres").PadRight(6),
moel@403
   295
            ("Physical").PadRight(8),
moel@403
   296
            Environment.NewLine);
moel@403
   297
moel@403
   298
          foreach (DriveAttributeValue value in values) {
moel@403
   299
            if (value.Identifier == 0x00)
moel@403
   300
              break;
moel@403
   301
moel@403
   302
            byte? threshold = null;
moel@403
   303
            foreach (DriveThresholdValue t in thresholds) {
moel@403
   304
              if (t.Identifier == value.Identifier) {
moel@403
   305
                threshold = t.Threshold;
moel@403
   306
              }
moel@324
   307
            }
moel@403
   308
moel@403
   309
            string description = "Unknown";
moel@403
   310
            float? physical = null;
moel@403
   311
            foreach (SmartAttribute a in smartAttributes) {
moel@403
   312
              if (a.Identifier == value.Identifier) {
moel@403
   313
                description = a.Name;
moel@403
   314
                if (a.HasRawValueConversion | a.SensorType.HasValue)
moel@403
   315
                  physical = a.ConvertValue(value, null);
moel@403
   316
                else
moel@403
   317
                  physical = null;
moel@403
   318
              }
moel@403
   319
            }
moel@403
   320
moel@403
   321
            string raw = BitConverter.ToString(value.RawValue);
moel@403
   322
            r.AppendFormat(CultureInfo.InvariantCulture,
moel@403
   323
              " {0}{1}{2}{3}{4}{5}{6}{7}",
moel@403
   324
              value.Identifier.ToString("X2").PadRight(3),
moel@403
   325
              description.PadRight(35),
moel@403
   326
              raw.Replace("-", "").PadRight(13),
moel@403
   327
              value.WorstValue.ToString(CultureInfo.InvariantCulture).PadRight(6),
moel@403
   328
              value.AttrValue.ToString(CultureInfo.InvariantCulture).PadRight(6),
moel@403
   329
              (threshold.HasValue ? threshold.Value.ToString(
moel@403
   330
                CultureInfo.InvariantCulture) : "-").PadRight(6),
moel@403
   331
              (physical.HasValue ? physical.Value.ToString(
moel@403
   332
                CultureInfo.InvariantCulture) : "-").PadRight(8),
moel@403
   333
              Environment.NewLine);
moel@324
   334
          }
moel@403
   335
          r.AppendLine();
moel@324
   336
        }
moel@324
   337
      }
moel@324
   338
moel@369
   339
      foreach (DriveInfo di in driveInfos) {
moel@404
   340
        if (!di.IsReady)
moel@404
   341
          continue;
moel@404
   342
        try {
moel@404
   343
          r.AppendLine("Logical drive name: " + di.Name);
moel@404
   344
          r.AppendLine("Format: " + di.DriveFormat);
moel@404
   345
          r.AppendLine("Total size: " + di.TotalSize);
moel@404
   346
          r.AppendLine("Total free space: " + di.TotalFreeSpace);
moel@404
   347
          r.AppendLine();
moel@404
   348
        } catch (IOException) { } catch (UnauthorizedAccessException) { }
moel@369
   349
      }
moel@369
   350
moel@324
   351
      return r.ToString();
moel@324
   352
    }
moel@324
   353
moel@374
   354
    protected static float RawToInt(byte[] raw, byte value,
moel@374
   355
      IReadOnlyArray<IParameter> parameters) 
moel@374
   356
    {
moel@324
   357
      return (raw[3] << 24) | (raw[2] << 16) | (raw[1] << 8) | raw[0];
moel@324
   358
    }
moel@324
   359
moel@324
   360
    public override void Close() {
moel@403
   361
      if (handle != smart.InvalidHandle)
moel@403
   362
        smart.CloseHandle(handle);
moel@403
   363
moel@324
   364
      base.Close();
moel@324
   365
    }
moel@324
   366
moel@324
   367
    public override void Traverse(IVisitor visitor) {
moel@324
   368
      foreach (ISensor sensor in Sensors)
moel@324
   369
        sensor.Accept(visitor);
moel@324
   370
    }
moel@324
   371
  }
moel@324
   372
}