未验证 提交 bf38a0d6 编写于 作者: P Patrick Grawehr 提交者: GitHub

New transport layer for HardwareAccess (#1755)

To use the new transport, use OpenHardwareMonitor with a version of 0.10.0 or above from https://github.com/hexagon-oss/openhardwaremonitor/releases
上级 5528eb89
......@@ -17,5 +17,6 @@
<MicrosoftExtensionsLoggingConsolePackageVersion>5.0.0</MicrosoftExtensionsLoggingConsolePackageVersion>
<MicrosoftExtensionsLoggingPackageVersion>5.0.0</MicrosoftExtensionsLoggingPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>5.0.0</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
<SystemTextJsonPackageVersion>6.0.1</SystemTextJsonPackageVersion>
</PropertyGroup>
</Project>
......@@ -32,6 +32,7 @@
<ProjectReference Include="$(MainLibraryPath)System.Device.Gpio.csproj" />
<PackageReference Include="System.Management" Version="$(SystemManagementPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
<PackageReference Include="System.Text.Json" Version="$(SystemTextJsonPackageVersion)" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="../devices/CharacterLcd/BigFontMap.txt" />
......
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>$(DefaultBindingTfms)</TargetFrameworks>
<TargetFrameworks>net6.0</TargetFrameworks>
<RootNamespace>Iot.Device.Arduino.Sample</RootNamespace>
<EnableDefaultItems>false</EnableDefaultItems>
<AssemblyName>Arduino.Monitor</AssemblyName>
......
......@@ -3,16 +3,24 @@
<PropertyGroup>
<TargetFrameworks>$(DefaultBindingTfms)</TargetFrameworks>
<EnableDefaultItems>false</EnableDefaultItems>
<DefaultNamespace>Iot.Device.HardwareMonitor</DefaultNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="OpenHardwareMonitor.cs" />
<Compile Include="SensorType.cs" />
<Compile Include="*.cs" />
<Compile Include="JsonSchema\ComputerJson.cs" />
<Compile Include="JsonSchema\HardwareJson.cs" />
<Compile Include="JsonSchema\SensorJson.cs" />
<None Include="README.md" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Management" Version="$(SystemManagementPackageVersion)" />
<PackageReference Include="System.Text.Json" Version="$(SystemTextJsonPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Common\CommonHelpers.csproj" />
</ItemGroup>
</Project>

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26124.0
# Visual Studio Version 17
VisualStudioVersion = 17.0.32002.185
MinimumVisualStudioVersion = 15.0.26124.0
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{45EC0518-E2FF-4278-ABDB-E0A2D79E71BE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenHardwareMonitor.Samples", "samples\OpenHardwareMonitor.Samples.csproj", "{356BCA51-E8A9-4190-A814-70A6F87E33E1}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenHardwareMonitor.Samples", "samples\OpenHardwareMonitor.Samples.csproj", "{356BCA51-E8A9-4190-A814-70A6F87E33E1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HardwareMonitor", "HardwareMonitor.csproj", "{B9CCBDB6-FAE3-49BF-B654-30698EF8131D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HardwareMonitor", "HardwareMonitor.csproj", "{B9CCBDB6-FAE3-49BF-B654-30698EF8131D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommonHelpers", "..\Common\CommonHelpers.csproj", "{FFBF1150-89AE-41EB-8160-C9DAAC92FA42}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
......@@ -18,9 +20,6 @@ Global
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{356BCA51-E8A9-4190-A814-70A6F87E33E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{356BCA51-E8A9-4190-A814-70A6F87E33E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
......@@ -46,8 +45,26 @@ Global
{B9CCBDB6-FAE3-49BF-B654-30698EF8131D}.Release|x64.Build.0 = Release|Any CPU
{B9CCBDB6-FAE3-49BF-B654-30698EF8131D}.Release|x86.ActiveCfg = Release|Any CPU
{B9CCBDB6-FAE3-49BF-B654-30698EF8131D}.Release|x86.Build.0 = Release|Any CPU
{FFBF1150-89AE-41EB-8160-C9DAAC92FA42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FFBF1150-89AE-41EB-8160-C9DAAC92FA42}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FFBF1150-89AE-41EB-8160-C9DAAC92FA42}.Debug|x64.ActiveCfg = Debug|Any CPU
{FFBF1150-89AE-41EB-8160-C9DAAC92FA42}.Debug|x64.Build.0 = Debug|Any CPU
{FFBF1150-89AE-41EB-8160-C9DAAC92FA42}.Debug|x86.ActiveCfg = Debug|Any CPU
{FFBF1150-89AE-41EB-8160-C9DAAC92FA42}.Debug|x86.Build.0 = Debug|Any CPU
{FFBF1150-89AE-41EB-8160-C9DAAC92FA42}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FFBF1150-89AE-41EB-8160-C9DAAC92FA42}.Release|Any CPU.Build.0 = Release|Any CPU
{FFBF1150-89AE-41EB-8160-C9DAAC92FA42}.Release|x64.ActiveCfg = Release|Any CPU
{FFBF1150-89AE-41EB-8160-C9DAAC92FA42}.Release|x64.Build.0 = Release|Any CPU
{FFBF1150-89AE-41EB-8160-C9DAAC92FA42}.Release|x86.ActiveCfg = Release|Any CPU
{FFBF1150-89AE-41EB-8160-C9DAAC92FA42}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{356BCA51-E8A9-4190-A814-70A6F87E33E1} = {45EC0518-E2FF-4278-ABDB-E0A2D79E71BE}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DDC5A930-246C-4854-8A4B-F8F983158925}
EndGlobalSection
EndGlobal
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Iot.Device.HardwareMonitor.JsonSchema;
namespace Iot.Device.HardwareMonitor
{
internal interface IOpenHardwareMonitorInternal : IDisposable
{
public SensorUpdateStrategy UpdateStrategy { get; set; }
public TimeSpan UpdateInterval { get; set; }
bool HasHardware();
IList<OpenHardwareMonitor.Sensor> GetSensorList();
IList<OpenHardwareMonitor.Hardware> GetHardwareComponents();
void UpdateSensors(bool refreshSensorList);
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
namespace Iot.Device.HardwareMonitor.JsonSchema
{
internal class ComputerJson
{
public ComputerJson()
{
ComputerName = string.Empty;
Hardware = Array.Empty<HardwareJson>();
}
public string ComputerName
{
get;
set;
}
public int LogicalProcessorCount
{
get;
set;
}
public HardwareJson[] Hardware
{
get;
set;
}
public override string ToString()
{
return ComputerName;
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
namespace Iot.Device.HardwareMonitor.JsonSchema
{
internal class HardwareJson
{
public HardwareJson()
{
NodeId = String.Empty;
Name = String.Empty;
Sensors = Array.Empty<SensorJson>();
Parent = string.Empty;
HardwareType = string.Empty;
}
public string NodeId
{
get;
set;
}
public string Name
{
get;
set;
}
public string Parent
{
get;
set;
}
public SensorJson[] Sensors
{
get;
set;
}
public string HardwareType
{
get;
set;
}
public override string ToString()
{
return Name;
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Iot.Device.HardwareMonitor.JsonSchema
{
internal class SensorJson
{
public SensorJson()
{
NodeId = string.Empty;
Name = string.Empty;
Type = string.Empty;
Unit = string.Empty;
Parent = string.Empty;
}
public string NodeId
{
get;
set;
}
public string Name
{
get;
set;
}
public string Type
{
get;
set;
}
public string Unit
{
get;
set;
}
public double Value
{
get;
set;
}
public string Parent
{
get; set;
}
public override string ToString()
{
return Name;
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Iot.Device.Common;
using Iot.Device.HardwareMonitor;
using Iot.Device.HardwareMonitor.JsonSchema;
using Microsoft.Extensions.Logging;
namespace Iot.Device.HardwareMonitor
{
internal sealed class OpenHardwareMonitorHttp : IOpenHardwareMonitorInternal
{
private readonly string _host;
private readonly int _port;
private readonly Uri _uri;
private readonly ILogger _logger;
private readonly object _lock = new object();
private HttpClient _httpClient;
private string? _version;
private List<OpenHardwareMonitor.Hardware> _hardware;
private List<SensorHttp> _sensors;
private DateTime _lastUpdateTime;
public OpenHardwareMonitorHttp(string host, int port)
{
_host = host;
_port = port;
_lastUpdateTime = DateTime.MinValue;
_hardware = new List<OpenHardwareMonitor.Hardware>();
_sensors = new List<SensorHttp>();
_uri = new Uri("http://" + host + ":" + port + "/");
_httpClient = new HttpClient();
_logger = this.GetCurrentClassLogger();
TryConnectToApi();
}
public SensorUpdateStrategy UpdateStrategy
{
get;
set;
}
public TimeSpan UpdateInterval
{
get;
set;
}
public void UpdateSensors(bool refreshSensorList)
{
GetFullDataAndDecode(refreshSensorList);
}
public bool TryConnectToApi()
{
if (!string.IsNullOrEmpty(_version))
{
return true;
}
_version = string.Empty;
try
{
_version = _httpClient.GetStringAsync(_uri + "api/version").Result;
}
catch (AggregateException ex)
{
_logger.LogWarning(ex, $"Unable to read version from OpenHardwareMonitor REST api: {ex.Message}");
_version = String.Empty;
}
return !string.IsNullOrEmpty(_version);
}
public bool HasHardware()
{
return !string.IsNullOrEmpty(_version);
}
private void GetFullDataAndDecode(bool refreshSensorList)
{
if (!TryConnectToApi())
{
return;
}
try
{
string fullJson = _httpClient.GetStringAsync(_uri + "api/rootnode").Result;
lock (_lock)
{
ComputerJson? rootNode = JsonSerializer.Deserialize<ComputerJson>(fullJson);
if (rootNode == null)
{
return;
}
if (_hardware.Count == 0 || _sensors.Count == 0 || refreshSensorList)
{
var (newHardware, newSensors) = CreateSensorTree(rootNode);
_hardware = newHardware;
_sensors = newSensors;
}
else
{
// Instead of updating the sensor list, update only the values for each sensor
List<SensorJson> sensorsFromJson = new();
foreach (var hw in rootNode.Hardware)
{
sensorsFromJson.AddRange(hw.Sensors);
}
foreach (var sensor in sensorsFromJson)
{
var sensorToUpdate = _sensors.FirstOrDefault(x => x.Identifier == sensor.NodeId);
if (sensorToUpdate != null)
{
sensorToUpdate.Value = sensor.Value;
}
}
}
_lastUpdateTime = DateTime.UtcNow;
}
}
catch (AggregateException ex)
{
_logger.LogWarning(ex, $"Unable to read /api/rootnode: {ex.Message}");
// If we failed to retrieve updates for some time, reset the fields.
if ((DateTime.UtcNow - _lastUpdateTime).Duration().TotalSeconds > UpdateInterval.TotalSeconds * 10) // (netstandard doesn't support operator* on TimeSpan)
{
_hardware = new List<OpenHardwareMonitor.Hardware>();
_sensors = new List<SensorHttp>();
}
}
}
private (List<OpenHardwareMonitor.Hardware> Hardware, List<SensorHttp> Sensors) CreateSensorTree(ComputerJson rootNode)
{
var newHardware = new List<OpenHardwareMonitor.Hardware>();
var newSensors = new List<SensorHttp>();
foreach (var hardware in rootNode.Hardware)
{
var hw = new OpenHardwareMonitor.Hardware(hardware.Name, hardware.NodeId, hardware.Parent, hardware.HardwareType);
newHardware.Add(hw);
foreach (var sensor in hardware.Sensors)
{
SensorType sensorType;
if (Enum.TryParse<SensorType>(sensor.Type, true, out sensorType))
{
var s = new SensorHttp(this, sensor.Name, sensor.NodeId, sensor.Parent, sensorType, sensor.Value);
newSensors.Add(s);
}
}
}
return (newHardware, newSensors);
}
public IList<OpenHardwareMonitor.Sensor> GetSensorList()
{
if (!_sensors.Any())
{
GetFullDataAndDecode(false);
}
return _sensors.Cast<OpenHardwareMonitor.Sensor>().ToList();
}
public IList<OpenHardwareMonitor.Hardware> GetHardwareComponents()
{
if (!_hardware.Any())
{
GetFullDataAndDecode(false);
}
return _hardware;
}
public void Dispose()
{
_version = string.Empty;
_httpClient.Dispose();
}
private Uri Combine(Uri root, params String[] moreParts)
{
if (!moreParts.Any())
{
return root;
}
Uri combined = root;
foreach (var part in moreParts)
{
Uri? temp;
// The second argument to TryCreate must not be rooted, or the newly added part overwrites any existing parts.
var trimmed = part.TrimStart('/');
if (!Uri.TryCreate(combined, trimmed, out temp))
{
throw new InvalidOperationException($"{part} is not a valid URL component");
}
combined = temp;
}
return combined;
}
private bool TryUpdateSensor(SensorHttp sensor, out double value)
{
value = 0;
if (UpdateStrategy == SensorUpdateStrategy.PerSensor || UpdateStrategy == SensorUpdateStrategy.Unspecified)
{
try
{
var apiUrl = Combine(_uri, "/api/nodes/", sensor.Identifier);
string fullJson = _httpClient.GetStringAsync(apiUrl).Result;
lock (_lock)
{
SensorJson? rootNode = JsonSerializer.Deserialize<SensorJson>(fullJson);
if (rootNode == null)
{
_logger.LogWarning($"Unable to parse json: {fullJson}");
return false;
}
value = rootNode.Value;
return true;
}
}
catch (AggregateException ex)
{
_logger.LogWarning(ex, $"Unable to read update value for node {sensor.Identifier}: {ex.Message}");
return false;
}
}
else if (UpdateStrategy == SensorUpdateStrategy.SynchronousAfterTimeout && (DateTime.UtcNow - _lastUpdateTime).Duration() > UpdateInterval)
{
UpdateSensors(false);
}
value = sensor.Value;
return true;
}
private sealed class SensorHttp : OpenHardwareMonitor.Sensor
{
private readonly OpenHardwareMonitorHttp _monitor;
public SensorHttp(OpenHardwareMonitorHttp monitor, string name, string identifier, string? parent, SensorType typeEnum, double initialValue)
: base(name, identifier, parent, typeEnum)
{
_monitor = monitor;
Value = initialValue;
}
protected override bool UpdateValue(out double value)
{
return _monitor.TryUpdateSensor(this, out value);
}
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Iot.Device.HardwareMonitor
{
/// <summary>
/// Select the preferred transport protocol for the Hardware monitor
/// </summary>
public enum OpenHardwareMonitorTransport
{
/// <summary>
/// Automatically choose a transport
/// </summary>
Auto = 0,
/// <summary>
/// Use the legacy WMI transport
/// </summary>
Wmi = 1,
/// <summary>
/// Use HTTP transport
/// </summary>
Http = 2,
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Management;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using UnitsNet;
using UnitsNet.Units;
// We have a check on all constructors to ensure that we throw when not on Windows, so disabling warning on calling windows-only apis.
#pragma warning disable CA1416 // Validate platform compatibility
namespace Iot.Device.HardwareMonitor
{
/// <summary>
/// This class connects to a running instance of OpenHardwareMonitor and reads out all available values using WMI.
/// This works only if OpenHardwareMonitor (https://openhardwaremonitor.org/) is currently running.
/// While the tool needs to be run with elevated permissions, the application using this binding does not.
/// The connection using WMI has proven to be unreliable (the WMI classes are sometimes just not visible), therefore
/// OHM versions greater > 0.10.0 use a rest api instead.
/// </summary>
internal sealed class OpenHardwareMonitorWmi : IDisposable, IOpenHardwareMonitorInternal
{
private OpenHardwareMonitor.Hardware? _cpu;
/// <summary>
/// Constructs a new instance of this class.
/// The class can be constructed even if no sensors are available or OpenHardwareMonitor is not running (yet).
/// </summary>
/// <exception cref="PlatformNotSupportedException">The operating system is not Windows.</exception>
public OpenHardwareMonitorWmi()
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
throw new PlatformNotSupportedException("This class is only supported on Windows operating systems");
}
InitHardwareMonitor();
}
public SensorUpdateStrategy UpdateStrategy { get; set; }
public TimeSpan UpdateInterval { get; set; }
public void UpdateSensors(bool refreshSensorList)
{
if (refreshSensorList)
{
_cpu = null;
InitHardwareMonitor();
}
}
public bool HasHardware()
{
return _cpu != null;
}
private void InitHardwareMonitor()
{
try
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher(@"root\OpenHardwareMonitor", "SELECT * FROM Sensor");
foreach (var hardware in GetHardwareComponents())
{
if (hardware.Type != null && hardware.Type.Equals("CPU", StringComparison.OrdinalIgnoreCase))
{
_cpu = hardware;
}
}
searcher.Dispose();
}
catch (Exception x) when (x is IOException || x is UnauthorizedAccessException || x is ManagementException)
{
// Nothing to do - WMI not available for this element or missing permissions.
// WMI enumeration may require elevated rights.
}
}
/// <summary>
/// Query the list of all available sensors.
/// </summary>
/// <returns>A list of <see cref="OpenHardwareMonitor.Sensor"/> instances. May be empty.</returns>
/// <exception cref="ManagementException">The WMI objects required are not available. Is OpenHardwareMonitor running?</exception>
public IList<OpenHardwareMonitor.Sensor> GetSensorList()
{
List<OpenHardwareMonitor.Sensor> ret = new List<OpenHardwareMonitor.Sensor>();
ManagementObjectSearcher searcher = new ManagementObjectSearcher(@"root\OpenHardwareMonitor", "SELECT * FROM Sensor");
foreach (ManagementObject sensor in searcher.Get())
{
string? name = Convert.ToString(sensor["Name"]);
string? identifier = Convert.ToString(sensor["Identifier"]);
// This is not expected to really happen
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(identifier))
{
continue;
}
string? parent = Convert.ToString(sensor["Parent"]);
string? type = Convert.ToString(sensor["SensorType"]);
SensorType typeEnum;
if (!Enum.TryParse(type, true, out typeEnum))
{
typeEnum = SensorType.Unknown;
}
ret.Add(new SensorWmi(sensor, name, identifier, parent, typeEnum));
}
return ret;
}
/// <summary>
/// Returns a list of hardware components, such as "CPU", "GPU" or "Mainboard"
/// </summary>
public IList<OpenHardwareMonitor.Hardware> GetHardwareComponents()
{
IList<OpenHardwareMonitor.Hardware> ret = new List<OpenHardwareMonitor.Hardware>();
ManagementObjectSearcher searcher = new ManagementObjectSearcher(@"root\OpenHardwareMonitor", "SELECT * FROM Hardware");
if (searcher.Get().Count > 0)
{
foreach (ManagementObject sensor in searcher.Get())
{
string? name = Convert.ToString(sensor["Name"]);
string? identifier = Convert.ToString(sensor["Identifier"]);
if (name == null || identifier == null)
{
continue;
}
string? parent = Convert.ToString(sensor["Parent"]);
string? type = Convert.ToString(sensor["HardwareType"]);
ret.Add(new OpenHardwareMonitor.Hardware(name, identifier, parent, type));
}
}
return ret;
}
/// <summary>
/// Get the list of sensors for a specific piece of hardware
/// </summary>
/// <param name="forHardware">The module that should be queried</param>
/// <returns>A list of sensors</returns>
public IEnumerable<OpenHardwareMonitor.Sensor> GetSensorList(OpenHardwareMonitor.Hardware? forHardware)
{
if (forHardware == null)
{
throw new ArgumentNullException(nameof(forHardware));
}
return GetSensorList().Where(x => x.Identifier != null && x.Identifier.StartsWith(forHardware.Identifier ?? string.Empty)).OrderBy(y => y.Identifier);
}
/// <inheritdoc/>
public void Dispose()
{
_cpu = null;
}
/// <summary>
/// Represents a single Wmi sensor
/// </summary>
public class SensorWmi : OpenHardwareMonitor.Sensor, IDisposable
{
private readonly ManagementObject _instance;
private bool _valueRead;
/// <summary>
/// Creates a sensor instance
/// </summary>
public SensorWmi(ManagementObject instance, string name, string identifier, string? parent, SensorType typeEnum)
: base(name, identifier, parent, typeEnum)
{
_instance = instance;
InstanceId = 0;
_valueRead = false;
if (!string.IsNullOrWhiteSpace(instance.Path.RelativePath))
{
int instanceBegin = instance.Path.RelativePath.IndexOf("InstanceId=\"", StringComparison.OrdinalIgnoreCase) + 12;
int instanceEnd = instance.Path.RelativePath.IndexOf('\"', instanceBegin);
if (Int32.TryParse(instance.Path.RelativePath.Substring(instanceBegin, instanceEnd - instanceBegin), out int id))
{
InstanceId = id;
}
}
}
public int InstanceId { get; private set; }
private ManagementObjectSearcher? ActiveCollection
{
get;
set;
}
protected override bool UpdateValue(out double value)
{
if (_valueRead && InstanceId != 0)
{
// Cache the searcher. We have to re-query the instances each time, or the value stays the same.
ActiveCollection = new ManagementObjectSearcher(@"root\OpenHardwareMonitor", $"SELECT Value FROM Sensor WHERE InstanceId='{InstanceId}'");
}
if (_valueRead && InstanceId != 0 && ActiveCollection != null)
{
value = 0;
// We expect exactly one instance, but unfortunately, the returned ManagementObjectCollection doesn't implement IEnumerable or IList
foreach (var inst in ActiveCollection.Get())
{
value = Convert.ToSingle(inst.GetPropertyValue("Value"));
}
}
else
{
// The artificial instances auto-update. And if the object is new, we also don't need a refresh
value = Convert.ToSingle(_instance.GetPropertyValue("Value"));
_valueRead = true;
}
return true;
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
_instance.Dispose();
if (ActiveCollection != null)
{
ActiveCollection.Dispose();
ActiveCollection = null;
}
}
}
}
}
#pragma warning restore CA1416 // Validate platform compatibility
......@@ -4,7 +4,7 @@ Client binding for OpenHardwareMonitor. Returns a set of sensor measurements for
## Documentation
This binding works on Windows only. It requires that OpenHardwareMonitor (<https://openhardwaremonitor.org/>) is running in the background. While that tool requires elevated permissions to work, the binding (and the application using it) does not. Check out <https://github.com/hexagon-oss/openhardwaremonitor> for an improved fork with some additional features.
This binding works on Windows only. It requires that OpenHardwareMonitor (<https://openhardwaremonitor.org/>) is running in the background. While that tool requires elevated permissions to work, the binding (and the application using it) does not. Check out <https://github.com/hexagon-oss/openhardwaremonitor> for an improved fork with some additional features. The latest version contains a new transport protocol between the tool and the binding (HTTP instead of WMI), which improves the reliability of the data and additionally allows reading the performance data from a remote computer.
The binding supports some additional, "virtual" sensor measuments that are derived from other values. The following extra values are provided:
......@@ -24,6 +24,10 @@ using UnitsNet;
Console.WriteLine("Press any key to quit");
OpenHardwareMonitor hw = new OpenHardwareMonitor();
// Explicit update - exactly one data set is read for each iteration
hw.UpdateStrategy = SensorUpdateStrategy.SynchronousExplicit;
if (hw.GetSensorList().Count == 0)
{
Console.WriteLine("OpenHardwareMonitor is not running");
......
namespace Iot.Device.HardwareMonitor
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Iot.Device.HardwareMonitor
{
/// <summary>
/// Designates a sensor type
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Iot.Device.HardwareMonitor;
namespace Iot.Device.HardwareMonitor
{
/// <summary>
/// Selects when the sensors get updated values
/// </summary>
public enum SensorUpdateStrategy
{
/// <summary>
/// The setting has not been set
/// </summary>
Unspecified,
/// <summary>
/// Each time a sensor's TryGetValue is called, a new value is selected.
/// This is the default (and only supported option) for WMI
/// </summary>
PerSensor,
/// <summary>
/// All sensors are updated synchronously when the value is older than the <see cref="OpenHardwareMonitor.MonitoringInterval"/>.
/// </summary>
SynchronousAfterTimeout,
/// <summary>
/// All sensors are updated only when <see cref="OpenHardwareMonitor.UpdateSensors"/> is explicitly called
/// </summary>
SynchronousExplicit,
}
}
......@@ -3,6 +3,7 @@
using System;
using System.Globalization;
using System.Linq;
using System.Threading;
using Iot.Device.HardwareMonitor;
using UnitsNet;
......@@ -10,11 +11,9 @@ using UnitsNet;
Console.WriteLine("Press any key to quit");
OpenHardwareMonitor hw = new OpenHardwareMonitor();
if (hw.GetSensorList().Count == 0)
{
Console.WriteLine("OpenHardwareMonitor is not running");
return;
}
// Explicit update - exactly one data set is read for each iteration
hw.UpdateStrategy = SensorUpdateStrategy.SynchronousExplicit;
hw.EnableDerivedSensors();
......@@ -22,7 +21,13 @@ while (!Console.KeyAvailable)
{
Console.Clear();
Console.WriteLine("Showing all available sensors (press any key to quit)");
hw.UpdateSensors(false);
var components = hw.GetHardwareComponents();
if (!components.Any())
{
Console.WriteLine("Waiting for connection to OpenHardwareMonitor. Is it running?");
}
foreach (var component in components)
{
Console.WriteLine("--------------------------------------------------------------------");
......@@ -42,11 +47,15 @@ while (!Console.KeyAvailable)
}
}
// Set update strategy to immediately, to test that receiving individual sensors works as well
hw.UpdateStrategy = SensorUpdateStrategy.PerSensor;
if (hw.TryGetAverageGpuTemperature(out Temperature gpuTemp) &&
hw.TryGetAverageCpuTemperature(out Temperature cpuTemp))
{
Console.WriteLine($"Averages: CPU temp {cpuTemp:s2}, GPU temp {gpuTemp:s2}, CPU Load {hw.GetCpuLoad()}");
}
Thread.Sleep(1000);
hw.UpdateStrategy = SensorUpdateStrategy.SynchronousExplicit;
Thread.Sleep(2000);
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册