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
{