Add support for battery monitoring (#612)

Thanks to @EMN-CSharp.
This commit is contained in:
EMN-CSharp
2022-02-07 17:05:36 +01:00
committed by GitHub
parent 0ed564e7c6
commit 5152b435fc
19 changed files with 844 additions and 149 deletions
@@ -0,0 +1,195 @@
// 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;
using System.Runtime.InteropServices;
using LibreHardwareMonitor.Interop;
using Microsoft.Win32.SafeHandles;
namespace LibreHardwareMonitor.Hardware.Battery
{
internal sealed class Battery : Hardware
{
private readonly SafeFileHandle _batteryHandle;
private readonly Kernel32.BATTERY_INFORMATION _batteryInformation;
private readonly uint _batteryTag;
private readonly Sensor _chargeDischargeCurrent;
private readonly Sensor _chargeDischargeRate;
private readonly Sensor _chargeLevel;
private readonly Sensor _degradationPercentage;
private readonly Sensor _designedCapacity;
private readonly Sensor _fullChargedCapacity;
private readonly Sensor _remainingCapacity;
private readonly Sensor _voltage;
public Battery
(
string name,
string manufacturer,
SafeFileHandle batteryHandle,
Kernel32.BATTERY_INFORMATION batteryInfo,
uint batteryTag,
ISettings settings) :
base(name, new Identifier("battery"), settings)
{
Name = name;
Manufacturer = manufacturer;
_batteryTag = batteryTag;
_batteryHandle = batteryHandle;
_batteryInformation = batteryInfo;
if (batteryInfo.Chemistry.SequenceEqual(new[] { 'P', 'b', 'A', 'c' }))
{
Chemistry = BatteryChemistry.LeadAcid;
}
else if (batteryInfo.Chemistry.SequenceEqual(new[] { 'L', 'I', 'O', 'N' }) || batteryInfo.Chemistry.SequenceEqual(new[] { 'L', 'i', '-', 'I' }))
{
Chemistry = BatteryChemistry.LithiumIon;
}
else if (batteryInfo.Chemistry.SequenceEqual(new[] { 'N', 'i', 'C', 'd' }))
{
Chemistry = BatteryChemistry.NickelCadmium;
}
else if (batteryInfo.Chemistry.SequenceEqual(new[] { 'N', 'i', 'M', 'H' }))
{
Chemistry = BatteryChemistry.NickelMetalHydride;
}
else if (batteryInfo.Chemistry.SequenceEqual(new[] { 'N', 'i', 'Z', 'n' }))
{
Chemistry = BatteryChemistry.NickelZinc;
}
else if (batteryInfo.Chemistry.SequenceEqual(new[] { 'R', 'A', 'M', '\x00' }))
{
Chemistry = BatteryChemistry.AlkalineManganese;
}
else
{
Chemistry = BatteryChemistry.Unknown;
}
DegradationLevel = 100f - (batteryInfo.FullChargedCapacity * 100f / batteryInfo.DesignedCapacity);
DesignedCapacity = batteryInfo.DesignedCapacity;
FullChargedCapacity = batteryInfo.FullChargedCapacity;
_chargeLevel = new Sensor("Charge Level", 0, SensorType.Level, this, settings);
ActivateSensor(_chargeLevel);
_voltage = new Sensor("Voltage", 1, SensorType.Voltage, this, settings);
ActivateSensor(_voltage);
_chargeDischargeCurrent = new Sensor("Current", 2, SensorType.Current, this, settings);
ActivateSensor(_chargeDischargeCurrent);
_designedCapacity = new Sensor("Designed Capacity", 3, SensorType.Energy, this, settings);
ActivateSensor(_designedCapacity);
_fullChargedCapacity = new Sensor("Full Charged Capacity", 4, SensorType.Energy, this, settings);
ActivateSensor(_fullChargedCapacity);
_remainingCapacity = new Sensor("Remaining Capacity", 5, SensorType.Energy, this, settings);
ActivateSensor(_remainingCapacity);
_chargeDischargeRate = new Sensor("Charge/Discharge Rate", 0, SensorType.Power, this, settings);
ActivateSensor(_chargeDischargeRate);
_degradationPercentage = new Sensor("Degradation Level", 0, SensorType.Level, this, settings);
ActivateSensor(_degradationPercentage);
}
public float ChargeDischargeCurrent { get; private set; }
public float ChargeDischargeRate { get; private set; }
public float ChargeLevel { get; private set; }
public BatteryChemistry Chemistry { get; }
public float DegradationLevel { get; }
public float DesignedCapacity { get; }
public float FullChargedCapacity { get; }
public override HardwareType HardwareType => HardwareType.Battery;
public string Manufacturer { get; }
public float RemainingCapacity { get; private set; }
public float Voltage { get; private set; }
public override void Update()
{
Kernel32.BATTERY_WAIT_STATUS bws = default;
bws.BatteryTag = _batteryTag;
Kernel32.BATTERY_STATUS batteryStatus = default;
if (Kernel32.DeviceIoControl(_batteryHandle,
Kernel32.IOCTL.IOCTL_BATTERY_QUERY_STATUS,
ref bws,
Marshal.SizeOf(bws),
ref batteryStatus,
Marshal.SizeOf(batteryStatus),
out _,
IntPtr.Zero))
{
_designedCapacity.Value = Convert.ToSingle(_batteryInformation.DesignedCapacity);
_fullChargedCapacity.Value = Convert.ToSingle(_batteryInformation.FullChargedCapacity);
_remainingCapacity.Value = Convert.ToSingle(batteryStatus.Capacity);
RemainingCapacity = Convert.ToSingle(batteryStatus.Capacity);
_voltage.Value = Convert.ToSingle(batteryStatus.Voltage) / 1000f;
Voltage = Convert.ToSingle(batteryStatus.Voltage) / 1000f;
_chargeLevel.Value = _remainingCapacity.Value * 100f / _fullChargedCapacity.Value;
ChargeLevel = (_remainingCapacity.Value * 100f / _fullChargedCapacity.Value).GetValueOrDefault();
ChargeDischargeRate = batteryStatus.Rate / 1000f;
switch (batteryStatus.Rate)
{
case > 0:
_chargeDischargeRate.Name = "Charge Rate";
_chargeDischargeRate.Value = batteryStatus.Rate / 1000f;
_chargeDischargeCurrent.Name = "Charge Current";
_chargeDischargeCurrent.Value = _chargeDischargeRate.Value / _voltage.Value;
ChargeDischargeCurrent = (_chargeDischargeRate.Value / _voltage.Value).GetValueOrDefault();
break;
case < 0:
_chargeDischargeRate.Name = "Discharge Rate";
_chargeDischargeRate.Value = Math.Abs(batteryStatus.Rate) / 1000f;
_chargeDischargeCurrent.Name = "Discharge Current";
_chargeDischargeCurrent.Value = _chargeDischargeRate.Value / _voltage.Value;
ChargeDischargeCurrent = (_chargeDischargeRate.Value / _voltage.Value).GetValueOrDefault();
break;
default:
_chargeDischargeRate.Name = "Charge/Discharge Rate";
_chargeDischargeRate.Value = 0f;
ChargeDischargeRate = 0f;
_chargeDischargeCurrent.Name = "Charge/Discharge Current";
_chargeDischargeCurrent.Value = 0f;
ChargeDischargeCurrent = 0f;
break;
}
_degradationPercentage.Value = 100f - (_fullChargedCapacity.Value * 100f / _designedCapacity.Value);
}
}
public override void Close()
{
base.Close();
_batteryHandle.Close();
}
}
}
@@ -0,0 +1,17 @@
// 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.Battery;
internal enum BatteryChemistry
{
Unknown,
LeadAcid,
NickelCadmium,
NickelMetalHydride,
LithiumIon,
NickelZinc,
AlkalineManganese
}
@@ -0,0 +1,227 @@
// 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.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using LibreHardwareMonitor.Interop;
using Microsoft.Win32.SafeHandles;
namespace LibreHardwareMonitor.Hardware.Battery
{
internal class BatteryGroup : IGroup
{
private readonly List<Battery> _hardware = new();
public unsafe BatteryGroup(ISettings settings)
{
// No implementation for battery information on Unix systems
if (Software.OperatingSystem.IsUnix)
return;
IntPtr hdev = SetupApi.SetupDiGetClassDevs(ref SetupApi.GUID_DEVICE_BATTERY, IntPtr.Zero, IntPtr.Zero, SetupApi.DIGCF_PRESENT | SetupApi.DIGCF_DEVICEINTERFACE);
if (hdev != SetupApi.INVALID_HANDLE_VALUE)
{
for (uint i = 0;; i++)
{
SetupApi.SP_DEVICE_INTERFACE_DATA did = default;
did.cbSize = (uint)Marshal.SizeOf(typeof(SetupApi.SP_DEVICE_INTERFACE_DATA));
if (!SetupApi.SetupDiEnumDeviceInterfaces(hdev,
IntPtr.Zero,
ref SetupApi.GUID_DEVICE_BATTERY,
i,
ref did))
{
if (Marshal.GetLastWin32Error() == SetupApi.ERROR_NO_MORE_ITEMS)
break;
}
else
{
SetupApi.SetupDiGetDeviceInterfaceDetail(hdev,
did,
IntPtr.Zero,
0,
out uint cbRequired,
IntPtr.Zero);
if (Marshal.GetLastWin32Error() == SetupApi.ERROR_INSUFFICIENT_BUFFER)
{
IntPtr pdidd = Kernel32.LocalAlloc(Kernel32.LPTR, cbRequired);
Marshal.WriteInt32(pdidd, Environment.Is64BitOperatingSystem ? 8 : 4); // cbSize.
if (SetupApi.SetupDiGetDeviceInterfaceDetail(hdev,
did,
pdidd,
cbRequired,
out _,
IntPtr.Zero))
{
string devicePath = new((char*)(pdidd + 4));
SafeFileHandle battery = Kernel32.CreateFile(devicePath, FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero);
if (!battery.IsInvalid)
{
Kernel32.BATTERY_QUERY_INFORMATION bqi = default;
uint dwWait = 0;
if (Kernel32.DeviceIoControl(battery,
Kernel32.IOCTL.IOCTL_BATTERY_QUERY_TAG,
ref dwWait,
Marshal.SizeOf(dwWait),
ref bqi.BatteryTag,
Marshal.SizeOf(bqi.BatteryTag),
out _,
IntPtr.Zero))
{
Kernel32.BATTERY_INFORMATION bi = default;
bqi.InformationLevel = Kernel32.BATTERY_QUERY_INFORMATION_LEVEL.BatteryInformation;
if (Kernel32.DeviceIoControl(battery,
Kernel32.IOCTL.IOCTL_BATTERY_QUERY_INFORMATION,
ref bqi,
Marshal.SizeOf(bqi),
ref bi,
Marshal.SizeOf(bi),
out _,
IntPtr.Zero))
{
// Only batteries count.
if (bi.Capabilities == Kernel32.BatteryCapabilities.BATTERY_SYSTEM_BATTERY)
{
const int maxLoadString = 100;
IntPtr ptrDevName = Marshal.AllocCoTaskMem(maxLoadString);
bqi.InformationLevel = Kernel32.BATTERY_QUERY_INFORMATION_LEVEL.BatteryDeviceName;
if (Kernel32.DeviceIoControl(battery,
Kernel32.IOCTL.IOCTL_BATTERY_QUERY_INFORMATION,
ref bqi,
Marshal.SizeOf(bqi),
ptrDevName,
maxLoadString,
out _,
IntPtr.Zero))
{
IntPtr ptrManName = Marshal.AllocCoTaskMem(maxLoadString);
bqi.InformationLevel = Kernel32.BATTERY_QUERY_INFORMATION_LEVEL.BatteryManufactureName;
if (Kernel32.DeviceIoControl(battery,
Kernel32.IOCTL.IOCTL_BATTERY_QUERY_INFORMATION,
ref bqi,
Marshal.SizeOf(bqi),
ptrManName,
maxLoadString,
out _,
IntPtr.Zero))
{
string name = Marshal.PtrToStringUni(ptrDevName);
string manufacturer = Marshal.PtrToStringUni(ptrManName);
_hardware.Add(new Battery(name, manufacturer, battery, bi, bqi.BatteryTag, settings));
}
Marshal.FreeCoTaskMem(ptrManName);
}
Marshal.FreeCoTaskMem(ptrDevName);
}
}
}
}
}
Kernel32.LocalFree(pdidd);
}
}
}
SetupApi.SetupDiDestroyDeviceInfoList(hdev);
}
}
/// <inheritdoc />
public IReadOnlyList<IHardware> Hardware => _hardware;
/// <inheritdoc />
public void Close()
{
foreach (Battery battery in _hardware)
battery.Close();
}
/// <inheritdoc />
public string GetReport()
{
StringBuilder reportBuilder = new();
uint count = 1;
foreach (Battery bat in _hardware)
{
string chemistry = "Unknown";
switch (bat.Chemistry)
{
case BatteryChemistry.LeadAcid:
chemistry = "Lead Acid";
break;
case BatteryChemistry.NickelCadmium:
chemistry = "Nickel-Cadmium";
break;
case BatteryChemistry.NickelMetalHydride:
chemistry = "Nickel-Metal Hydride";
break;
case BatteryChemistry.LithiumIon:
chemistry = "Lithium Ion";
break;
case BatteryChemistry.NickelZinc:
chemistry = "Nickel-Zinc";
break;
case BatteryChemistry.AlkalineManganese:
chemistry = "Rechargeable Alkaline-Manganese";
break;
}
reportBuilder.Append("Battery #").Append(count).AppendLine(":")
.Append(" Name: ").AppendLine(bat.Name)
.Append(" Manufacturer: ").AppendLine(bat.Manufacturer)
.Append(" Chemistry: ").AppendLine(chemistry)
.Append(" Degradation Level: ").AppendFormat("{0:F2}", bat.DegradationLevel).AppendLine(" %")
.Append(" Designed Capacity: ").Append(bat.DesignedCapacity).AppendLine(" mWh")
.Append(" Full Charged Capacity: ").Append(bat.FullChargedCapacity).AppendLine(" mWh")
.Append(" Remaining Capacity: ").Append(bat.RemainingCapacity).AppendLine(" mWh")
.Append(" Charge Level: ").AppendFormat("{0:F2}", bat.RemainingCapacity * 100f / bat.FullChargedCapacity).AppendLine(" %")
.Append(" Voltage: ").AppendFormat("{0:F3}", bat.Voltage).AppendLine(" V");
switch (bat.ChargeDischargeRate)
{
case > 0:
reportBuilder.Append(" Charge Rate: ").AppendFormat("{0:F1}", bat.ChargeDischargeRate).AppendLine(" W")
.Append(" Charge Current: ").AppendFormat("{0:F3}", bat.ChargeDischargeRate / bat.Voltage).AppendLine(" A");
break;
case < 0:
reportBuilder.Append(" Discharge Rate: ").AppendFormat("{0:F1}", Math.Abs(bat.ChargeDischargeRate)).AppendLine(" W")
.Append(" Discharge Current: ").AppendFormat("{0:F3}", Math.Abs(bat.ChargeDischargeRate) / bat.Voltage).AppendLine(" A");
break;
default:
reportBuilder.AppendLine(" Charge/Discharge Rate: 0 W")
.AppendLine(" Charge/Discharge Current: 0 A");
break;
}
reportBuilder.AppendLine();
count++;
}
return reportBuilder.ToString();
}
}
}
+87 -68
View File
@@ -8,7 +8,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Security.Permissions;
using LibreHardwareMonitor.Hardware.Battery;
using LibreHardwareMonitor.Hardware.Controller.AeroCool;
using LibreHardwareMonitor.Hardware.Controller.AquaComputer;
using LibreHardwareMonitor.Hardware.Controller.Heatmaster;
@@ -28,16 +28,11 @@ namespace LibreHardwareMonitor.Hardware
/// </summary>
public class Computer : IComputer
{
/// <inheritdoc />
public event HardwareEventHandler HardwareAdded;
/// <inheritdoc />
public event HardwareEventHandler HardwareRemoved;
private readonly object _lock = new object();
private readonly List<IGroup> _groups = new List<IGroup>();
private readonly List<IGroup> _groups = new();
private readonly object _lock = new();
private readonly ISettings _settings;
private bool _batteryEnabled;
private bool _controllerEnabled;
private bool _cpuEnabled;
private bool _gpuEnabled;
@@ -45,13 +40,12 @@ namespace LibreHardwareMonitor.Hardware
private bool _motherboardEnabled;
private bool _networkEnabled;
private bool _open;
private bool _storageEnabled;
private bool _psuEnabled;
private SMBios _smbios;
private bool _storageEnabled;
/// <summary>
/// Creates a new <see cref="IComputer"/> instance with basic initial <see cref="Settings"/>.
/// Creates a new <see cref="IComputer" /> instance with basic initial <see cref="Settings" />.
/// </summary>
public Computer()
{
@@ -59,14 +53,20 @@ namespace LibreHardwareMonitor.Hardware
}
/// <summary>
/// Creates a new <see cref="IComputer"/> instance with additional <see cref="ISettings"/>.
/// Creates a new <see cref="IComputer" /> instance with additional <see cref="ISettings" />.
/// </summary>
/// <param name="settings">Computer settings that will be transferred to each <see cref="IHardware"/>.</param>
/// <param name="settings">Computer settings that will be transferred to each <see cref="IHardware" />.</param>
public Computer(ISettings settings)
{
_settings = settings ?? new Settings();
}
/// <inheritdoc />
public event HardwareEventHandler HardwareAdded;
/// <inheritdoc />
public event HardwareEventHandler HardwareRemoved;
/// <inheritdoc />
public IList<IHardware> Hardware
{
@@ -74,7 +74,7 @@ namespace LibreHardwareMonitor.Hardware
{
lock (_lock)
{
List<IHardware> list = new List<IHardware>();
List<IHardware> list = new();
foreach (IGroup group in _groups)
list.AddRange(group.Hardware);
@@ -84,35 +84,25 @@ namespace LibreHardwareMonitor.Hardware
}
}
/// <summary>
/// Contains computer information table read in accordance with <see href="https://www.dmtf.org/standards/smbios">System Management BIOS (SMBIOS) Reference Specification</see>.
/// </summary>
public SMBios SMBios
{
get
{
if (!_open)
throw new InvalidOperationException("SMBIOS cannot be accessed before opening.");
return _smbios;
}
}
/// <inheritdoc />
public bool IsCpuEnabled
public bool IsBatteryEnabled
{
get { return _cpuEnabled; }
get { return _batteryEnabled; }
set
{
if (_open && value != _cpuEnabled)
if (_open && value != _batteryEnabled)
{
if (value)
Add(new CPU.CpuGroup(_settings));
{
Add(new BatteryGroup(_settings));
}
else
RemoveType<CPU.CpuGroup>();
{
RemoveType<BatteryGroup>();
}
}
_cpuEnabled = value;
_batteryEnabled = value;
}
}
@@ -146,6 +136,24 @@ namespace LibreHardwareMonitor.Hardware
}
}
/// <inheritdoc />
public bool IsCpuEnabled
{
get { return _cpuEnabled; }
set
{
if (_open && value != _cpuEnabled)
{
if (value)
Add(new CPU.CpuGroup(_settings));
else
RemoveType<CPU.CpuGroup>();
}
_cpuEnabled = value;
}
}
/// <inheritdoc />
public bool IsGpuEnabled
{
@@ -224,6 +232,28 @@ namespace LibreHardwareMonitor.Hardware
}
}
/// <inheritdoc />
public bool IsPsuEnabled
{
get { return _psuEnabled; }
set
{
if (_open && value != _psuEnabled)
{
if (value)
{
Add(new CorsairPsuGroup(_settings));
}
else
{
RemoveType<CorsairPsuGroup>();
}
}
_psuEnabled = value;
}
}
/// <inheritdoc />
public bool IsStorageEnabled
{
@@ -242,24 +272,17 @@ namespace LibreHardwareMonitor.Hardware
}
}
/// <inheritdoc />
public bool IsPsuEnabled
/// <summary>
/// Contains computer information table read in accordance with <see href="https://www.dmtf.org/standards/smbios">System Management BIOS (SMBIOS) Reference Specification</see>.
/// </summary>
public SMBios SMBios
{
get { return _psuEnabled; }
set
get
{
if (_open && value != _psuEnabled)
{
if (value)
{
Add(new CorsairPsuGroup(_settings));
}
else
{
RemoveType<CorsairPsuGroup>();
}
}
_psuEnabled = value;
if (!_open)
throw new InvalidOperationException("SMBIOS cannot be accessed before opening.");
return _smbios;
}
}
@@ -268,7 +291,7 @@ namespace LibreHardwareMonitor.Hardware
{
lock (_lock)
{
using StringWriter w = new StringWriter(CultureInfo.InvariantCulture);
using StringWriter w = new(CultureInfo.InvariantCulture);
w.WriteLine();
w.WriteLine(nameof(LibreHardwareMonitor) + " Report");
@@ -341,7 +364,7 @@ namespace LibreHardwareMonitor.Hardware
}
/// <summary>
/// Triggers the <see cref="IVisitor.VisitComputer"/> method for the given observer.
/// Triggers the <see cref="IVisitor.VisitComputer" /> method for the given observer.
/// </summary>
/// <param name="visitor">Observer who call to devices.</param>
public void Accept(IVisitor visitor)
@@ -349,12 +372,11 @@ namespace LibreHardwareMonitor.Hardware
if (visitor == null)
throw new ArgumentNullException(nameof(visitor));
visitor.VisitComputer(this);
}
/// <summary>
/// Triggers the <see cref="IElement.Accept"/> method with the given visitor for each device in each group.
/// Triggers the <see cref="IElement.Accept" /> method with the given visitor for each device in each group.
/// </summary>
/// <param name="visitor">Observer who call to devices.</param>
public void Traverse(IVisitor visitor)
@@ -387,13 +409,11 @@ namespace LibreHardwareMonitor.Hardware
if (group == null)
return;
lock (_lock)
{
if (_groups.Contains(group))
return;
_groups.Add(group);
if (group is IHardwareChanged hardwareChanged)
@@ -417,7 +437,6 @@ namespace LibreHardwareMonitor.Hardware
if (!_groups.Contains(group))
return;
_groups.Remove(group);
if (group is IHardwareChanged hardwareChanged)
@@ -438,7 +457,7 @@ namespace LibreHardwareMonitor.Hardware
private void RemoveType<T>() where T : IGroup
{
List<T> list = new List<T>();
List<T> list = new();
lock (_lock)
{
@@ -454,14 +473,14 @@ namespace LibreHardwareMonitor.Hardware
}
/// <summary>
/// If hasn't been opened before, opens <see cref="SMBios"/>, <see cref="Ring0"/>, <see cref="OpCode"/> and triggers the private <see cref="AddGroups"/> method depending on which categories are enabled.
/// If hasn't been opened before, opens <see cref="SMBios" />, <see cref="Ring0" />, <see cref="OpCode" /> and triggers the private <see cref="AddGroups" /> method depending on which categories are
/// enabled.
/// </summary>
public void Open()
{
if (_open)
return;
_smbios = new SMBios();
Ring0.Open();
@@ -506,6 +525,9 @@ namespace LibreHardwareMonitor.Hardware
if (_psuEnabled)
Add(new CorsairPsuGroup(_settings));
if (_batteryEnabled)
Add(new BatteryGroup(_settings));
}
private static void NewSection(TextWriter writer)
@@ -523,7 +545,6 @@ namespace LibreHardwareMonitor.Hardware
if (c == 0)
return a.Index.CompareTo(b.Index);
return c;
}
@@ -584,14 +605,13 @@ namespace LibreHardwareMonitor.Hardware
}
/// <summary>
/// If opened before, removes all <see cref="IGroup"/> and triggers <see cref="OpCode.Close"/>, <see cref="InpOut.Close"/> and <see cref="Ring0.Close"/>.
/// If opened before, removes all <see cref="IGroup" /> and triggers <see cref="OpCode.Close" />, <see cref="InpOut.Close" /> and <see cref="Ring0.Close" />.
/// </summary>
public void Close()
{
if (!_open)
return;
lock (_lock)
{
while (_groups.Count > 0)
@@ -610,14 +630,13 @@ namespace LibreHardwareMonitor.Hardware
}
/// <summary>
/// If opened before, removes all <see cref="IGroup"/> and recreates it.
/// If opened before, removes all <see cref="IGroup" /> and recreates it.
/// </summary>
public void Reset()
{
if (!_open)
return;
RemoveGroups();
AddGroups();
}
@@ -635,7 +654,7 @@ namespace LibreHardwareMonitor.Hardware
}
/// <summary>
/// <see cref="Computer"/> specific additional settings passed to its <see cref="IHardware"/>.
/// <see cref="Computer" /> specific additional settings passed to its <see cref="IHardware" />.
/// </summary>
private class Settings : ISettings
{
@@ -21,6 +21,7 @@ namespace LibreHardwareMonitor.Hardware
Network,
Cooler,
EmbeddedController,
Psu
Psu,
Battery
}
}
+72 -56
View File
@@ -20,77 +20,93 @@ namespace LibreHardwareMonitor.Hardware
public interface IComputer : IElement
{
/// <summary>
/// Gets or sets a value indicating whether collecting information about <see cref="HardwareType.Cpu"/> devices should be enabled and updated.
/// Triggered when a new <see cref="IHardware" /> is registered.
/// </summary>
/// <returns><see langword="true"/> if a given category of devices is already enabled.</returns>
bool IsCpuEnabled { get; }
event HardwareEventHandler HardwareAdded;
/// <summary>
/// Gets or sets a value indicating whether collecting information about:
/// <list>
/// <item><see cref="LibreHardwareMonitor.Hardware.Controller.TBalancer.TBalancerGroup"/></item>
/// <item><see cref="LibreHardwareMonitor.Hardware.Controller.Heatmaster.HeatmasterGroup"/></item>
/// <item><see cref="LibreHardwareMonitor.Hardware.Controller.AquaComputer.AquaComputerGroup"/></item>
/// <item><see cref="LibreHardwareMonitor.Hardware.Controller.AeroCool.AeroCoolGroup"/></item>
/// <item><see cref="LibreHardwareMonitor.Hardware.Controller.Nzxt.NzxtGroup"/></item>
/// </list>
/// devices should be enabled and updated.
/// Triggered when a <see cref="IHardware" /> is removed.
/// </summary>
/// <returns><see langword="true"/> if a given category of devices is already enabled.</returns>
bool IsControllerEnabled { get; }
event HardwareEventHandler HardwareRemoved;
/// <summary>
/// Gets or sets a value indicating whether collecting information about <see cref="HardwareType.GpuAmd"/> or <see cref="HardwareType.GpuNvidia"/> devices should be enabled and updated.
/// </summary>
/// <returns><see langword="true"/> if a given category of devices is already enabled.</returns>
bool IsGpuEnabled { get; }
/// <summary>
/// Gets or sets a value indicating whether collecting information about <see cref="HardwareType.Storage"/> devices should be enabled and updated.
/// </summary>
/// <returns><see langword="true"/> if a given category of devices is already enabled.</returns>
bool IsStorageEnabled { get; }
/// <summary>
/// Gets or sets a value indicating whether collecting information about <see cref="HardwareType.Motherboard"/> devices should be enabled and updated.
/// </summary>
/// <returns><see langword="true"/> if a given category of devices is already enabled.</returns>
bool IsMotherboardEnabled { get; }
/// <summary>
/// Gets or sets a value indicating whether collecting information about <see cref="HardwareType.Network"/> devices should be enabled and updated.
/// </summary>
/// <returns><see langword="true"/> if a given category of devices is already enabled.</returns>
bool IsNetworkEnabled { get; }
/// <summary>
/// Gets or sets a value indicating whether collecting information about <see cref="HardwareType.Memory"/> devices should be enabled and updated.
/// </summary>
/// <returns><see langword="true"/> if a given category of devices is already enabled.</returns>
bool IsMemoryEnabled { get; }
/// <summary>
/// Gets or sets a value indicating whether collecting information about <see cref="HardwareType.Psu"/> devices should be enabled and updated.
/// </summary>
/// <returns><see langword="true"/> if a given category of devices is already enabled.</returns>
bool IsPsuEnabled { get; }
/// <summary>
/// Gets a list of all known <see cref="IHardware"/>.
/// <para>Can be updated by <see cref="IVisitor"/>.</para>
/// Gets a list of all known <see cref="IHardware" />.
/// <para>Can be updated by <see cref="IVisitor" />.</para>
/// </summary>
/// <returns>List of all enabled devices.</returns>
IList<IHardware> Hardware { get; }
/// <summary>
/// Triggered when a new <see cref="IHardware"/> is registered.
/// Gets or sets a value indicating whether collecting information about <see cref="HardwareType.Battery" /> devices should be enabled and updated.
/// </summary>
event HardwareEventHandler HardwareAdded;
/// <returns><see langword="true" /> if a given category of devices is already enabled.</returns>
bool IsBatteryEnabled { get; }
/// <summary>
/// Triggered when a <see cref="IHardware"/> is removed.
/// Gets or sets a value indicating whether collecting information about:
/// <list>
/// <item>
/// <see cref="Controller.TBalancer.TBalancerGroup" />
/// </item>
/// <item>
/// <see cref="Controller.Heatmaster.HeatmasterGroup" />
/// </item>
/// <item>
/// <see cref="Controller.AquaComputer.AquaComputerGroup" />
/// </item>
/// <item>
/// <see cref="Controller.AeroCool.AeroCoolGroup" />
/// </item>
/// <item>
/// <see cref="Controller.Nzxt.NzxtGroup" />
/// </item>
/// </list>
/// devices should be enabled and updated.
/// </summary>
event HardwareEventHandler HardwareRemoved;
/// <returns><see langword="true" /> if a given category of devices is already enabled.</returns>
bool IsControllerEnabled { get; }
/// <summary>
/// Gets or sets a value indicating whether collecting information about <see cref="HardwareType.Cpu" /> devices should be enabled and updated.
/// </summary>
/// <returns><see langword="true" /> if a given category of devices is already enabled.</returns>
bool IsCpuEnabled { get; }
/// <summary>
/// Gets or sets a value indicating whether collecting information about <see cref="HardwareType.GpuAmd" /> or <see cref="HardwareType.GpuNvidia" /> devices should be enabled and updated.
/// </summary>
/// <returns><see langword="true" /> if a given category of devices is already enabled.</returns>
bool IsGpuEnabled { get; }
/// <summary>
/// Gets or sets a value indicating whether collecting information about <see cref="HardwareType.Memory" /> devices should be enabled and updated.
/// </summary>
/// <returns><see langword="true" /> if a given category of devices is already enabled.</returns>
bool IsMemoryEnabled { get; }
/// <summary>
/// Gets or sets a value indicating whether collecting information about <see cref="HardwareType.Motherboard" /> devices should be enabled and updated.
/// </summary>
/// <returns><see langword="true" /> if a given category of devices is already enabled.</returns>
bool IsMotherboardEnabled { get; }
/// <summary>
/// Gets or sets a value indicating whether collecting information about <see cref="HardwareType.Network" /> devices should be enabled and updated.
/// </summary>
/// <returns><see langword="true" /> if a given category of devices is already enabled.</returns>
bool IsNetworkEnabled { get; }
/// <summary>
/// Gets or sets a value indicating whether collecting information about <see cref="HardwareType.Psu" /> devices should be enabled and updated.
/// </summary>
/// <returns><see langword="true" /> if a given category of devices is already enabled.</returns>
bool IsPsuEnabled { get; }
/// <summary>
/// Gets or sets a value indicating whether collecting information about <see cref="HardwareType.Storage" /> devices should be enabled and updated.
/// </summary>
/// <returns><see langword="true" /> if a given category of devices is already enabled.</returns>
bool IsStorageEnabled { get; }
/// <summary>
/// Generates full LibreHardwareMonitor report for devices that have been enabled.
@@ -30,6 +30,7 @@ namespace LibreHardwareMonitor.Hardware
SmallData, // MB = 2^20 Bytes
Throughput, // B/s
TimeSpan, // Seconds
Energy // milliwatt-hour (mWh)
}
/// <summary>
+135 -1
View File
@@ -26,6 +26,9 @@ namespace LibreHardwareMonitor.Interop
internal const byte SMART_LBA_MID = 0x4F;
internal const byte SMART_LBA_HI_EXCEEDED = 0x2C;
internal const byte SMART_LBA_MID_EXCEEDED = 0xF4;
internal const uint LPTR = 0x0000 | 0x0040;
private const string DllName = "kernel32.dll";
[Flags]
@@ -203,6 +206,64 @@ namespace LibreHardwareMonitor.Interop
out uint lpBytesReturned,
IntPtr lpOverlapped);
[DllImport(DllName, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DeviceIoControl
(
SafeFileHandle hDevice,
IOCTL dwIoControlCode,
ref BATTERY_QUERY_INFORMATION lpInBuffer,
int nInBufferSize,
ref BATTERY_INFORMATION lpOutBuffer,
int nOutBufferSize,
out uint lpBytesReturned,
IntPtr lpOverlapped);
[DllImport(DllName, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DeviceIoControl
(
SafeFileHandle hDevice,
IOCTL dwIoControlCode,
ref uint lpInBuffer,
int nInBufferSize,
ref uint lpOutBuffer,
int nOutBufferSize,
out uint lpBytesReturned,
IntPtr lpOverlapped);
[DllImport(DllName, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DeviceIoControl
(
SafeFileHandle hDevice,
IOCTL dwIoControlCode,
ref BATTERY_WAIT_STATUS lpInBuffer,
int nInBufferSize,
ref BATTERY_STATUS lpOutBuffer,
int nOutBufferSize,
out uint lpBytesReturned,
IntPtr lpOverlapped);
[DllImport(DllName, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DeviceIoControl
(
SafeFileHandle hDevice,
IOCTL dwIoControlCode,
ref BATTERY_QUERY_INFORMATION lpInBuffer,
int nInBufferSize,
IntPtr lpOutBuffer,
int nOutBufferSize,
out uint lpBytesReturned,
IntPtr lpOverlapped);
[DllImport(DllName, CallingConvention = CallingConvention.Winapi)]
internal static extern IntPtr LocalAlloc(uint uFlags, ulong uBytes);
[DllImport(DllName, CallingConvention = CallingConvention.Winapi)]
internal static extern IntPtr LocalFree(IntPtr hMem);
[DllImport(DllName, SetLastError = true)]
internal static extern void RtlZeroMemory(IntPtr Destination, int Length);
@@ -291,7 +352,80 @@ namespace LibreHardwareMonitor.Interop
IOCTL_SCSI_MINIPORT = 0x04d008,
IOCTL_SCSI_PASS_THROUGH_DIRECT = 0x04d014,
IOCTL_SCSI_GET_ADDRESS = 0x41018,
IOCTL_STORAGE_QUERY_PROPERTY = 0x2D1400
IOCTL_STORAGE_QUERY_PROPERTY = 0x2D1400,
IOCTL_BATTERY_QUERY_TAG = 0x294040,
IOCTL_BATTERY_QUERY_INFORMATION = 0x294044,
IOCTL_BATTERY_QUERY_STATUS = 0x29404C
}
[Flags]
internal enum BatteryCapabilities : uint
{
BATTERY_CAPACITY_RELATIVE = 0x40000000,
BATTERY_IS_SHORT_TERM = 0x20000000,
BATTERY_SET_CHARGE_SUPPORTED = 0x00000001,
BATTERY_SET_DISCHARGE_SUPPORTED = 0x00000002,
BATTERY_SYSTEM_BATTERY = 0x80000000
}
internal enum BATTERY_QUERY_INFORMATION_LEVEL
{
BatteryInformation,
BatteryGranularityInformation,
BatteryTemperature,
BatteryEstimatedTime,
BatteryDeviceName,
BatteryManufactureDate,
BatteryManufactureName,
BatteryUniqueID,
BatterySerialNumber
}
[StructLayout(LayoutKind.Sequential)]
internal struct BATTERY_QUERY_INFORMATION
{
public uint BatteryTag;
public BATTERY_QUERY_INFORMATION_LEVEL InformationLevel;
public uint AtRate;
}
[StructLayout(LayoutKind.Sequential)]
internal struct BATTERY_INFORMATION
{
public BatteryCapabilities Capabilities;
public byte Technology;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] Reserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public char[] Chemistry;
public uint DesignedCapacity;
public uint FullChargedCapacity;
public uint DefaultAlert1;
public uint DefaultAlert2;
public uint CriticalBias;
public uint CycleCount;
}
[StructLayout(LayoutKind.Sequential)]
internal struct BATTERY_WAIT_STATUS
{
public uint BatteryTag;
public uint Timeout;
public uint PowerState;
public uint LowCapacity;
public uint HighCapacity;
}
[StructLayout(LayoutKind.Sequential)]
internal struct BATTERY_STATUS
{
public uint PowerState;
public uint Capacity;
public uint Voltage;
public int Rate;
}
[Flags]
@@ -0,0 +1,50 @@
// 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.Runtime.InteropServices;
namespace LibreHardwareMonitor.Interop
{
internal class SetupApi
{
internal const int ERROR_INSUFFICIENT_BUFFER = 122;
internal const int ERROR_NO_MORE_ITEMS = 259;
internal const int DIGCF_PRESENT = 0x00000002;
internal const int DIGCF_DEVICEINTERFACE = 0x00000010;
internal static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
internal static Guid GUID_DEVICE_BATTERY = new Guid(0x72631e54, 0x78A4, 0x11d0, 0xbc, 0xf7, 0x00, 0xaa, 0x00, 0xb7, 0xb3, 0x2a);
private const string DllName = "SetupAPI.dll";
[StructLayout(LayoutKind.Sequential)]
internal struct SP_DEVICE_INTERFACE_DATA
{
public uint cbSize;
public Guid InterfaceClassGuid;
public uint Flags;
public IntPtr Reserved;
}
[DllImport(DllName, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
internal static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, IntPtr Enumerator, IntPtr hwndParent, uint Flags);
[DllImport(DllName, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet, IntPtr DeviceInfoData, ref Guid InterfaceClassGuid, uint MemberIndex, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData);
[DllImport(DllName, SetLastError = true, EntryPoint = "SetupDiGetDeviceInterfaceDetailW", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet,
in SP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
[Out, Optional] IntPtr DeviceInterfaceDetailData,
uint DeviceInterfaceDetailDataSize,
out uint RequiredSize,
IntPtr DeviceInfoData = default);
[DllImport(DllName, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);
}
}