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@344: Copyright (C) 2009-2012 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@324: 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@369: if (di.TotalSize > 0) moel@369: driveInfoList.Add(new DriveInfo(logicalDrive)); moel@369: } catch (ArgumentException) { } catch (IOException) { } 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@324: if (deviceHandle == smart.InvalidHandle) moel@324: return null; moel@324: moel@325: string name; moel@325: string firmwareRevision; moel@325: bool nameValid = smart.ReadNameAndFirmwareRevision(deviceHandle, moel@325: driveIndex, out name, out firmwareRevision); moel@324: bool smartEnabled = smart.EnableSmart(deviceHandle, driveIndex); moel@324: moel@324: DriveAttributeValue[] values = {}; moel@324: if (smartEnabled) moel@324: values = smart.ReadSmartData(deviceHandle, driveIndex); moel@324: moel@324: smart.CloseHandle(deviceHandle); moel@324: moel@325: if (!nameValid || string.IsNullOrEmpty(name)) moel@369: return null; 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@324: IList> sensorTypeAndChannels = moel@324: new List>(); moel@324: moel@324: DriveAttributeValue[] values = smart.ReadSmartData(handle, index); moel@324: moel@324: foreach (SmartAttribute attribute in smartAttributes) { moel@324: if (!attribute.SensorType.HasValue) moel@324: continue; moel@324: moel@324: bool found = false; moel@324: foreach (DriveAttributeValue value in values) { moel@324: if (value.Identifier == attribute.Identifier) { moel@324: found = true; moel@324: break; moel@324: } moel@324: } moel@324: if (!found) moel@324: continue; moel@324: moel@324: Pair pair = new Pair( moel@324: attribute.SensorType.Value, attribute.SensorChannel); moel@324: moel@324: if (!sensorTypeAndChannels.Contains(pair)) { moel@324: Sensor sensor = new Sensor(attribute.Name, moel@340: attribute.SensorChannel, attribute.DefaultHiddenSensor, moel@374: attribute.SensorType.Value, this, attribute.ParameterDescriptions, moel@374: settings); moel@324: moel@324: sensors.Add(attribute, sensor); moel@339: ActivateSensor(sensor); moel@324: sensorTypeAndChannels.Add(pair); 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@324: DriveAttributeValue[] values = smart.ReadSmartData(handle, index); moel@324: moel@324: foreach (KeyValuePair keyValuePair in sensors) { moel@324: SmartAttribute attribute = keyValuePair.Key; moel@324: foreach (DriveAttributeValue value in values) { moel@324: if (value.Identifier == attribute.Identifier) { moel@324: Sensor sensor = keyValuePair.Value; moel@374: sensor.Value = attribute.ConvertValue(value, sensor.Parameters); moel@324: } moel@324: } moel@339: } moel@339: moel@339: UpdateAdditionalSensors(values); moel@369: moel@369: if (usageSensor != null) { moel@369: long totalSize = 0; moel@369: long totalFreeSpace = 0; moel@369: for (int i = 0; i < driveInfos.Length; i++) { moel@369: totalSize += driveInfos[i].TotalSize; moel@369: totalFreeSpace += driveInfos[i].TotalFreeSpace; moel@369: } moel@369: usageSensor.Value = 100.0f - (100.0f * totalFreeSpace) / totalSize; 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: DriveAttributeValue[] values = smart.ReadSmartData(handle, index); moel@324: DriveThresholdValue[] thresholds = moel@324: smart.ReadSmartThresholds(handle, index); moel@324: moel@324: if (values.Length > 0) { moel@324: r.AppendLine(this.GetType().Name); moel@324: r.AppendLine(); moel@324: r.AppendLine("Drive name: " + name); moel@325: r.AppendLine("Firmware version: " + firmwareRevision); moel@369: r.AppendLine(); moel@324: r.AppendFormat(CultureInfo.InvariantCulture, moel@324: " {0}{1}{2}{3}{4}{5}{6}{7}", moel@324: ("ID").PadRight(3), moel@328: ("Description").PadRight(35), moel@324: ("Raw Value").PadRight(13), moel@324: ("Worst").PadRight(6), moel@324: ("Value").PadRight(6), moel@324: ("Thres").PadRight(6), moel@324: ("Physical").PadRight(8), moel@324: Environment.NewLine); moel@324: moel@324: foreach (DriveAttributeValue value in values) { moel@324: if (value.Identifier == 0x00) moel@324: break; moel@324: moel@324: byte? threshold = null; moel@324: foreach (DriveThresholdValue t in thresholds) { moel@324: if (t.Identifier == value.Identifier) { moel@324: threshold = t.Threshold; moel@324: } moel@324: } moel@324: moel@324: string description = "Unknown"; moel@324: float? physical = null; moel@324: foreach (SmartAttribute a in smartAttributes) { moel@324: if (a.Identifier == value.Identifier) { moel@324: description = a.Name; moel@324: if (a.HasRawValueConversion | a.SensorType.HasValue) moel@374: physical = a.ConvertValue(value, null); moel@324: else moel@324: physical = null; moel@324: } moel@324: } moel@324: moel@324: string raw = BitConverter.ToString(value.RawValue); moel@324: r.AppendFormat(CultureInfo.InvariantCulture, moel@324: " {0}{1}{2}{3}{4}{5}{6}{7}", moel@324: value.Identifier.ToString("X2").PadRight(3), moel@328: description.PadRight(35), moel@324: raw.Replace("-", "").PadRight(13), moel@324: value.WorstValue.ToString(CultureInfo.InvariantCulture).PadRight(6), moel@324: value.AttrValue.ToString(CultureInfo.InvariantCulture).PadRight(6), moel@324: (threshold.HasValue ? threshold.Value.ToString( moel@324: CultureInfo.InvariantCulture) : "-").PadRight(6), moel@324: (physical.HasValue ? physical.Value.ToString( moel@324: CultureInfo.InvariantCulture) : "-").PadRight(8), moel@324: Environment.NewLine); moel@324: } moel@324: r.AppendLine(); moel@324: } moel@324: moel@369: foreach (DriveInfo di in driveInfos) { moel@369: r.AppendLine("Logical drive name: " + di.Name); moel@369: r.AppendLine("Format: " + di.DriveFormat); moel@369: r.AppendLine("Total size: " + di.TotalSize); moel@369: r.AppendLine("Total free space: " + di.TotalFreeSpace); moel@369: r.AppendLine(); 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@324: smart.CloseHandle(handle); 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: }