moel@324: /* moel@324: moel@344: This Source Code Form is subject to the terms of the Mozilla Public moel@344: License, v. 2.0. If a copy of the MPL was not distributed with this moel@344: file, You can obtain one at http://mozilla.org/MPL/2.0/. moel@324: moel@403: Copyright (C) 2009-2013 Michael Möller moel@344: Copyright (C) 2010 Paul Werelds moel@344: Copyright (C) 2011 Roland Reinl moel@344: moel@324: */ moel@324: moel@324: using System; moel@324: using System.Collections.Generic; moel@324: using System.Globalization; moel@369: using System.IO; moel@324: using System.Text; moel@324: using OpenHardwareMonitor.Collections; moel@324: moel@324: namespace OpenHardwareMonitor.Hardware.HDD { moel@324: internal abstract class AbstractHarddrive : Hardware { moel@324: moel@324: private const int UPDATE_DIVIDER = 30; // update only every 30s moel@324: moel@324: // array of all harddrive types, matching type is searched in this order moel@324: private static Type[] hddTypes = { moel@324: typeof(SSDPlextor), moel@324: typeof(SSDIntel), moel@324: typeof(SSDSandforce), moel@324: typeof(SSDIndilinx), moel@328: typeof(SSDSamsung), moel@358: typeof(SSDMicron), moel@324: typeof(GenericHarddisk) moel@324: }; moel@324: moel@325: private string firmwareRevision; moel@324: private readonly ISmart smart; moel@324: moel@324: private readonly IntPtr handle; moel@324: private readonly int index; moel@324: private int count; moel@324: moel@324: private IList smartAttributes; moel@324: private IDictionary sensors; moel@324: moel@369: private DriveInfo[] driveInfos; moel@369: private Sensor usageSensor; moel@369: moel@325: protected AbstractHarddrive(ISmart smart, string name, moel@325: string firmwareRevision, int index, moel@324: IEnumerable smartAttributes, ISettings settings) moel@324: : base(name, new Identifier("hdd", moel@324: index.ToString(CultureInfo.InvariantCulture)), settings) moel@324: { moel@325: this.firmwareRevision = firmwareRevision; moel@324: this.smart = smart; moel@324: handle = smart.OpenDrive(index); moel@324: moel@403: if (handle != smart.InvalidHandle) moel@403: smart.EnableSmart(handle, index); moel@324: moel@324: this.index = index; moel@324: this.count = 0; moel@324: moel@324: this.smartAttributes = new List(smartAttributes); moel@324: moel@369: string[] logicalDrives = smart.GetLogicalDrives(index); moel@369: List driveInfoList = new List(logicalDrives.Length); moel@369: foreach (string logicalDrive in logicalDrives) { moel@369: try { moel@369: DriveInfo di = new DriveInfo(logicalDrive); moel@409: if (di.TotalSize > 0) moel@369: driveInfoList.Add(new DriveInfo(logicalDrive)); moel@409: } catch (ArgumentException) { moel@409: } catch (IOException) { moel@409: } catch (UnauthorizedAccessException) { moel@409: } moel@369: } moel@369: driveInfos = driveInfoList.ToArray(); moel@369: moel@324: CreateSensors(); moel@324: } moel@324: moel@324: public static AbstractHarddrive CreateInstance(ISmart smart, moel@324: int driveIndex, ISettings settings) moel@324: { moel@324: IntPtr deviceHandle = smart.OpenDrive(driveIndex); moel@324: moel@403: string name = null; moel@403: string firmwareRevision = null; moel@403: DriveAttributeValue[] values = { }; moel@324: moel@403: if (deviceHandle != smart.InvalidHandle) { moel@403: bool nameValid = smart.ReadNameAndFirmwareRevision(deviceHandle, moel@325: driveIndex, out name, out firmwareRevision); moel@403: bool smartEnabled = smart.EnableSmart(deviceHandle, driveIndex); moel@324: moel@403: if (smartEnabled) moel@403: values = smart.ReadSmartData(deviceHandle, driveIndex); moel@324: moel@403: smart.CloseHandle(deviceHandle); moel@324: moel@403: if (!nameValid) { moel@403: name = null; moel@403: firmwareRevision = null; moel@403: } moel@403: } else { moel@403: string[] logicalDrives = smart.GetLogicalDrives(driveIndex); moel@403: if (logicalDrives == null || logicalDrives.Length == 0) moel@403: return null; moel@409: moel@409: bool hasNonZeroSizeDrive = false; moel@409: foreach (string logicalDrive in logicalDrives) { moel@409: try { moel@409: DriveInfo di = new DriveInfo(logicalDrive); moel@409: if (di.TotalSize > 0) { moel@409: hasNonZeroSizeDrive = true; moel@409: break; moel@409: } moel@409: } catch (ArgumentException) { moel@409: } catch (IOException) { moel@409: } catch (UnauthorizedAccessException) { moel@409: } moel@409: } moel@409: moel@409: if (!hasNonZeroSizeDrive) moel@409: return null; moel@403: } moel@403: moel@403: if (string.IsNullOrEmpty(name)) moel@403: name = "Generic Hard Disk"; moel@403: moel@403: if (string.IsNullOrEmpty(firmwareRevision)) moel@403: firmwareRevision = "Unknown"; moel@324: moel@324: foreach (Type type in hddTypes) { moel@324: // get the array of name prefixes for the current type moel@324: NamePrefixAttribute[] namePrefixes = type.GetCustomAttributes( moel@324: typeof(NamePrefixAttribute), true) as NamePrefixAttribute[]; moel@324: moel@324: // get the array of the required SMART attributes for the current type moel@324: RequireSmartAttribute[] requiredAttributes = type.GetCustomAttributes( moel@324: typeof(RequireSmartAttribute), true) as RequireSmartAttribute[]; moel@324: moel@324: // check if all required attributes are present moel@324: bool allRequiredAttributesFound = true; moel@324: foreach (var requireAttribute in requiredAttributes) { moel@324: bool adttributeFound = false; moel@324: foreach (DriveAttributeValue value in values) { moel@324: if (value.Identifier == requireAttribute.AttributeId) { moel@324: adttributeFound = true; moel@324: break; moel@324: } moel@324: } moel@324: if (!adttributeFound) { moel@324: allRequiredAttributesFound = false; moel@324: break; moel@324: } moel@324: } moel@324: moel@324: // if an attribute is missing, then try the next type moel@324: if (!allRequiredAttributesFound) moel@324: continue; moel@324: moel@324: // check if there is a matching name prefix for this type moel@324: foreach (NamePrefixAttribute prefix in namePrefixes) { moel@324: if (name.StartsWith(prefix.Prefix, StringComparison.InvariantCulture)) moel@325: return Activator.CreateInstance(type, smart, name, firmwareRevision, moel@325: driveIndex, settings) as AbstractHarddrive; moel@324: } moel@324: } moel@324: moel@324: // no matching type has been found moel@324: return null; moel@324: } moel@324: moel@324: private void CreateSensors() { moel@324: sensors = new Dictionary(); moel@324: moel@403: if (handle != smart.InvalidHandle) { moel@403: IList> sensorTypeAndChannels = moel@403: new List>(); moel@324: moel@403: DriveAttributeValue[] values = smart.ReadSmartData(handle, index); moel@324: moel@403: foreach (SmartAttribute attribute in smartAttributes) { moel@403: if (!attribute.SensorType.HasValue) moel@403: continue; moel@324: moel@403: bool found = false; moel@403: foreach (DriveAttributeValue value in values) { moel@403: if (value.Identifier == attribute.Identifier) { moel@403: found = true; moel@403: break; moel@403: } moel@403: } moel@403: if (!found) moel@403: continue; moel@403: moel@403: Pair pair = new Pair( moel@403: attribute.SensorType.Value, attribute.SensorChannel); moel@403: moel@403: if (!sensorTypeAndChannels.Contains(pair)) { moel@403: Sensor sensor = new Sensor(attribute.Name, moel@403: attribute.SensorChannel, attribute.DefaultHiddenSensor, moel@403: attribute.SensorType.Value, this, attribute.ParameterDescriptions, moel@403: settings); moel@403: moel@403: sensors.Add(attribute, sensor); moel@403: ActivateSensor(sensor); moel@403: sensorTypeAndChannels.Add(pair); moel@324: } moel@324: } moel@324: } moel@369: moel@369: if (driveInfos.Length > 0) { moel@369: usageSensor = moel@369: new Sensor("Used Space", 0, SensorType.Load, this, settings); moel@369: ActivateSensor(usageSensor); moel@369: } moel@324: } moel@324: moel@324: public override HardwareType HardwareType { moel@324: get { return HardwareType.HDD; } moel@324: } moel@324: moel@339: public virtual void UpdateAdditionalSensors(DriveAttributeValue[] values) {} moel@324: moel@324: public override void Update() { moel@324: if (count == 0) { moel@403: if (handle != smart.InvalidHandle) { moel@403: DriveAttributeValue[] values = smart.ReadSmartData(handle, index); moel@324: moel@403: foreach (KeyValuePair keyValuePair in sensors) moel@403: { moel@403: SmartAttribute attribute = keyValuePair.Key; moel@403: foreach (DriveAttributeValue value in values) { moel@403: if (value.Identifier == attribute.Identifier) { moel@403: Sensor sensor = keyValuePair.Value; moel@403: sensor.Value = attribute.ConvertValue(value, sensor.Parameters); moel@403: } moel@324: } moel@324: } moel@403: moel@403: UpdateAdditionalSensors(values); moel@339: } moel@339: moel@369: if (usageSensor != null) { moel@369: long totalSize = 0; moel@369: long totalFreeSpace = 0; moel@385: moel@369: for (int i = 0; i < driveInfos.Length; i++) { moel@385: if (!driveInfos[i].IsReady) moel@385: continue; moel@385: try { moel@385: totalSize += driveInfos[i].TotalSize; moel@385: totalFreeSpace += driveInfos[i].TotalFreeSpace; moel@385: } catch (IOException) { } catch (UnauthorizedAccessException) { } moel@369: } moel@385: if (totalSize > 0) { moel@385: usageSensor.Value = 100.0f - (100.0f * totalFreeSpace) / totalSize; moel@385: } else { moel@385: usageSensor.Value = null; moel@385: } moel@369: } moel@324: } moel@324: moel@324: count++; moel@324: count %= UPDATE_DIVIDER; moel@324: } moel@324: moel@324: public override string GetReport() { moel@324: StringBuilder r = new StringBuilder(); moel@324: moel@403: r.AppendLine(this.GetType().Name); moel@403: r.AppendLine(); moel@403: r.AppendLine("Drive name: " + name); moel@403: r.AppendLine("Firmware version: " + firmwareRevision); moel@403: r.AppendLine(); moel@324: moel@403: if (handle != smart.InvalidHandle) { moel@403: DriveAttributeValue[] values = smart.ReadSmartData(handle, index); moel@403: DriveThresholdValue[] thresholds = moel@403: smart.ReadSmartThresholds(handle, index); moel@324: moel@403: if (values.Length > 0) { moel@403: r.AppendFormat(CultureInfo.InvariantCulture, moel@403: " {0}{1}{2}{3}{4}{5}{6}{7}", moel@403: ("ID").PadRight(3), moel@403: ("Description").PadRight(35), moel@403: ("Raw Value").PadRight(13), moel@403: ("Worst").PadRight(6), moel@403: ("Value").PadRight(6), moel@403: ("Thres").PadRight(6), moel@403: ("Physical").PadRight(8), moel@403: Environment.NewLine); moel@403: moel@403: foreach (DriveAttributeValue value in values) { moel@403: if (value.Identifier == 0x00) moel@403: break; moel@403: moel@403: byte? threshold = null; moel@403: foreach (DriveThresholdValue t in thresholds) { moel@403: if (t.Identifier == value.Identifier) { moel@403: threshold = t.Threshold; moel@403: } moel@324: } moel@403: moel@403: string description = "Unknown"; moel@403: float? physical = null; moel@403: foreach (SmartAttribute a in smartAttributes) { moel@403: if (a.Identifier == value.Identifier) { moel@403: description = a.Name; moel@403: if (a.HasRawValueConversion | a.SensorType.HasValue) moel@403: physical = a.ConvertValue(value, null); moel@403: else moel@403: physical = null; moel@403: } moel@403: } moel@403: moel@403: string raw = BitConverter.ToString(value.RawValue); moel@403: r.AppendFormat(CultureInfo.InvariantCulture, moel@403: " {0}{1}{2}{3}{4}{5}{6}{7}", moel@403: value.Identifier.ToString("X2").PadRight(3), moel@403: description.PadRight(35), moel@403: raw.Replace("-", "").PadRight(13), moel@403: value.WorstValue.ToString(CultureInfo.InvariantCulture).PadRight(6), moel@403: value.AttrValue.ToString(CultureInfo.InvariantCulture).PadRight(6), moel@403: (threshold.HasValue ? threshold.Value.ToString( moel@403: CultureInfo.InvariantCulture) : "-").PadRight(6), moel@403: (physical.HasValue ? physical.Value.ToString( moel@403: CultureInfo.InvariantCulture) : "-").PadRight(8), moel@403: Environment.NewLine); moel@324: } moel@403: r.AppendLine(); moel@324: } moel@324: } moel@324: moel@369: foreach (DriveInfo di in driveInfos) { moel@404: if (!di.IsReady) moel@404: continue; moel@404: try { moel@404: r.AppendLine("Logical drive name: " + di.Name); moel@404: r.AppendLine("Format: " + di.DriveFormat); moel@404: r.AppendLine("Total size: " + di.TotalSize); moel@404: r.AppendLine("Total free space: " + di.TotalFreeSpace); moel@404: r.AppendLine(); moel@404: } catch (IOException) { } catch (UnauthorizedAccessException) { } moel@369: } moel@369: moel@324: return r.ToString(); moel@324: } moel@324: moel@374: protected static float RawToInt(byte[] raw, byte value, moel@374: IReadOnlyArray parameters) moel@374: { moel@324: return (raw[3] << 24) | (raw[2] << 16) | (raw[1] << 8) | raw[0]; moel@324: } moel@324: moel@324: public override void Close() { moel@403: if (handle != smart.InvalidHandle) moel@403: smart.CloseHandle(handle); moel@403: moel@324: base.Close(); moel@324: } moel@324: moel@324: public override void Traverse(IVisitor visitor) { moel@324: foreach (ISensor sensor in Sensors) moel@324: sensor.Accept(visitor); moel@324: } moel@324: } moel@324: }