diff --git a/LibreHardwareMonitor/LibreHardwareMonitor.csproj b/LibreHardwareMonitor/LibreHardwareMonitor.csproj index 6efcd42..5384736 100644 --- a/LibreHardwareMonitor/LibreHardwareMonitor.csproj +++ b/LibreHardwareMonitor/LibreHardwareMonitor.csproj @@ -11,6 +11,7 @@ LibreHardwareMonitor.Program true ..\bin\$(Configuration)\ + latest full diff --git a/LibreHardwareMonitor/Resources/power-supply.png b/LibreHardwareMonitor/Resources/power-supply.png new file mode 100644 index 0000000..edcf5ac Binary files /dev/null and b/LibreHardwareMonitor/Resources/power-supply.png differ diff --git a/LibreHardwareMonitor/Resources/time.png b/LibreHardwareMonitor/Resources/time.png new file mode 100644 index 0000000..e0e7c3c Binary files /dev/null and b/LibreHardwareMonitor/Resources/time.png differ diff --git a/LibreHardwareMonitor/UI/HardwareNode.cs b/LibreHardwareMonitor/UI/HardwareNode.cs index 9abed78..fc2ed6c 100644 --- a/LibreHardwareMonitor/UI/HardwareNode.cs +++ b/LibreHardwareMonitor/UI/HardwareNode.cs @@ -8,6 +8,7 @@ using LibreHardwareMonitor.Hardware; using LibreHardwareMonitor.Utilities; using System; using System.Collections.Generic; +using System.Text; namespace LibreHardwareMonitor.UI { @@ -48,6 +49,27 @@ namespace LibreHardwareMonitor.UI set { Hardware.Name = value; } } + public override string ToolTip + { + get + { + IDictionary properties = Hardware.Properties; + + if (properties.Count > 0) + { + StringBuilder stringBuilder = new(); + stringBuilder.AppendLine("Hardware properties:"); + + foreach (KeyValuePair property in properties) + stringBuilder.AppendFormat(" • {0}: {1}\n", property.Key, property.Value); + + return stringBuilder.ToString(); + } + + return null; + } + } + public IHardware Hardware { get; } public bool Expanded diff --git a/LibreHardwareMonitor/UI/HardwareTypeImage.cs b/LibreHardwareMonitor/UI/HardwareTypeImage.cs index e30aebb..42b96d0 100644 --- a/LibreHardwareMonitor/UI/HardwareTypeImage.cs +++ b/LibreHardwareMonitor/UI/HardwareTypeImage.cs @@ -54,6 +54,9 @@ namespace LibreHardwareMonitor.UI case HardwareType.Cooler: image = Utilities.EmbeddedResources.GetImage("fan.png"); break; + case HardwareType.Psu: + image = Utilities.EmbeddedResources.GetImage("power-supply.png"); + break; default: image = new Bitmap(1, 1); break; diff --git a/LibreHardwareMonitor/UI/MainForm.Designer.cs b/LibreHardwareMonitor/UI/MainForm.Designer.cs index 90f15b4..920944e 100644 --- a/LibreHardwareMonitor/UI/MainForm.Designer.cs +++ b/LibreHardwareMonitor/UI/MainForm.Designer.cs @@ -125,6 +125,7 @@ namespace LibreHardwareMonitor.UI this.timer = new System.Windows.Forms.Timer(this.components); this.splitContainer = new LibreHardwareMonitor.UI.SplitContainerAdv(); this.treeView = new Aga.Controls.Tree.TreeViewAdv(); + this.psuMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.mainMenu.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit(); this.splitContainer.Panel1.SuspendLayout(); @@ -263,7 +264,8 @@ namespace LibreHardwareMonitor.UI this.gpuMenuItem, this.fanControllerMenuItem, this.hddMenuItem, - this.nicMenuItem}); + this.nicMenuItem, + this.psuMenuItem}); this.menuItem5.Name = "menuItem5"; this.menuItem5.Size = new System.Drawing.Size(145, 22); this.menuItem5.Text = "Hardware"; @@ -844,12 +846,19 @@ namespace LibreHardwareMonitor.UI this.treeView.MouseMove += new System.Windows.Forms.MouseEventHandler(this.TreeView_MouseMove); this.treeView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.TreeView_MouseUp); // + // psuMenuItem + // + this.psuMenuItem.Name = "psuMenuItem"; + this.psuMenuItem.Size = new System.Drawing.Size(180, 22); + this.psuMenuItem.Text = "Power supplies"; + // // MainForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(418, 533); this.Controls.Add(this.splitContainer); + this.Controls.Add(this.mainMenu); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MainMenuStrip = this.mainMenu; this.Name = "MainForm"; @@ -860,11 +869,11 @@ namespace LibreHardwareMonitor.UI this.Move += new System.EventHandler(this.MainForm_MoveOrResize); this.mainMenu.ResumeLayout(false); this.mainMenu.PerformLayout(); - this.Controls.Add(mainMenu); this.splitContainer.Panel1.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit(); this.splitContainer.ResumeLayout(false); this.ResumeLayout(false); + this.PerformLayout(); } @@ -959,6 +968,7 @@ namespace LibreHardwareMonitor.UI private ToolStripRadioButtonMenuItem timeWindow12hMenuItem; private ToolStripRadioButtonMenuItem timeWindow24hMenuItem; private System.Windows.Forms.ToolStripMenuItem authWebServerMenuItem; + private System.Windows.Forms.ToolStripMenuItem psuMenuItem; } } diff --git a/LibreHardwareMonitor/UI/MainForm.cs b/LibreHardwareMonitor/UI/MainForm.cs index 013d0ab..4fabdf8 100644 --- a/LibreHardwareMonitor/UI/MainForm.cs +++ b/LibreHardwareMonitor/UI/MainForm.cs @@ -49,6 +49,7 @@ namespace LibreHardwareMonitor.UI private readonly UserOption _readFanControllersSensors; private readonly UserOption _readHddSensors; private readonly UserOption _readNicSensors; + private readonly UserOption _readPsuSensors; private readonly UserOption _showGadget; private UserRadioGroup _plotLocation; @@ -137,6 +138,10 @@ namespace LibreHardwareMonitor.UI _wmiProvider = new WmiProvider(_computer); } + treeView.ShowNodeToolTips = true; + NodeToolTipProvider tooltipProvider = new(); + nodeTextBoxText.ToolTipProvider = tooltipProvider; + nodeTextBoxValue.ToolTipProvider = tooltipProvider; _logger = new Logger(_computer); _plotColorPalette = new Color[13]; @@ -250,6 +255,12 @@ namespace LibreHardwareMonitor.UI _computer.IsNetworkEnabled = _readNicSensors.Value; }; + _readPsuSensors = new UserOption("psuMenuItem", true, psuMenuItem, _settings); + _readPsuSensors.Changed += delegate + { + _computer.IsPsuEnabled = _readPsuSensors.Value; + }; + _showGadget = new UserOption("gadgetMenuItem", false, gadgetMenuItem, _settings); _showGadget.Changed += delegate { diff --git a/LibreHardwareMonitor/UI/Node.cs b/LibreHardwareMonitor/UI/Node.cs index df30b02..1ef5131 100644 --- a/LibreHardwareMonitor/UI/Node.cs +++ b/LibreHardwareMonitor/UI/Node.cs @@ -72,6 +72,8 @@ namespace LibreHardwareMonitor.UI } } + public virtual string ToolTip { get; } + public Image Image { get; set; } public virtual bool IsVisible diff --git a/LibreHardwareMonitor/UI/NodeToolTipProvider.cs b/LibreHardwareMonitor/UI/NodeToolTipProvider.cs new file mode 100644 index 0000000..09ce236 --- /dev/null +++ b/LibreHardwareMonitor/UI/NodeToolTipProvider.cs @@ -0,0 +1,15 @@ +// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. +// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// Copyright (C) LibreHardwareMonitor and Contributors. +// All Rights Reserved. + +using Aga.Controls.Tree; +using Aga.Controls.Tree.NodeControls; + +namespace LibreHardwareMonitor.UI +{ + internal class NodeToolTipProvider : IToolTipProvider + { + public string GetToolTip(TreeNodeAdv node, NodeControl nodeControl) => (node.Tag as Node)?.ToolTip; + } +} diff --git a/LibreHardwareMonitor/UI/SensorGadget.cs b/LibreHardwareMonitor/UI/SensorGadget.cs index dae5ad6..341c716 100644 --- a/LibreHardwareMonitor/UI/SensorGadget.cs +++ b/LibreHardwareMonitor/UI/SensorGadget.cs @@ -640,6 +640,9 @@ namespace LibreHardwareMonitor.UI case SensorType.Factor: format = "{0:F3}"; break; + case SensorType.TimeSpan: + format = "{0:g}"; + break; } if (sensor.SensorType == SensorType.Temperature && _unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit) @@ -687,6 +690,10 @@ namespace LibreHardwareMonitor.UI } formatted = result; } + else if (sensor.SensorType == SensorType.TimeSpan) + { + formatted = string.Format(format, TimeSpan.FromSeconds(sensor.Value.Value)); + } else { formatted = string.Format(format, sensor.Value); diff --git a/LibreHardwareMonitor/UI/SensorNode.cs b/LibreHardwareMonitor/UI/SensorNode.cs index bd0be34..2d88343 100644 --- a/LibreHardwareMonitor/UI/SensorNode.cs +++ b/LibreHardwareMonitor/UI/SensorNode.cs @@ -6,6 +6,8 @@ using System; using System.Drawing; +using System.Globalization; +using System.Text; using LibreHardwareMonitor.Hardware; using LibreHardwareMonitor.Utilities; @@ -15,10 +17,157 @@ namespace LibreHardwareMonitor.UI { private readonly PersistentSettings _settings; private readonly UnitManager _unitManager; - private bool _plot; private Color? _penColor; + private bool _plot; + + public SensorNode(ISensor sensor, PersistentSettings settings, UnitManager unitManager) + { + Sensor = sensor; + _settings = settings; + _unitManager = unitManager; + + switch (sensor.SensorType) + { + case SensorType.Voltage: + Format = "{0:F3} V"; + break; + case SensorType.Current: + Format = "{0:F3} A"; + break; + case SensorType.Clock: + Format = "{0:F1} MHz"; + break; + case SensorType.Load: + Format = "{0:F1} %"; + break; + case SensorType.Temperature: + Format = "{0:F1} °C"; + break; + case SensorType.Fan: + Format = "{0:F0} RPM"; + break; + case SensorType.Flow: + Format = "{0:F1} L/h"; + break; + case SensorType.Control: + Format = "{0:F1} %"; + break; + case SensorType.Level: + Format = "{0:F1} %"; + break; + case SensorType.Power: + Format = "{0:F1} W"; + break; + case SensorType.Data: + Format = "{0:F1} GB"; + break; + case SensorType.SmallData: + Format = "{0:F1} MB"; + break; + case SensorType.Factor: + Format = "{0:F3}"; + break; + case SensorType.Frequency: + Format = "{0:F1} Hz"; + break; + case SensorType.Throughput: + Format = "{0:F1} B/s"; + break; + case SensorType.TimeSpan: + Format = "{0:g}"; + break; + } + + bool hidden = settings.GetValue(new Identifier(sensor.Identifier, "hidden").ToString(), sensor.IsDefaultHidden); + base.IsVisible = !hidden; + Plot = settings.GetValue(new Identifier(sensor.Identifier, "plot").ToString(), false); + string id = new Identifier(sensor.Identifier, "penColor").ToString(); + + if (settings.Contains(id)) + PenColor = settings.GetValue(id, Color.Black); + } + + public event EventHandler PlotSelectionChanged; + public string Format { get; set; } = ""; + public override bool IsVisible + { + get { return base.IsVisible; } + set + { + base.IsVisible = value; + _settings.SetValue(new Identifier(Sensor.Identifier, "hidden").ToString(), !value); + } + } + + public string Max + { + get { return ValueToString(Sensor.Max); } + } + + public string Min + { + get { return ValueToString(Sensor.Min); } + } + + public Color? PenColor + { + get { return _penColor; } + set + { + _penColor = value; + + string id = new Identifier(Sensor.Identifier, "penColor").ToString(); + if (value.HasValue) + _settings.SetValue(id, value.Value); + else + _settings.Remove(id); + + PlotSelectionChanged?.Invoke(this, null); + } + } + + public bool Plot + { + get { return _plot; } + set + { + _plot = value; + _settings.SetValue(new Identifier(Sensor.Identifier, "plot").ToString(), value); + PlotSelectionChanged?.Invoke(this, null); + } + } + + public ISensor Sensor { get; } + + public override string Text + { + get { return Sensor.Name; } + set { Sensor.Name = value; } + } + + public override string ToolTip + { + get + { + StringBuilder stringBuilder = new(); + + if (Sensor is ICriticalSensorLimits criticalSensorLimits) + OptionallyAppendCriticalRange(stringBuilder, criticalSensorLimits.CriticalLowLimit, criticalSensorLimits.CriticalHighLimit, "critical"); + + if (Sensor is ISensorLimits sensorLimits) + OptionallyAppendCriticalRange(stringBuilder, sensorLimits.LowLimit, sensorLimits.HighLimit, "normal"); + + return stringBuilder.ToString(); + } + } + + public string Value + { + get { return ValueToString(Sensor.Value); } + } + public string ValueToString(float? value) { if (value.HasValue) @@ -59,7 +208,8 @@ namespace LibreHardwareMonitor.UI else result = $"{value / 1073741824:F1} Gbps"; } - break; + + break; } break; @@ -73,8 +223,13 @@ namespace LibreHardwareMonitor.UI break; } } + return result; } + case SensorType.TimeSpan: + { + return value.HasValue ? string.Format(Format, TimeSpan.FromSeconds(value.Value)) : "-"; + } default: { return string.Format(Format, value); @@ -85,103 +240,20 @@ namespace LibreHardwareMonitor.UI return "-"; } - public SensorNode(ISensor sensor, PersistentSettings settings, UnitManager unitManager) + private void OptionallyAppendCriticalRange(StringBuilder str, float? min, float? max, string kind) { - Sensor = sensor; - _settings = settings; - _unitManager = unitManager; - - switch (sensor.SensorType) + if (min.HasValue) { - case SensorType.Voltage: Format = "{0:F3} V"; break; - case SensorType.Current: Format = "{0:F3} A"; break; - case SensorType.Clock: Format = "{0:F1} MHz"; break; - case SensorType.Load: Format = "{0:F1} %"; break; - case SensorType.Temperature: Format = "{0:F1} °C"; break; - case SensorType.Fan: Format = "{0:F0} RPM"; break; - case SensorType.Flow: Format = "{0:F1} L/h"; break; - case SensorType.Control: Format = "{0:F1} %"; break; - case SensorType.Level: Format = "{0:F1} %"; break; - case SensorType.Power: Format = "{0:F1} W"; break; - case SensorType.Data: Format = "{0:F1} GB"; break; - case SensorType.SmallData: Format = "{0:F1} MB"; break; - case SensorType.Factor: Format = "{0:F3}"; break; - case SensorType.Frequency: Format = "{0:F1} Hz"; break; - case SensorType.Throughput: Format = "{0:F1} B/s"; break; + str.AppendLine(max.HasValue + ? $"{CultureInfo.CurrentUICulture.TextInfo.ToTitleCase(kind)} range: {ValueToString(min)} to {ValueToString(max)}." + : $"Minimal {kind} value: {ValueToString(min)}."); } - - bool hidden = settings.GetValue(new Identifier(sensor.Identifier, "hidden").ToString(), sensor.IsDefaultHidden); - base.IsVisible = !hidden; - Plot = settings.GetValue(new Identifier(sensor.Identifier, "plot").ToString(), false); - string id = new Identifier(sensor.Identifier, "penColor").ToString(); - - if (settings.Contains(id)) - PenColor = settings.GetValue(id, Color.Black); - } - - public override string Text - { - get { return Sensor.Name; } - set { Sensor.Name = value; } - } - - public override bool IsVisible - { - get { return base.IsVisible; } - set + else if (max.HasValue) { - base.IsVisible = value; - _settings.SetValue(new Identifier(Sensor.Identifier, "hidden").ToString(), !value); + str.AppendLine($"Maximal {kind} value: {ValueToString(max)}."); } } - public Color? PenColor - { - get { return _penColor; } - set - { - _penColor = value; - - string id = new Identifier(Sensor.Identifier, "penColor").ToString(); - if (value.HasValue) - _settings.SetValue(id, value.Value); - else - _settings.Remove(id); - - PlotSelectionChanged?.Invoke(this, null); - } - } - - public bool Plot - { - get { return _plot; } - set - { - _plot = value; - _settings.SetValue(new Identifier(Sensor.Identifier, "plot").ToString(), value); - PlotSelectionChanged?.Invoke(this, null); - } - } - - public event EventHandler PlotSelectionChanged; - - public ISensor Sensor { get; } - - public string Value - { - get { return ValueToString(Sensor.Value); } - } - - public string Min - { - get { return ValueToString(Sensor.Min); } - } - - public string Max - { - get { return ValueToString(Sensor.Max); } - } - public override bool Equals(object obj) { if (obj == null) @@ -190,6 +262,7 @@ namespace LibreHardwareMonitor.UI if (!(obj is SensorNode s)) return false; + return (Sensor == s.Sensor); } diff --git a/LibreHardwareMonitor/UI/SensorNotifyIcon.cs b/LibreHardwareMonitor/UI/SensorNotifyIcon.cs index 1ab1e7b..dac2fbd 100644 --- a/LibreHardwareMonitor/UI/SensorNotifyIcon.cs +++ b/LibreHardwareMonitor/UI/SensorNotifyIcon.cs @@ -161,32 +161,30 @@ namespace LibreHardwareMonitor.UI switch (Sensor.SensorType) { - case SensorType.Voltage: - return $"{Sensor.Value:F1}"; - case SensorType.Current: - return $"{Sensor.Value:F1}"; - case SensorType.Clock: - return $"{1e-3f * Sensor.Value:F1}"; - case SensorType.Load: - return $"{Sensor.Value:F0}"; case SensorType.Temperature: return _unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit ? $"{UnitManager.CelsiusToFahrenheit(Sensor.Value):F0}" : $"{Sensor.Value:F0}"; + case SensorType.TimeSpan: + return $"{TimeSpan.FromSeconds(Sensor.Value.Value):g}"; + case SensorType.Clock: case SensorType.Fan: - return $"{1e-3f * Sensor.Value:F1}"; case SensorType.Flow: return $"{1e-3f * Sensor.Value:F1}"; - case SensorType.Control: - return $"{Sensor.Value:F0}"; - case SensorType.Level: - return $"{Sensor.Value:F0}"; - case SensorType.Power: - return $"{Sensor.Value:F0}"; - case SensorType.Data: - return $"{Sensor.Value:F0}"; + case SensorType.Voltage: + case SensorType.Current: + case SensorType.SmallData: case SensorType.Factor: + case SensorType.Throughput: return $"{Sensor.Value:F1}"; + case SensorType.Control: + case SensorType.Frequency: + case SensorType.Level: + case SensorType.Power: + case SensorType.Data: + case SensorType.Load: + return $"{Sensor.Value:F0}"; + default: + return "-"; } - return "-"; } private Icon CreateTransparentIcon() diff --git a/LibreHardwareMonitor/UI/TypeNode.cs b/LibreHardwareMonitor/UI/TypeNode.cs index 18a6610..183ff0b 100644 --- a/LibreHardwareMonitor/UI/TypeNode.cs +++ b/LibreHardwareMonitor/UI/TypeNode.cs @@ -83,6 +83,10 @@ namespace LibreHardwareMonitor.UI Image = Utilities.EmbeddedResources.GetImage("throughput.png"); Text = "Throughput"; break; + case SensorType.TimeSpan: + Image = Utilities.EmbeddedResources.GetImage("time.png"); + Text = "Times"; + break; } NodeAdded += TypeNode_NodeAdded; diff --git a/LibreHardwareMonitor/Utilities/HttpServer.cs b/LibreHardwareMonitor/Utilities/HttpServer.cs index 7a43375..1b9491d 100644 --- a/LibreHardwareMonitor/Utilities/HttpServer.cs +++ b/LibreHardwareMonitor/Utilities/HttpServer.cs @@ -641,6 +641,8 @@ namespace LibreHardwareMonitor.Utilities return "fan.png"; case HardwareType.Network: return "nic.png"; + case HardwareType.Psu: + return "power-supply.png"; default: return "cpu.png"; } diff --git a/LibreHardwareMonitorLib/Hardware/CompositeSensor.cs b/LibreHardwareMonitorLib/Hardware/CompositeSensor.cs new file mode 100644 index 0000000..66a11db --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/CompositeSensor.cs @@ -0,0 +1,40 @@ +// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. +// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// Copyright (C) LibreHardwareMonitor and Contributors. +// All Rights Reserved. + +using System; +using System.Linq; + +namespace LibreHardwareMonitor.Hardware +{ + internal class CompositeSensor : Sensor + { + private readonly ISensor[] _components; + private readonly Func _reducer; + private readonly float _seedValue; + + public CompositeSensor + ( + string name, + int index, + SensorType sensorType, + Hardware hardware, + ISettings settings, + ISensor[] components, + Func reducer, + float seedValue = .0f) + : base(name, index, sensorType, hardware, settings) + { + _components = components; + _reducer = reducer; + _seedValue = seedValue; + } + + public override float? Value + { + get { return _components.Aggregate(_seedValue, _reducer); } + set => throw new NotImplementedException(); + } + } +} diff --git a/LibreHardwareMonitorLib/Hardware/Computer.cs b/LibreHardwareMonitorLib/Hardware/Computer.cs index 8fce950..db9a8e8 100644 --- a/LibreHardwareMonitorLib/Hardware/Computer.cs +++ b/LibreHardwareMonitorLib/Hardware/Computer.cs @@ -18,6 +18,7 @@ using LibreHardwareMonitor.Hardware.Gpu; using LibreHardwareMonitor.Hardware.Memory; using LibreHardwareMonitor.Hardware.Motherboard; using LibreHardwareMonitor.Hardware.Network; +using LibreHardwareMonitor.Hardware.Psu.Corsair; using LibreHardwareMonitor.Hardware.Storage; namespace LibreHardwareMonitor.Hardware @@ -42,6 +43,7 @@ namespace LibreHardwareMonitor.Hardware private bool _open; private SMBios _smbios; private bool _storageEnabled; + private bool _psuEnabled; public Computer() { @@ -247,6 +249,30 @@ namespace LibreHardwareMonitor.Hardware } } + /// + /// Gets or sets a value indicating whether collecting information about devices should be enabled and updated. + /// + /// if a given category of devices is already enabled. + public bool IsPsuEnabled + { + get { return _psuEnabled; } + set + { + if (_open && value != _psuEnabled) + { + if (value) + { + Add(new CorsairPsuGroup(_settings)); + } + else + { + RemoveType(); + } + } + _psuEnabled = value; + } + } + /// /// Generates full LibreHardwareMonitor report for devices that have been enabled. /// @@ -490,6 +516,9 @@ namespace LibreHardwareMonitor.Hardware if (_networkEnabled) Add(new NetworkGroup(_settings)); + + if (_psuEnabled) + Add(new CorsairPsuGroup(_settings)); } private static void NewSection(TextWriter writer) diff --git a/LibreHardwareMonitorLib/Hardware/Hardware.cs b/LibreHardwareMonitorLib/Hardware/Hardware.cs index f04364d..6c70c2c 100644 --- a/LibreHardwareMonitorLib/Hardware/Hardware.cs +++ b/LibreHardwareMonitorLib/Hardware/Hardware.cs @@ -12,7 +12,7 @@ namespace LibreHardwareMonitor.Hardware { public abstract class Hardware : IHardware { - protected readonly HashSet _active = new HashSet(); + protected readonly HashSet _active = new(); protected readonly string _name; protected readonly ISettings _settings; private string _customName; @@ -25,6 +25,8 @@ namespace LibreHardwareMonitor.Hardware _customName = settings.GetValue(new Identifier(Identifier, "name").ToString(), name); } + public event HardwareEventHandler Closing; + public abstract HardwareType HardwareType { get; } public Identifier Identifier { get; } @@ -45,6 +47,8 @@ namespace LibreHardwareMonitor.Hardware get { return null; } } + public virtual IDictionary Properties => new SortedDictionary(); + public virtual ISensor[] Sensors { get { return _active.ToArray(); } @@ -89,8 +93,6 @@ namespace LibreHardwareMonitor.Hardware SensorRemoved?.Invoke(sensor); } - public event HardwareEventHandler Closing; - public virtual void Close() { Closing?.Invoke(this); diff --git a/LibreHardwareMonitorLib/Hardware/IComputer.cs b/LibreHardwareMonitorLib/Hardware/IComputer.cs index 2696f29..1cd169b 100644 --- a/LibreHardwareMonitorLib/Hardware/IComputer.cs +++ b/LibreHardwareMonitorLib/Hardware/IComputer.cs @@ -31,6 +31,8 @@ namespace LibreHardwareMonitor.Hardware bool IsMemoryEnabled { get; } + bool IsPsuEnabled { get; } + string GetReport(); event HardwareEventHandler HardwareAdded; diff --git a/LibreHardwareMonitorLib/Hardware/IHardware.cs b/LibreHardwareMonitorLib/Hardware/IHardware.cs index 5f09e59..0aa852c 100644 --- a/LibreHardwareMonitorLib/Hardware/IHardware.cs +++ b/LibreHardwareMonitorLib/Hardware/IHardware.cs @@ -4,6 +4,8 @@ // Partial Copyright (C) Michael Möller and Contributors. // All Rights Reserved. +using System.Collections.Generic; + namespace LibreHardwareMonitor.Hardware { public delegate void SensorEventHandler(ISensor sensor); @@ -22,7 +24,8 @@ namespace LibreHardwareMonitor.Hardware Storage, Network, Cooler, - EmbeddedController + EmbeddedController, + Psu } /// @@ -81,5 +84,10 @@ namespace LibreHardwareMonitor.Hardware /// /// event SensorEventHandler SensorRemoved; + + /// + /// Rarely changed hardware properties that can't be represented as sensors. + /// + IDictionary Properties { get; } } } diff --git a/LibreHardwareMonitorLib/Hardware/ISensor.cs b/LibreHardwareMonitorLib/Hardware/ISensor.cs index 750c685..ee19f77 100644 --- a/LibreHardwareMonitorLib/Hardware/ISensor.cs +++ b/LibreHardwareMonitorLib/Hardware/ISensor.cs @@ -28,7 +28,8 @@ namespace LibreHardwareMonitor.Hardware Factor, // 1 Data, // GB = 2^30 Bytes SmallData, // MB = 2^20 Bytes - Throughput // B/s + Throughput, // B/s + TimeSpan, // Seconds } /// diff --git a/LibreHardwareMonitorLib/Hardware/ISensorLimits.cs b/LibreHardwareMonitorLib/Hardware/ISensorLimits.cs new file mode 100644 index 0000000..9bf2aef --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/ISensorLimits.cs @@ -0,0 +1,21 @@ +// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. +// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// Copyright (C) LibreHardwareMonitor and Contributors. +// All Rights Reserved. + +namespace LibreHardwareMonitor.Hardware +{ + public interface ISensorLimits + { + float? HighLimit { get; } + + float? LowLimit { get; } + } + + public interface ICriticalSensorLimits + { + float? CriticalHighLimit { get; } + + float? CriticalLowLimit { get; } + } +} diff --git a/LibreHardwareMonitorLib/Hardware/Motherboard/Motherboard.cs b/LibreHardwareMonitorLib/Hardware/Motherboard/Motherboard.cs index fa415f7..a7bc22f 100644 --- a/LibreHardwareMonitorLib/Hardware/Motherboard/Motherboard.cs +++ b/LibreHardwareMonitorLib/Hardware/Motherboard/Motherboard.cs @@ -85,7 +85,7 @@ namespace LibreHardwareMonitor.Hardware.Motherboard public Identifier Identifier { - get { return new("motherboard"); } + get { return new Identifier("motherboard"); } } public string Name @@ -104,6 +104,8 @@ namespace LibreHardwareMonitor.Hardware.Motherboard get { return null; } } + public virtual IDictionary Properties => new SortedDictionary(); + public ISensor[] Sensors { get { return new ISensor[0]; } diff --git a/LibreHardwareMonitorLib/Hardware/Psu/Corsair/CorsairPsu.cs b/LibreHardwareMonitorLib/Hardware/Psu/Corsair/CorsairPsu.cs new file mode 100644 index 0000000..3cb48c1 --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/Psu/Corsair/CorsairPsu.cs @@ -0,0 +1,512 @@ +// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. +// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +// Copyright (C) 2020 Wilken Gottwalt +// Copyright (C) LibreHardwareMonitor and Contributors. +// All Rights Reserved. +// Implemented after the Linuix kernel driver corsair_psu by Wilken Gottwalt and contributers + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using HidSharp; + +namespace LibreHardwareMonitor.Hardware.Psu.Corsair +{ + internal class SensorIndices + { + private readonly Dictionary _lastIndices = new(); + + public int NextIndex(SensorType type) + { + if (!_lastIndices.ContainsKey(type)) + { + _lastIndices.Add(type, 0); + return 0; + } + + int res = _lastIndices[type] + 1; + _lastIndices[type] = res; + return res; + } + } + + // might need refactoring into two classes if AXi series API differs significantly + internal sealed class CorsairPsu : Hardware + { + private readonly List _compositeSensors = new(); + private readonly HidDevice _device; + private readonly List _sensors = new(); + + public CorsairPsu(HidDevice device, ISettings settings, int index) + : base("Corsair PSU", new Identifier("psu", "corsair", index.ToString()), settings) + { + _device = device; + using HidStream stream = device.Open(); + + UsbApi.Init(stream); + UsbApi.FirmwareInfo fwInfo = UsbApi.FwInfo(stream); + Name = $"{CultureInfo.InvariantCulture.TextInfo.ToTitleCase(fwInfo.Vendor.ToLowerInvariant())} {fwInfo.Product}"; + + AddSensors(UsbApi.GetOptionalCommands(stream), UsbApi.GetCriticals(stream), settings); + } + + public override HardwareType HardwareType => HardwareType.Psu; + + public override IDictionary Properties + { + get + { + SortedDictionary properties = new(); + + using HidStream stream = _device.Open(); + + float? mode = UsbApi.GetValue(stream, UsbApi.Command.OCPMODE, 0); + if (mode.HasValue) + properties.Add("Over-current protection", mode > 1.0 ? "multi-rail" : "single-rail"); + + return properties; + } + } + + public override void Update() + { + using HidStream stream = _device.Open(); + _sensors.ForEach(s => s.Update(stream)); + } + + private void AddSensors(UsbApi.OptionalCommands optionalCommands, UsbApi.Criticals criticals, ISettings settings) + { + SensorIndices indices = new(); + _sensors.Add(new PsuSensorWithLimits("VRM", + indices, + SensorType.Temperature, + this, + settings, + UsbApi.Command.TEMP0, + null, + criticals.TempMax[0])); + + _sensors.Add(new PsuSensorWithLimits("Case", + indices, + SensorType.Temperature, + this, + settings, + UsbApi.Command.TEMP1, + null, + criticals.TempMax[1])); + + _sensors.Add(new PsuSensor("Case", indices, SensorType.Fan, this, settings, UsbApi.Command.FAN_RPM)); + + _sensors.Add(new PsuSensor("Input", indices, SensorType.Voltage, this, settings, UsbApi.Command.IN_VOLTS)); + _sensors.Add(new PsuSensorWithLimits("+12V", + indices, + SensorType.Voltage, + this, + settings, + UsbApi.Command.RAIL_VOLTS, + criticals.VoltageMin[(byte)Rail._12V], + criticals.VoltageMax[(byte)Rail._12V])); + + _sensors.Add(new PsuSensorWithLimits("+5V", + indices, + SensorType.Voltage, + this, + settings, + UsbApi.Command.RAIL_VOLTS, + criticals.VoltageMin[(byte)Rail._5V], + criticals.VoltageMax[(byte)Rail._5V], + Rail._5V)); + + _sensors.Add(new PsuSensorWithLimits("+3.3V", + indices, + SensorType.Voltage, + this, + settings, + UsbApi.Command.RAIL_VOLTS, + criticals.VoltageMin[(byte)Rail._3V], + criticals.VoltageMax[(byte)Rail._3V], + Rail._3V)); + + if (optionalCommands.HasFlag(UsbApi.OptionalCommands.InputCurrent)) + { + _sensors.Add(new PsuSensor("Input", indices, SensorType.Current, this, settings, UsbApi.Command.IN_AMPS)); + } + + _sensors.Add(new PsuSensorWithLimits("+12V", + indices, + SensorType.Current, + this, + settings, + UsbApi.Command.RAIL_AMPS, + null, + criticals.CurrentMax[(byte)Rail._12V])); + + _sensors.Add(new PsuSensorWithLimits("+5V", + indices, + SensorType.Current, + this, + settings, + UsbApi.Command.RAIL_AMPS, + null, + criticals.CurrentMax[(byte)Rail._5V], + Rail._5V)); + + _sensors.Add(new PsuSensorWithLimits("+3.3V", + indices, + SensorType.Current, + this, + settings, + UsbApi.Command.RAIL_AMPS, + null, + criticals.CurrentMax[(byte)Rail._3V], + Rail._3V)); + + PsuSensor[] powerRails = + { + new("+12V", indices, SensorType.Power, this, settings, UsbApi.Command.RAIL_WATTS), + new("+5V", indices, SensorType.Power, this, settings, UsbApi.Command.RAIL_WATTS, Rail._5V), + new("+3.3V", indices, SensorType.Power, this, settings, UsbApi.Command.RAIL_WATTS, Rail._3V) + }; + + _sensors.AddRange(powerRails); + _sensors.Add(new PsuSensor("Total watts", indices, SensorType.Power, this, settings, UsbApi.Command.TOTAL_WATTS)); + _compositeSensors.Add(new CompositeSensor("Total Output", + indices.NextIndex(SensorType.Power), + SensorType.Power, + this, + settings, + powerRails, + (acc, sensor) => acc + sensor.Value ?? 0f)); + + ActivateSensor(_compositeSensors[_compositeSensors.Count - 1]); + + _sensors.Add(new PsuSensor("Uptime", indices, SensorType.TimeSpan, this, settings, UsbApi.Command.UPTIME, Rail._12V, true)); + _sensors.Add(new PsuSensor("Total uptime", indices, SensorType.TimeSpan, this, settings, UsbApi.Command.TOTAL_UPTIME, Rail._12V, true)); + } + + private class PsuSensor : Sensor + { + private readonly UsbApi.Command _cmd; + private readonly byte _rail; + + public PsuSensor(string name, SensorIndices indices, SensorType type, CorsairPsu hardware, ISettings settings, UsbApi.Command cmd, Rail rail = Rail._12V, bool noHistory = false) + : base(name, indices.NextIndex(type), false, type, hardware, null, settings, noHistory) + { + _cmd = cmd; + _rail = (byte)rail; + + hardware.ActivateSensor(this); + } + + public void Update(HidStream stream) + { + Value = UsbApi.GetValue(stream, _cmd, _rail); + } + } + + private class PsuSensorWithLimits : PsuSensor, ICriticalSensorLimits + { + public PsuSensorWithLimits + ( + string name, + SensorIndices indices, + SensorType type, + CorsairPsu hardware, + ISettings settings, + UsbApi.Command cmd, + float? lowCritical, + float? highCritical, + Rail rail = Rail._12V) + : base(name, indices, type, hardware, settings, cmd, rail) + { + CriticalLowLimit = lowCritical; + CriticalHighLimit = highCritical; + } + + public float? CriticalHighLimit { get; } + + public float? CriticalLowLimit { get; } + } + + private enum Rail : byte + { + _12V = 0, + _5V = 1, + _3V = 2 + } + } + + + #region Exception classes + + public class CommunicationProtocolError : ApplicationException + { + public CommunicationProtocolError(HidDevice device, string message) + : base($"Error communicating with the PSU controller at {device.DevicePath}: {message}") + { } + } + + #endregion + + + #region PSU USB communication protocol implementation + + internal static class UsbApi + { + /* some values are SMBus LINEAR11 data which need a conversion */ +#if false + static int Linear11ToInt(ushort val, int scale) + { + int exp = ((short)val) >> 11; + int mant = (((short)(val & 0x7ff)) << 5) >> 5; + int result = mant * scale; + + return (exp >= 0) ? (result << exp) : (result >> -exp); + } +#endif + static float Linear11ToFloat32(ushort val) + { + int exp = ((short)val) >> 11; + int mant = (((short)(val & 0x7ff)) << 5) >> 5; + return mant * (float)Math.Pow(2, exp); + } + + static bool SendCommand(HidStream stream, byte length, Command cmd, byte arg, out byte[] replyData) + { + /* + * Corsair protocol for PSUs + * + * message size = 64 bytes (request and response, little endian) + * request: + * [length][command][param0][param1][paramX]... + * reply: + * [echo of length][echo of command][data0][data1][dataX]... + * + * - commands are byte sized opcodes + * - length is the sum of all bytes of the commands/params + * - the micro-controller of most of these PSUs support concatenation in the request and reply, + * but it is better to not rely on this (it is also hard to parse) + * - the driver uses raw events to be accessible from userspace (though this is not really + * supported, it is just there for convenience, may be removed in the future) + * - a reply always start with the length and command in the same order the request used it + * - length of the reply data is specific to the command used + * - some of the commands work on a rail and can be switched to a specific rail (0 = 12v, + * 1 = 5v, 2 = 3.3v) + * - the format of the init command 0xFE is swapped length/command bytes + * - parameter bytes amount and values are specific to the command (rail setting is the only + * for now that uses non-zero values) + * - there are much more commands, especially for configuring the device, but they are not + * supported because a wrong command/length can lockup the micro-controller + * - the driver supports debugfs for values not fitting into the hwmon class + * - not every device class (HXi, RMi or AXi) supports all commands + * - it is a pure sensors reading driver (will not support configuring) + */ + + const int cmdBufferSize = 64; + const int replySize = 16; + + byte[] cmdBuffer = new byte[cmdBufferSize + 1]; + cmdBuffer[0] = 0; // report id + cmdBuffer[1] = length; + cmdBuffer[2] = (byte)cmd; + cmdBuffer[3] = arg; + + stream.Write(cmdBuffer); + byte[] reply = stream.Read(); + replyData = new byte[replySize]; + Array.Copy(reply, 3, replyData, 0, replySize); + + return reply[1] == cmdBuffer[1] && reply[2] == cmdBuffer[2]; + } + + public static void Init(HidStream stream) + { + /* + * PSU_CMD_INIT uses swapped length/command and expects 2 parameter bytes, this command + * actually generates a reply, but we don't need it + */ + SendCommand(stream, (byte)Command.INIT, (Command)3, 0, out _); + } + + public struct FirmwareInfo + { + public string Vendor; + public string Product; + } + + public static FirmwareInfo FwInfo(HidStream stream) + { + if (!SendCommand(stream, 3, Command.VEND_STR, 0, out byte[] vendorArr)) + throw new CommunicationProtocolError(stream.Device, "Can't read vendor string"); + + if (!SendCommand(stream, 3, Command.PROD_STR, 0, out byte[] productArr)) + throw new CommunicationProtocolError(stream.Device, "Can't read product"); + + + string ArrayToString(byte[] ar) + { + int i = 0; + while (ar[i] != 0) + i++; + + byte[] trimmed = new byte[i]; + for (int j = 0; j < i; j++) + trimmed[j] = ar[j]; + + return Encoding.ASCII.GetString(trimmed); + } + + + return new FirmwareInfo { Vendor = ArrayToString(vendorArr), Product = ArrayToString(productArr) }; + } + + static bool Request(HidStream stream, Command cmd, byte rail, out byte[] data) + { + //mutex_lock(&priv->lock) ; + switch (cmd) + { + case Command.RAIL_VOLTS_HCRIT: + case Command.RAIL_VOLTS_LCRIT: + case Command.RAIL_AMPS_HCRIT: + case Command.RAIL_VOLTS: + case Command.RAIL_AMPS: + case Command.RAIL_WATTS: + { + if (!SendCommand(stream, 2, Command.SELECT_RAIL, rail, out _)) + { + data = null; + return false; + } + + break; + } + } + + return SendCommand(stream, 3, cmd, 0, out data); + + + // mutex_unlock(&priv->lock) ; + // return ret; + } + + public static float? GetValue(HidStream stream, Command cmd, byte rail) + { + if (!Request(stream, cmd, rail, out byte[] data)) + { + return null; + } + + /* + * the biggest value here comes from the uptime command and to exceed MAXINT total uptime + * needs to be about 68 years, the rest are u16 values and the biggest value coming out of + * the LINEAR11 conversion are the watts values which are about 1200 for the strongest psu + * supported (HX1200i) + */ + int tmp = BitConverter.ToInt32(data, 0); // ((int)data[3] << 24) + (data[2] << 16) + (data[1] << 8) + data[0]; + switch (cmd) + { + case Command.RAIL_VOLTS_HCRIT: + case Command.RAIL_VOLTS_LCRIT: + case Command.RAIL_AMPS_HCRIT: + case Command.TEMP_HCRIT: + case Command.IN_VOLTS: + case Command.IN_AMPS: + case Command.RAIL_VOLTS: + case Command.RAIL_AMPS: + case Command.TEMP0: + case Command.TEMP1: + case Command.FAN_RPM: + case Command.RAIL_WATTS: + case Command.TOTAL_WATTS: + return Linear11ToFloat32((ushort)tmp); // Linear11ToInt((ushort)tmp, 1000000); + case Command.TOTAL_UPTIME: + case Command.UPTIME: + case Command.OCPMODE: + return tmp; + } + + return null; + } + + public struct Criticals + { + public float?[] TempMax; + + public float?[] VoltageMin; + public float?[] VoltageMax; + + public float?[] CurrentMax; + } + + public static Criticals GetCriticals(HidStream stream) + { + Criticals res = new(); + const byte tempCount = 2; + res.TempMax = new float?[tempCount]; + + for (byte rail = 0; rail < tempCount; rail++) + { + res.TempMax[rail] = GetValue(stream, Command.TEMP_HCRIT, rail); + } + + const byte railCount = 3; /* 3v + 5v + 12v */ + res.VoltageMin = new float?[railCount]; + res.VoltageMax = new float?[railCount]; + res.CurrentMax = new float?[railCount]; + + for (byte rail = 0; rail < railCount; rail++) + { + res.VoltageMax[rail] = GetValue(stream, Command.RAIL_VOLTS_HCRIT, rail); + res.VoltageMin[rail] = GetValue(stream, Command.RAIL_VOLTS_LCRIT, rail); + res.CurrentMax[rail] = GetValue(stream, Command.RAIL_AMPS_HCRIT, rail); + } + + return res; + } + + [Flags] + public enum OptionalCommands + { + None = 0x0, + InputCurrent = 0x1 + } + + public static OptionalCommands GetOptionalCommands(HidStream stream) + { + OptionalCommands res = OptionalCommands.None; + if (GetValue(stream, Command.IN_AMPS, 0).HasValue) + { + res |= OptionalCommands.InputCurrent; + } + + return res; + } + + public enum Command : byte + { + SELECT_RAIL = 0x00, /* expects length 2 */ + RAIL_VOLTS_HCRIT = 0x40, /* the rest of the commands expect length 3 */ + RAIL_VOLTS_LCRIT = 0x44, + RAIL_AMPS_HCRIT = 0x46, + TEMP_HCRIT = 0x4F, + IN_VOLTS = 0x88, + IN_AMPS = 0x89, + RAIL_VOLTS = 0x8B, + RAIL_AMPS = 0x8C, + TEMP0 = 0x8D, + TEMP1 = 0x8E, + FAN_RPM = 0x90, + RAIL_WATTS = 0x96, + VEND_STR = 0x99, + PROD_STR = 0x9A, + TOTAL_UPTIME = 0xD1, + UPTIME = 0xD2, + OCPMODE = 0xD8, + TOTAL_WATTS = 0xEE, + INIT = 0xFE + } + } + + #endregion +} diff --git a/LibreHardwareMonitorLib/Hardware/Psu/Corsair/CorsairPsuGroup.cs b/LibreHardwareMonitorLib/Hardware/Psu/Corsair/CorsairPsuGroup.cs new file mode 100644 index 0000000..871c062 --- /dev/null +++ b/LibreHardwareMonitorLib/Hardware/Psu/Corsair/CorsairPsuGroup.cs @@ -0,0 +1,71 @@ +// This Source Code Form is subject to the terms of the GNU Public License, v. 2.0. +// Copyright(C) 2020 Wilken Gottwalt +// Copyright (C) LibreHardwareMonitor and Contributors. +// All Rights Reserved. +// Implemented after the Linuix kernel driver corsair_psu by Wilken Gottwalt and contributers + +using System.Collections.Generic; +using System.Linq; +using System.Text; +using HidSharp; + +namespace LibreHardwareMonitor.Hardware.Psu.Corsair +{ + public class CorsairPsuGroup : IGroup + { + private static readonly int[] _productIds = + { + 0x1c03, // HX550i + 0x1c04, // HX650i + 0x1c05, // HX750i + 0x1c06, // HX850i + 0x1c07, // HX1000i + 0x1c08, // HX1200i + 0x1c09, // RM550i + 0x1c0a, // RM650i + 0x1c0b, // RM750i + 0x1c0c, // RM850i + 0x1c0d, // RM1000i + // 0x1c11, // AX1600i + }; + + private static readonly ushort _vendorId = 0x1b1c; + private readonly List _hardware; + private readonly StringBuilder _report; + + public CorsairPsuGroup(ISettings settings) + { + _report = new StringBuilder(); + _report.AppendLine("Corsair HXi/RMi series PSU Hardware"); + _report.AppendLine(); + + _hardware = new List(); + foreach (HidDevice dev in DeviceList.Local.GetHidDevices(_vendorId)) + { + if (_productIds.Contains(dev.ProductID)) + { + var device = new CorsairPsu(dev, settings, _hardware.Count); + _hardware.Add(device); + _report.AppendLine($"Device name: {device.Name}"); + _report.AppendLine(); + } + } + } + + public IReadOnlyList Hardware => _hardware; + + public void Close() + { + foreach (IHardware iHardware in _hardware) + { + if (iHardware is Hardware hardware) + hardware.Close(); + } + } + + public string GetReport() + { + return _report.ToString(); + } + } +} diff --git a/LibreHardwareMonitorLib/Hardware/Sensor.cs b/LibreHardwareMonitorLib/Hardware/Sensor.cs index e3f8a66..3128e4a 100644 --- a/LibreHardwareMonitorLib/Hardware/Sensor.cs +++ b/LibreHardwareMonitorLib/Hardware/Sensor.cs @@ -17,7 +17,8 @@ namespace LibreHardwareMonitor.Hardware private readonly string _defaultName; private readonly Hardware _hardware; private readonly ISettings _settings; - private readonly List _values = new List(); + private readonly bool _trackMinMax; + private readonly List _values = new(); private int _count; private float? _currentValue; private string _name; @@ -32,7 +33,16 @@ namespace LibreHardwareMonitor.Hardware this(name, index, false, sensorType, hardware, parameterDescriptions, settings) { } - public Sensor(string name, int index, bool defaultHidden, SensorType sensorType, Hardware hardware, ParameterDescription[] parameterDescriptions, ISettings settings) + public Sensor + ( + string name, + int index, + bool defaultHidden, + SensorType sensorType, + Hardware hardware, + ParameterDescription[] parameterDescriptions, + ISettings settings, + bool disableHistory = false) { Index = index; IsDefaultHidden = defaultHidden; @@ -51,6 +61,11 @@ namespace LibreHardwareMonitor.Hardware _settings = settings; _defaultName = name; _name = settings.GetValue(new Identifier(Identifier, "name").ToString(), name); + _trackMinMax = !disableHistory; + if (disableHistory) + { + _valuesTimeWindow = TimeSpan.Zero; + } GetSensorValuesFromSettings(); @@ -92,7 +107,7 @@ namespace LibreHardwareMonitor.Hardware public SensorType SensorType { get; } - public float? Value + public virtual float? Value { get { return _currentValue; } set @@ -117,11 +132,14 @@ namespace LibreHardwareMonitor.Hardware } _currentValue = value; - if (Min > value || !Min.HasValue) - Min = value; + if (_trackMinMax) + { + if (!Min.HasValue || Min > value) + Min = value; - if (Max < value || !Max.HasValue) - Max = value; + if (!Max.HasValue || Max < value) + Max = value; + } } } @@ -168,31 +186,24 @@ namespace LibreHardwareMonitor.Hardware private void SetSensorValuesToSettings() { - using (MemoryStream memoryStream = new MemoryStream()) + using MemoryStream memoryStream = new(); + using GZipStream gZipStream = new(memoryStream, CompressionMode.Compress); + using BufferedStream outputStream = new(gZipStream, 65536); + using BinaryWriter binaryWriter = new(outputStream); + + long t = 0; + + foreach (SensorValue sensorValue in _values) { - using (GZipStream gZipStream = new GZipStream(memoryStream, CompressionMode.Compress)) - { - using (BufferedStream outputStream = new BufferedStream(gZipStream, 65536)) - { - using (BinaryWriter binaryWriter = new BinaryWriter(outputStream)) - { - long t = 0; - - foreach (SensorValue sensorValue in _values) - { - long v = sensorValue.Time.ToBinary(); - binaryWriter.Write(v - t); - t = v; - binaryWriter.Write(sensorValue.Value); - } - - binaryWriter.Flush(); - } - } - } - - _settings.SetValue(new Identifier(Identifier, "values").ToString(), Convert.ToBase64String(memoryStream.ToArray())); + long v = sensorValue.Time.ToBinary(); + binaryWriter.Write(v - t); + t = v; + binaryWriter.Write(sensorValue.Value); } + + binaryWriter.Flush(); + + _settings.SetValue(new Identifier(Identifier, "values").ToString(), Convert.ToBase64String(memoryStream.ToArray())); } private void GetSensorValuesFromSettings() @@ -207,40 +218,33 @@ namespace LibreHardwareMonitor.Hardware byte[] array = Convert.FromBase64String(s); DateTime now = DateTime.UtcNow; - using (MemoryStream memoryStream = new MemoryStream(array)) + using MemoryStream memoryStream = new(array); + using GZipStream gZipStream = new(memoryStream, CompressionMode.Decompress); + using MemoryStream destination = new(); + + gZipStream.CopyTo(destination); + destination.Seek(0, SeekOrigin.Begin); + + using BinaryReader reader = new(destination); + try { - using (GZipStream gZipStream = new GZipStream(memoryStream, CompressionMode.Decompress)) + long t = 0; + long readLen = reader.BaseStream.Length - reader.BaseStream.Position; + while (readLen > 0) { - using (MemoryStream destination = new MemoryStream()) - { - gZipStream.CopyTo(destination); - destination.Seek(0, SeekOrigin.Begin); - - using (BinaryReader reader = new BinaryReader(destination)) - { - try - { - long t = 0; - long readLen = reader.BaseStream.Length - reader.BaseStream.Position; - while (readLen > 0) - { - t += reader.ReadInt64(); - DateTime time = DateTime.FromBinary(t); - if (time > now) - break; + t += reader.ReadInt64(); + DateTime time = DateTime.FromBinary(t); + if (time > now) + break; - float value = reader.ReadSingle(); - AppendValue(value, time); - readLen = reader.BaseStream.Length - reader.BaseStream.Position; - } - } - catch (EndOfStreamException) - { } - } - } + float value = reader.ReadSingle(); + AppendValue(value, time); + readLen = reader.BaseStream.Length - reader.BaseStream.Position; } } + catch (EndOfStreamException) + { } } catch {