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 <mmoeller@openhardwaremonitor.org>
moel@344: 	Copyright (C) 2010 Paul Werelds
moel@344:   Copyright (C) 2011 Roland Reinl <roland-reinl@gmx.de>
moel@344: 	
moel@324: */
moel@324: 
moel@324: using System;
moel@324: using System.Collections.Generic;
moel@324: using System.Globalization;
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@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<SmartAttribute> smartAttributes;
moel@324:     private IDictionary<SmartAttribute, Sensor> sensors;
moel@324: 
moel@325:     protected AbstractHarddrive(ISmart smart, string name, 
moel@325:       string firmwareRevision, int index, 
moel@324:       IEnumerable<SmartAttribute> 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<SmartAttribute>(smartAttributes);
moel@324: 
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@324:         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<SmartAttribute, Sensor>();
moel@324: 
moel@324:       IList<Pair<SensorType, int>> sensorTypeAndChannels = 
moel@324:         new List<Pair<SensorType, int>>();
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<SensorType, int> pair = new Pair<SensorType, int>(
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@340:             attribute.SensorType.Value, this, null, settings);
moel@324: 
moel@324:           sensors.Add(attribute, sensor);
moel@339:           ActivateSensor(sensor);
moel@324:           sensorTypeAndChannels.Add(pair);
moel@324:         }     
moel@324:       }
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<SmartAttribute, Sensor> 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@324:               sensor.Value = attribute.ConvertValue(value);
moel@324:             }
moel@324:           }
moel@339:         }
moel@339: 
moel@339:         UpdateAdditionalSensors(values);
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@324:         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@324:                 physical = a.ConvertValue(value);
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@324:       return r.ToString();
moel@324:     }
moel@324: 
moel@324:     protected static float RawToInt(byte[] raw, byte value) {
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: }